Merge branch 'master' into CompatibiltyReporting

This commit is contained in:
BreadFish64 2018-01-18 10:36:32 -06:00 committed by GitHub
commit c3afd73592
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 1527 additions and 745 deletions

View file

@ -23,7 +23,7 @@ add_library(audio_core STATIC
time_stretch.cpp
time_stretch.h
$<$<BOOL:SDL2_FOUND>:sdl2_sink.cpp sdl2_sink.h>
$<$<BOOL:${SDL2_FOUND}>:sdl2_sink.cpp sdl2_sink.h>
)
create_target_directory_groups(audio_core)

View file

@ -11,10 +11,8 @@
// This needs to be included before getopt.h because the latter #defines symbols used by it
#include "common/microprofile.h"
#ifdef _MSC_VER
#include <getopt.h>
#else
#include <getopt.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
@ -157,13 +155,14 @@ int main(int argc, char** argv) {
errno = EINVAL;
if (errno != 0)
exit(1);
break;
}
case 'm': {
use_multiplayer = true;
std::string str_arg(optarg);
const std::string str_arg(optarg);
// regex to check if the format is nickname:password@ip:port
// with optional :password
std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
if (!std::regex_match(str_arg, re)) {
std::cout << "Wrong format for option --multiplayer\n";
PrintHelp(argv[0]);
@ -188,10 +187,6 @@ int main(int argc, char** argv) {
std::cout << "Address to room must not be empty.\n";
return 0;
}
if (port > 65535) {
std::cout << "Port must be between 0 and 65535.\n";
return 0;
}
break;
}
case 'h':

View file

@ -76,8 +76,9 @@ void Config::ReadValues() {
Settings::values.analogs[i] = default_param;
}
Settings::values.motion_device = sdl2_config->Get(
"Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
Settings::values.motion_device =
sdl2_config->Get("Controls", "motion_device",
"engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0");
Settings::values.touch_device =
sdl2_config->Get("Controls", "touch_device", "engine:emu_window");

View file

@ -89,19 +89,19 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
if (render_window == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting...");
LOG_CRITICAL(Frontend, "Failed to create SDL2 window: %s", SDL_GetError());
exit(1);
}
gl_context = SDL_GL_CreateContext(render_window);
if (gl_context == nullptr) {
LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting...");
LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: %s", SDL_GetError());
exit(1);
}
if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting...");
LOG_CRITICAL(Frontend, "Failed to initialize GL functions: %s", SDL_GetError());
exit(1);
}

View file

@ -90,12 +90,46 @@ file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
qt5_wrap_ui(UI_HDRS ${UIS})
if (ENABLE_QT_TRANSLATION)
set(CITRA_QT_LANGUAGES "${CMAKE_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
# Update source TS file if enabled
if (GENERATE_QT_TRANSLATION)
get_target_property(SRCS citra-qt SOURCES)
qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${CITRA_QT_LANGUAGES}/en.ts)
add_custom_target(translation ALL DEPENDS ${CITRA_QT_LANGUAGES}/en.ts)
endif()
# Find all TS files except en.ts
file(GLOB_RECURSE LANGUAGES_TS ${CITRA_QT_LANGUAGES}/*.ts)
list(REMOVE_ITEM LANGUAGES_TS ${CITRA_QT_LANGUAGES}/en.ts)
# Compile TS files to QM files
qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
# Build a QRC file from the QM file list
set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n")
foreach (QM ${LANGUAGES_QM})
get_filename_component(QM_FILE ${QM} NAME)
file(APPEND ${LANGUAGES_QRC} "<file>${QM_FILE}</file>\n")
endforeach (QM)
file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")
# Add the QRC file to package in all QM files
qt5_add_resources(LANGUAGES ${LANGUAGES_QRC})
else()
set(LANGUAGES)
endif()
target_sources(citra-qt
PRIVATE
${ICONS}
${THEMES}
${UI_HDRS}
${UIS}
${LANGUAGES}
)
if (APPLE)

View file

@ -36,6 +36,7 @@ void EmuThread::run() {
Core::System::ResultStatus result = Core::System::GetInstance().RunLoop();
if (result != Core::System::ResultStatus::Success) {
this->SetRunning(false);
emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails());
}

View file

@ -58,7 +58,9 @@ void Config::ReadValues() {
}
Settings::values.motion_device =
qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
qt_config
->value("motion_device",
"engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0")
.toString()
.toStdString();
Settings::values.touch_device =
@ -182,6 +184,7 @@ void Config::ReadValues() {
UISettings::values.gamedir = qt_config->value("gameListRootDir", ".").toString();
UISettings::values.gamedir_deepscan = qt_config->value("gameListDeepScan", false).toBool();
UISettings::values.recent_files = qt_config->value("recentFiles").toStringList();
UISettings::values.language = qt_config->value("language", "").toString();
qt_config->endGroup();
qt_config->beginGroup("Shortcuts");
@ -333,6 +336,7 @@ void Config::SaveValues() {
qt_config->setValue("gameListRootDir", UISettings::values.gamedir);
qt_config->setValue("gameListDeepScan", UISettings::values.gamedir_deepscan);
qt_config->setValue("recentFiles", UISettings::values.recent_files);
qt_config->setValue("language", UISettings::values.language);
qt_config->endGroup();
qt_config->beginGroup("Shortcuts");

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>740</width>
<width>461</width>
<height>500</height>
</rect>
</property>
@ -34,15 +34,15 @@
<string>Input</string>
</attribute>
</widget>
<widget class="ConfigureGraphics" name="graphicsTab">
<attribute name="title">
<string>Graphics</string>
</attribute>
</widget>
<widget class="ConfigureGraphics" name="graphicsTab">
<attribute name="title">
<string>Graphics</string>
</attribute>
</widget>
<widget class="ConfigureAudio" name="audioTab">
<attribute name="title">
<string>Audio</string>
</attribute>
<attribute name="title">
<string>Audio</string>
</attribute>
</widget>
<widget class="ConfigureDebug" name="debugTab">
<attribute name="title">

View file

@ -76,3 +76,7 @@ void ConfigureAudio::updateAudioDevices(int sink_index) {
ui->audio_device_combo_box->addItem(device.c_str());
}
}
void ConfigureAudio::retranslateUi() {
ui->retranslateUi(this);
}

View file

@ -19,6 +19,7 @@ public:
~ConfigureAudio();
void applyConfiguration();
void retranslateUi();
public slots:
void updateAudioDevices(int sink_index);

View file

@ -24,3 +24,7 @@ void ConfigureDebug::applyConfiguration() {
Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
Settings::Apply();
}
void ConfigureDebug::retranslateUi() {
ui->retranslateUi(this);
}

View file

@ -19,6 +19,7 @@ public:
~ConfigureDebug();
void applyConfiguration();
void retranslateUi();
private:
void setConfiguration();

View file

@ -10,6 +10,8 @@
ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) {
ui->setupUi(this);
this->setConfiguration();
connect(ui->generalTab, &ConfigureGeneral::languageChanged, this,
&ConfigureDialog::onLanguageChanged);
}
ConfigureDialog::~ConfigureDialog() {}
@ -26,3 +28,15 @@ void ConfigureDialog::applyConfiguration() {
ui->webTab->applyConfiguration();
Settings::Apply();
}
void ConfigureDialog::onLanguageChanged(const QString& locale) {
emit languageChanged(locale);
ui->retranslateUi(this);
ui->generalTab->retranslateUi();
ui->systemTab->retranslateUi();
ui->inputTab->retranslateUi();
ui->graphicsTab->retranslateUi();
ui->audioTab->retranslateUi();
ui->debugTab->retranslateUi();
ui->webTab->retranslateUi();
}

View file

@ -20,6 +20,12 @@ public:
void applyConfiguration();
private slots:
void onLanguageChanged(const QString& locale);
signals:
void languageChanged(const QString& locale);
private:
void setConfiguration();

View file

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QDirIterator>
#include "citra_qt/configuration/configure_general.h"
#include "citra_qt/ui_settings.h"
#include "core/core.h"
@ -12,6 +13,23 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
: QWidget(parent), ui(new Ui::ConfigureGeneral) {
ui->setupUi(this);
ui->language_combobox->addItem(tr("<System>"), QString(""));
ui->language_combobox->addItem(tr("English"), QString("en"));
QDirIterator it(":/languages", QDirIterator::NoIteratorFlags);
while (it.hasNext()) {
QString locale = it.next();
locale.truncate(locale.lastIndexOf('.'));
locale.remove(0, locale.lastIndexOf('/') + 1);
QString lang = QLocale::languageToString(QLocale(locale).language());
ui->language_combobox->addItem(lang, locale);
}
// Unlike other configuration changes, interface language changes need to be reflected on the
// interface immediately. This is done by passing a signal to the main window, and then
// retranslating when passing back.
connect(ui->language_combobox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureGeneral::onLanguageChanged);
for (auto theme : UISettings::themes) {
ui->theme_combobox->addItem(theme.first, theme.second);
@ -37,6 +55,8 @@ void ConfigureGeneral::setConfiguration() {
ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1);
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
ui->language_combobox->setCurrentIndex(
ui->language_combobox->findData(UISettings::values.language));
}
void ConfigureGeneral::applyConfiguration() {
@ -52,3 +72,14 @@ void ConfigureGeneral::applyConfiguration() {
Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
Settings::Apply();
}
void ConfigureGeneral::onLanguageChanged(int index) {
if (index == -1)
return;
emit languageChanged(ui->language_combobox->itemData(index).toString());
}
void ConfigureGeneral::retranslateUi() {
ui->retranslateUi(this);
}

View file

@ -19,6 +19,13 @@ public:
~ConfigureGeneral();
void applyConfiguration();
void retranslateUi();
private slots:
void onLanguageChanged(int index);
signals:
void languageChanged(const QString& locale);
private:
void setConfiguration();

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>377</height>
<width>345</width>
<height>493</height>
</rect>
</property>
<property name="windowTitle">
@ -38,6 +38,20 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="language_label">
<property name="text">
<string>Interface language</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="language_combobox"/>
</item>
</layout>
</item>
</layout>
</item>
</layout>

View file

@ -113,3 +113,7 @@ void ConfigureGraphics::applyConfiguration() {
Settings::values.swap_screen = ui->swap_screen->isChecked();
Settings::Apply();
}
void ConfigureGraphics::retranslateUi() {
ui->retranslateUi(this);
}

View file

@ -19,6 +19,7 @@ public:
~ConfigureGraphics();
void applyConfiguration();
void retranslateUi();
private:
void setConfiguration();

View file

@ -277,3 +277,7 @@ void ConfigureInput::keyPressEvent(QKeyEvent* event) {
}
setPollingResult({}, true);
}
void ConfigureInput::retranslateUi() {
ui->retranslateUi(this);
}

View file

@ -33,6 +33,7 @@ public:
/// Save all button configurations to settings file
void applyConfiguration();
void retranslateUi();
private:
std::unique_ptr<Ui::ConfigureInput> ui;

View file

@ -167,3 +167,7 @@ void ConfigureSystem::refreshConsoleID() {
Service::CFG::UpdateConfigNANDSavegame();
ui->label_console_id->setText("Console ID: 0x" + QString::number(console_id, 16).toUpper());
}
void ConfigureSystem::retranslateUi() {
ui->retranslateUi(this);
}

View file

@ -20,6 +20,7 @@ public:
void applyConfiguration();
void setConfiguration();
void retranslateUi();
public slots:
void updateBirthdayComboBox(int birthmonth_index);

View file

@ -100,3 +100,7 @@ void ConfigureWeb::OnLoginVerified() {
"correctly, and that your internet connection is working."));
}
}
void ConfigureWeb::retranslateUi() {
ui->retranslateUi(this);
}

View file

@ -20,6 +20,7 @@ public:
~ConfigureWeb();
void applyConfiguration();
void retranslateUi();
public slots:
void RefreshTelemetryID();

View file

@ -98,6 +98,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
// register size_t to use in slots and signals
qRegisterMetaType<size_t>("size_t");
LoadTranslation();
Pica::g_debug_context = Pica::DebugContext::Construct();
setAcceptDrops(true);
ui.setupUi(this);
@ -115,8 +117,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
ConnectMenuEvents();
ConnectWidgetEvents();
setWindowTitle(QString("Citra %1| %2-%3")
.arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
SetupUIStrings();
show();
game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
@ -892,6 +894,8 @@ void GMainWindow::ToggleWindowMode() {
void GMainWindow::OnConfigure() {
ConfigureDialog configureDialog(this);
connect(&configureDialog, &ConfigureDialog::languageChanged, this,
&GMainWindow::OnLanguageChanged);
auto result = configureDialog.exec();
if (result == QDialog::Accepted) {
configureDialog.applyConfiguration();
@ -995,6 +999,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
} else {
// Only show the message if the game is still running.
if (emu_thread) {
emu_thread->SetRunning(true);
message_label->setText(status_message);
message_label->setVisible(true);
}
@ -1105,6 +1110,45 @@ void GMainWindow::UpdateUITheme() {
}
}
void GMainWindow::LoadTranslation() {
// If the selected language is English, no need to install any translation
if (UISettings::values.language == "en") {
return;
}
bool loaded;
if (UISettings::values.language.isEmpty()) {
// If the selected language is empty, use system locale
loaded = translator.load(QLocale(), "", "", ":/languages/");
} else {
// Otherwise load from the specified file
loaded = translator.load(UISettings::values.language, ":/languages/");
}
if (loaded) {
qApp->installTranslator(&translator);
} else {
UISettings::values.language = "en";
}
}
void GMainWindow::OnLanguageChanged(const QString& locale) {
if (UISettings::values.language != "en") {
qApp->removeTranslator(&translator);
}
UISettings::values.language = locale;
LoadTranslation();
ui.retranslateUi(this);
SetupUIStrings();
}
void GMainWindow::SetupUIStrings() {
setWindowTitle(
tr("Citra %1| %2-%3").arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc));
}
#ifdef main
#undef main
#endif

View file

@ -7,6 +7,7 @@
#include <memory>
#include <QMainWindow>
#include <QTimer>
#include <QTranslator>
#include "core/core.h"
#include "core/hle/service/am/am.h"
#include "ui_main.h"
@ -152,9 +153,12 @@ private slots:
void OnUpdateFound(bool found, bool error);
void OnCheckForUpdates();
void OnOpenUpdater();
void OnLanguageChanged(const QString& locale);
private:
void UpdateStatusBar();
void LoadTranslation();
void SetupUIStrings();
Ui::MainWindow ui;
@ -193,6 +197,8 @@ private:
QAction* actions_recent_files[max_recent_files_item];
QTranslator translator;
protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;

View file

@ -48,6 +48,7 @@ struct Values {
QString gamedir;
bool gamedir_deepscan;
QStringList recent_files;
QString language;
QString theme;

View file

@ -32,6 +32,8 @@ add_library(common STATIC
break_points.cpp
break_points.h
chunk_file.h
cityhash.cpp
cityhash.h
code_block.h
color.h
common_funcs.h
@ -39,7 +41,6 @@ add_library(common STATIC
common_types.h
file_util.cpp
file_util.h
hash.cpp
hash.h
linear_disk_cache.h
logging/backend.cpp

340
src/common/cityhash.cpp Normal file
View file

@ -0,0 +1,340 @@
// Copyright (c) 2011 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// CityHash, by Geoff Pike and Jyrki Alakuijala
//
// This file provides CityHash64() and related functions.
//
// It's probably possible to create even faster hash functions by
// writing a program that systematically explores some of the space of
// possible hash functions, by using SIMD instructions, or by
// compromising on hash quality.
#include <algorithm>
#include <string.h> // for memcpy and memset
#include "cityhash.h"
#include "common/swap.h"
// #include "config.h"
#ifdef __GNUC__
#define HAVE_BUILTIN_EXPECT 1
#endif
#ifdef COMMON_BIG_ENDIAN
#define WORDS_BIGENDIAN 1
#endif
using namespace std;
typedef uint8_t uint8;
typedef uint32_t uint32;
typedef uint64_t uint64;
namespace Common {
static uint64 UNALIGNED_LOAD64(const char* p) {
uint64 result;
memcpy(&result, p, sizeof(result));
return result;
}
static uint32 UNALIGNED_LOAD32(const char* p) {
uint32 result;
memcpy(&result, p, sizeof(result));
return result;
}
#ifdef WORDS_BIGENDIAN
#define uint32_in_expected_order(x) (swap32(x))
#define uint64_in_expected_order(x) (swap64(x))
#else
#define uint32_in_expected_order(x) (x)
#define uint64_in_expected_order(x) (x)
#endif
#if !defined(LIKELY)
#if HAVE_BUILTIN_EXPECT
#define LIKELY(x) (__builtin_expect(!!(x), 1))
#else
#define LIKELY(x) (x)
#endif
#endif
static uint64 Fetch64(const char* p) {
return uint64_in_expected_order(UNALIGNED_LOAD64(p));
}
static uint32 Fetch32(const char* p) {
return uint32_in_expected_order(UNALIGNED_LOAD32(p));
}
// Some primes between 2^63 and 2^64 for various uses.
static const uint64 k0 = 0xc3a5c85c97cb3127ULL;
static const uint64 k1 = 0xb492b66fbe98f273ULL;
static const uint64 k2 = 0x9ae16a3b2f90404fULL;
// Bitwise right rotate. Normally this will compile to a single
// instruction, especially if the shift is a manifest constant.
static uint64 Rotate(uint64 val, int shift) {
// Avoid shifting by 64: doing so yields an undefined result.
return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
}
static uint64 ShiftMix(uint64 val) {
return val ^ (val >> 47);
}
static uint64 HashLen16(uint64 u, uint64 v) {
return Hash128to64(uint128(u, v));
}
static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
// Murmur-inspired hashing.
uint64 a = (u ^ v) * mul;
a ^= (a >> 47);
uint64 b = (v ^ a) * mul;
b ^= (b >> 47);
b *= mul;
return b;
}
static uint64 HashLen0to16(const char* s, size_t len) {
if (len >= 8) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) + k2;
uint64 b = Fetch64(s + len - 8);
uint64 c = Rotate(b, 37) * mul + a;
uint64 d = (Rotate(a, 25) + b) * mul;
return HashLen16(c, d, mul);
}
if (len >= 4) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch32(s);
return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
}
if (len > 0) {
uint8 a = s[0];
uint8 b = s[len >> 1];
uint8 c = s[len - 1];
uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);
uint32 z = static_cast<uint32>(len) + (static_cast<uint32>(c) << 2);
return ShiftMix(y * k2 ^ z * k0) * k2;
}
return k2;
}
// This probably works well for 16-byte strings as well, but it may be overkill
// in that case.
static uint64 HashLen17to32(const char* s, size_t len) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) * k1;
uint64 b = Fetch64(s + 8);
uint64 c = Fetch64(s + len - 8) * mul;
uint64 d = Fetch64(s + len - 16) * k2;
return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d, a + Rotate(b + k2, 18) + c, mul);
}
// Return a 16-byte hash for 48 bytes. Quick and dirty.
// Callers do best to use "random-looking" values for a and b.
static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a,
uint64 b) {
a += w;
b = Rotate(b + a + z, 21);
uint64 c = a;
a += x;
a += y;
b += Rotate(a, 44);
return make_pair(a + z, b + c);
}
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint64 b) {
return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a,
b);
}
// Return an 8-byte hash for 33 to 64 bytes.
static uint64 HashLen33to64(const char* s, size_t len) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) * k2;
uint64 b = Fetch64(s + 8);
uint64 c = Fetch64(s + len - 24);
uint64 d = Fetch64(s + len - 32);
uint64 e = Fetch64(s + 16) * k2;
uint64 f = Fetch64(s + 24) * 9;
uint64 g = Fetch64(s + len - 8);
uint64 h = Fetch64(s + len - 16) * mul;
uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
uint64 v = ((a + g) ^ d) + f + 1;
uint64 w = swap64((u + v) * mul) + h;
uint64 x = Rotate(e + f, 42) + c;
uint64 y = (swap64((v + w) * mul) + g) * mul;
uint64 z = e + f + c;
a = swap64((x + z) * mul + y) + b;
b = ShiftMix((z + a) * mul + d + h) * mul;
return b + x;
}
uint64 CityHash64(const char* s, size_t len) {
if (len <= 32) {
if (len <= 16) {
return HashLen0to16(s, len);
} else {
return HashLen17to32(s, len);
}
} else if (len <= 64) {
return HashLen33to64(s, len);
}
// For strings over 64 bytes we hash the end first, and then as we
// loop we keep 56 bytes of state: v, w, x, y, and z.
uint64 x = Fetch64(s + len - 40);
uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
x = x * k1 + Fetch64(s);
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
len = (len - 1) & ~static_cast<size_t>(63);
do {
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
x ^= w.second;
y += v.first + Fetch64(s + 40);
z = Rotate(z + w.first, 33) * k1;
v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
std::swap(z, x);
s += 64;
len -= 64;
} while (len != 0);
return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z,
HashLen16(v.second, w.second) + x);
}
uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) {
return CityHash64WithSeeds(s, len, k2, seed);
}
uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) {
return HashLen16(CityHash64(s, len) - seed0, seed1);
}
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
// of any length representable in signed long. Based on City and Murmur.
static uint128 CityMurmur(const char* s, size_t len, uint128 seed) {
uint64 a = Uint128Low64(seed);
uint64 b = Uint128High64(seed);
uint64 c = 0;
uint64 d = 0;
signed long l = static_cast<long>(len) - 16;
if (l <= 0) { // len <= 16
a = ShiftMix(a * k1) * k1;
c = b * k1 + HashLen0to16(s, len);
d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c));
} else { // len > 16
c = HashLen16(Fetch64(s + len - 8) + k1, a);
d = HashLen16(b + len, c + Fetch64(s + len - 16));
a += d;
do {
a ^= ShiftMix(Fetch64(s) * k1) * k1;
a *= k1;
b ^= a;
c ^= ShiftMix(Fetch64(s + 8) * k1) * k1;
c *= k1;
d ^= c;
s += 16;
l -= 16;
} while (l > 0);
}
a = HashLen16(a, c);
b = HashLen16(d, b);
return uint128(a ^ b, HashLen16(b, a));
}
uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
if (len < 128) {
return CityMurmur(s, len, seed);
}
// We expect len >= 128 to be the common case. Keep 56 bytes of state:
// v, w, x, y, and z.
pair<uint64, uint64> v, w;
uint64 x = Uint128Low64(seed);
uint64 y = Uint128High64(seed);
uint64 z = len * k1;
v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
w.first = Rotate(y + z, 35) * k1 + x;
w.second = Rotate(x + Fetch64(s + 88), 53) * k1;
// This is the same inner loop as CityHash64(), manually unrolled.
do {
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
x ^= w.second;
y += v.first + Fetch64(s + 40);
z = Rotate(z + w.first, 33) * k1;
v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
std::swap(z, x);
s += 64;
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
x ^= w.second;
y += v.first + Fetch64(s + 40);
z = Rotate(z + w.first, 33) * k1;
v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
std::swap(z, x);
s += 64;
len -= 128;
} while (LIKELY(len >= 128));
x += Rotate(v.first + z, 49) * k0;
y = y * k0 + Rotate(w.second, 37);
z = z * k0 + Rotate(w.first, 27);
w.first *= 9;
v.first *= k0;
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
for (size_t tail_done = 0; tail_done < len;) {
tail_done += 32;
y = Rotate(x + y, 42) * k0 + v.second;
w.first += Fetch64(s + len - tail_done + 16);
x = x * k0 + w.first;
z += w.second + Fetch64(s + len - tail_done);
w.second += v.first;
v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second);
v.first *= k0;
}
// At this point our 56 bytes of state should contain more than
// enough information for a strong 128-bit hash. We use two
// different 56-byte-to-8-byte hashes to get a 16-byte final result.
x = HashLen16(x, v.first);
y = HashLen16(y + z, w.first);
return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second));
}
uint128 CityHash128(const char* s, size_t len) {
return len >= 16
? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0))
: CityHash128WithSeed(s, len, uint128(k0, k1));
}
} // namespace Common

110
src/common/cityhash.h Normal file
View file

@ -0,0 +1,110 @@
// Copyright (c) 2011 Google, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// CityHash, by Geoff Pike and Jyrki Alakuijala
//
// http://code.google.com/p/cityhash/
//
// This file provides a few functions for hashing strings. All of them are
// high-quality functions in the sense that they pass standard tests such
// as Austin Appleby's SMHasher. They are also fast.
//
// For 64-bit x86 code, on short strings, we don't know of anything faster than
// CityHash64 that is of comparable quality. We believe our nearest competitor
// is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash
// tables and most other hashing (excluding cryptography).
//
// For 64-bit x86 code, on long strings, the picture is more complicated.
// On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc.,
// CityHashCrc128 appears to be faster than all competitors of comparable
// quality. CityHash128 is also good but not quite as fast. We believe our
// nearest competitor is Bob Jenkins' Spooky. We don't have great data for
// other 64-bit CPUs, but for long strings we know that Spooky is slightly
// faster than CityHash on some relatively recent AMD x86-64 CPUs, for example.
// Note that CityHashCrc128 is declared in citycrc.h.
//
// For 32-bit x86 code, we don't know of anything faster than CityHash32 that
// is of comparable quality. We believe our nearest competitor is Murmur3A.
// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.)
//
// Functions in the CityHash family are not suitable for cryptography.
//
// Please see CityHash's README file for more details on our performance
// measurements and so on.
//
// WARNING: This code has been only lightly tested on big-endian platforms!
// It is known to work well on little-endian platforms that have a small penalty
// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
// It should work on all 32-bit and 64-bit platforms that allow unaligned reads;
// bug reports are welcome.
//
// By the way, for some hash functions, given strings a and b, the hash
// of a+b is easily derived from the hashes of a and b. This property
// doesn't hold for any hash functions in this file.
#pragma once
#include <utility>
#include <stdint.h>
#include <stdlib.h> // for size_t.
namespace Common {
typedef std::pair<uint64_t, uint64_t> uint128;
inline uint64_t Uint128Low64(const uint128& x) {
return x.first;
}
inline uint64_t Uint128High64(const uint128& x) {
return x.second;
}
// Hash function for a byte array.
uint64_t CityHash64(const char* buf, size_t len);
// Hash function for a byte array. For convenience, a 64-bit seed is also
// hashed into the result.
uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed);
// Hash function for a byte array. For convenience, two seeds are also
// hashed into the result.
uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1);
// Hash function for a byte array.
uint128 CityHash128(const char* s, size_t len);
// Hash function for a byte array. For convenience, a 128-bit seed is also
// hashed into the result.
uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed);
// Hash 128 input bits down to 64 bits of output.
// This is intended to be a reasonably good hash function.
inline uint64_t Hash128to64(const uint128& x) {
// Murmur-inspired hashing.
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
uint64_t a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
a ^= (a >> 47);
uint64_t b = (Uint128High64(x) ^ a) * kMul;
b ^= (b >> 47);
b *= kMul;
return b;
}
} // namespace Common

View file

@ -1,141 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#if defined(_MSC_VER)
#include <stdlib.h>
#endif
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/hash.h"
namespace Common {
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// Block read - if your platform needs to do endian-swapping or can only handle aligned reads, do
// the conversion here
static FORCE_INLINE u64 getblock64(const u64* p, size_t i) {
return p[i];
}
// Finalization mix - force all bits of a hash block to avalanche
static FORCE_INLINE u64 fmix64(u64 k) {
k ^= k >> 33;
k *= 0xff51afd7ed558ccdllu;
k ^= k >> 33;
k *= 0xc4ceb9fe1a85ec53llu;
k ^= k >> 33;
return k;
}
// This is the 128-bit variant of the MurmurHash3 hash function that is targeted for 64-bit
// platforms (MurmurHash3_x64_128). It was taken from:
// https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
void MurmurHash3_128(const void* key, size_t len, u32 seed, void* out) {
const u8* data = (const u8*)key;
const size_t nblocks = len / 16;
u64 h1 = seed;
u64 h2 = seed;
const u64 c1 = 0x87c37b91114253d5llu;
const u64 c2 = 0x4cf5ad432745937fllu;
// Body
const u64* blocks = (const u64*)(data);
for (size_t i = 0; i < nblocks; i++) {
u64 k1 = getblock64(blocks, i * 2 + 0);
u64 k2 = getblock64(blocks, i * 2 + 1);
k1 *= c1;
k1 = _rotl64(k1, 31);
k1 *= c2;
h1 ^= k1;
h1 = _rotl64(h1, 27);
h1 += h2;
h1 = h1 * 5 + 0x52dce729;
k2 *= c2;
k2 = _rotl64(k2, 33);
k2 *= c1;
h2 ^= k2;
h2 = _rotl64(h2, 31);
h2 += h1;
h2 = h2 * 5 + 0x38495ab5;
}
// Tail
const u8* tail = (const u8*)(data + nblocks * 16);
u64 k1 = 0;
u64 k2 = 0;
switch (len & 15) {
case 15:
k2 ^= ((u64)tail[14]) << 48;
case 14:
k2 ^= ((u64)tail[13]) << 40;
case 13:
k2 ^= ((u64)tail[12]) << 32;
case 12:
k2 ^= ((u64)tail[11]) << 24;
case 11:
k2 ^= ((u64)tail[10]) << 16;
case 10:
k2 ^= ((u64)tail[9]) << 8;
case 9:
k2 ^= ((u64)tail[8]) << 0;
k2 *= c2;
k2 = _rotl64(k2, 33);
k2 *= c1;
h2 ^= k2;
case 8:
k1 ^= ((u64)tail[7]) << 56;
case 7:
k1 ^= ((u64)tail[6]) << 48;
case 6:
k1 ^= ((u64)tail[5]) << 40;
case 5:
k1 ^= ((u64)tail[4]) << 32;
case 4:
k1 ^= ((u64)tail[3]) << 24;
case 3:
k1 ^= ((u64)tail[2]) << 16;
case 2:
k1 ^= ((u64)tail[1]) << 8;
case 1:
k1 ^= ((u64)tail[0]) << 0;
k1 *= c1;
k1 = _rotl64(k1, 31);
k1 *= c2;
h1 ^= k1;
};
// Finalization
h1 ^= len;
h2 ^= len;
h1 += h2;
h2 += h1;
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 += h2;
h2 += h1;
((u64*)out)[0] = h1;
((u64*)out)[1] = h2;
}
} // namespace Common

View file

@ -5,12 +5,11 @@
#pragma once
#include <cstddef>
#include "common/cityhash.h"
#include "common/common_types.h"
namespace Common {
void MurmurHash3_128(const void* key, size_t len, u32 seed, void* out);
/**
* Computes a 64-bit hash over the specified block of data
* @param data Block of data to compute hash over
@ -18,9 +17,20 @@ void MurmurHash3_128(const void* key, size_t len, u32 seed, void* out);
* @returns 64-bit hash value that was computed over the data block
*/
static inline u64 ComputeHash64(const void* data, size_t len) {
u64 res[2];
MurmurHash3_128(data, len, 0, res);
return res[0];
return CityHash64(static_cast<const char*>(data), len);
}
/**
* Computes a 64-bit hash of a struct. In addition to being POD (trivially copyable and having
* standard layout), it is also critical that either the struct includes no padding, or that any
* padding is initialized to a known value by memsetting the struct to 0 before filling it in.
*/
template <typename T>
static inline u64 ComputeStructHash64(const T& data) {
static_assert(
std::is_trivially_copyable<T>::value && std::is_standard_layout<T>::value,
"Type passed to ComputeStructHash64 must be trivially copyable and standard layout");
return ComputeHash64(&data, sizeof(data));
}
} // namespace Common

View file

@ -1,10 +1,6 @@
add_library(core STATIC
3ds.h
arm/arm_interface.h
arm/dynarmic/arm_dynarmic.cpp
arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_cp15.cpp
arm/dynarmic/arm_dynarmic_cp15.h
arm/dyncom/arm_dyncom.cpp
arm/dyncom/arm_dyncom.h
arm/dyncom/arm_dyncom_dec.cpp
@ -404,7 +400,17 @@ add_library(core STATIC
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp fmt)
if (ENABLE_WEB_SERVICE)
target_link_libraries(core PUBLIC json-headers web_service)
endif()
if (ARCHITECTURE_x86_64)
target_sources(core PRIVATE
arm/dynarmic/arm_dynarmic.cpp
arm/dynarmic/arm_dynarmic.h
arm/dynarmic/arm_dynarmic_cp15.cpp
arm/dynarmic/arm_dynarmic_cp15.h
)
target_link_libraries(core PRIVATE dynarmic)
endif()

View file

@ -7,7 +7,9 @@
#include "audio_core/audio_core.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_dynarmic.h"
#endif
#include "core/arm/dyncom/arm_dyncom.h"
#include "core/core.h"
#include "core/core_timing.h"
@ -147,7 +149,12 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
LOG_DEBUG(HW_Memory, "initialized OK");
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE);
#else
cpu_core = std::make_unique<ARM_DynCom>(USER32MODE);
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
cpu_core = std::make_unique<ARM_DynCom>(USER32MODE);
}

View file

@ -59,7 +59,9 @@ public:
/// Empty placeholder structure for services with no per-session data. The session data classes
/// in each service must inherit from this.
struct SessionDataBase {};
struct SessionDataBase {
virtual ~SessionDataBase() = default;
};
protected:
/// Creates the storage for the session data of the service.

View file

@ -40,6 +40,7 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) {
process->codeset = std::move(code_set);
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->status = ProcessStatus::Created;
process_list.push_back(process);
return process;
@ -151,6 +152,8 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
HandleSpecialMapping(vm_manager, mapping);
}
status = ProcessStatus::Running;
vm_manager.LogLayout(Log::Level::Debug);
Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority, this);
}

View file

@ -49,6 +49,8 @@ union ProcessFlags {
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
};
enum class ProcessStatus { Created, Running, Exited };
class ResourceLimit;
struct MemoryRegionInfo;
@ -122,6 +124,8 @@ public:
u16 kernel_version = 0;
/// The default CPU for this process, threads are scheduled on this cpu by default.
u8 ideal_processor = 0;
/// Current status of the process
ProcessStatus status;
/// The id of this process
u32 process_id = next_process_id++;

View file

@ -143,6 +143,36 @@ static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 add
return RESULT_SUCCESS;
}
static void ExitProcess() {
LOG_INFO(Kernel_SVC, "Process %u exiting", g_current_process->process_id);
ASSERT_MSG(g_current_process->status == ProcessStatus::Running, "Process has already exited");
g_current_process->status = ProcessStatus::Exited;
// Stop all the process threads that are currently waiting for objects.
auto& thread_list = GetThreadList();
for (auto& thread : thread_list) {
if (thread->owner_process != g_current_process)
continue;
if (thread == GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
}
// Kill the current thread
GetCurrentThread()->Stop();
Core::System::GetInstance().PrepareReschedule();
}
/// Maps a memory block to specified address
static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
LOG_TRACE(Kernel_SVC,
@ -685,7 +715,7 @@ static ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limi
for (unsigned int i = 0; i < name_count; ++i) {
u32 name = Memory::Read32(names + i * sizeof(u32));
s64 value = resource_limit->GetMaxResourceValue(names);
s64 value = resource_limit->GetMaxResourceValue(name);
Memory::Write64(values + i * sizeof(u64), value);
}
@ -1232,7 +1262,7 @@ static const FunctionDef SVC_Table[] = {
{0x00, nullptr, "Unknown"},
{0x01, HLE::Wrap<ControlMemory>, "ControlMemory"},
{0x02, HLE::Wrap<QueryMemory>, "QueryMemory"},
{0x03, nullptr, "ExitProcess"},
{0x03, ExitProcess, "ExitProcess"},
{0x04, nullptr, "GetProcessAffinityMask"},
{0x05, nullptr, "SetProcessAffinityMask"},
{0x06, nullptr, "GetProcessIdealProcessor"},
@ -1373,6 +1403,9 @@ void CallSVC(u32 immediate) {
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
ASSERT_MSG(g_current_process->status == ProcessStatus::Running,
"Running threads from exiting processes is unimplemented");
const FunctionDef* info = GetSVCInfo(immediate);
if (info) {
if (info->func) {

View file

@ -9,7 +9,6 @@
#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/result.h"
@ -51,6 +50,20 @@ constexpr ResultCode ERR_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorM
ErrorSummary::InvalidArgument,
ErrorLevel::Usage); // 0xE0E02BEC
/// Maximum number of threads that can be registered at the same time in the GSP module.
constexpr u32 MaxGSPThreads = 4;
/// Thread ids currently in use by the sessions connected to the GSPGPU service.
static std::array<bool, MaxGSPThreads> used_thread_ids = {false, false, false, false};
static u32 GetUnusedThreadId() {
for (u32 id = 0; id < MaxGSPThreads; ++id) {
if (!used_thread_ids[id])
return id;
}
ASSERT_MSG(false, "All GSP threads are in use");
}
/// Gets a pointer to a thread command buffer in GSP shared memory
static inline u8* GetCommandBuffer(Kernel::SharedPtr<Kernel::SharedMemory> shared_memory,
u32 thread_id) {
@ -319,12 +332,16 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x13, 1, 2);
u32 flags = rp.Pop<u32>();
interrupt_event = rp.PopObject<Kernel::Event>();
auto interrupt_event = rp.PopObject<Kernel::Event>();
// TODO(mailwl): return right error code instead assert
ASSERT_MSG((interrupt_event != nullptr), "handle is not valid!");
interrupt_event->name = "GSP_GSP_GPU::interrupt_event";
SessionData* session_data = GetSessionData(ctx.Session());
session_data->interrupt_event = std::move(interrupt_event);
session_data->registered = true;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
if (first_initialization) {
@ -335,25 +352,60 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
rb.Push(thread_id);
rb.Push(session_data->thread_id);
rb.PushCopyObjects(shared_memory);
thread_id++;
interrupt_event->Signal(); // TODO(bunnei): Is this correct?
LOG_WARNING(Service_GSP, "called, flags=0x%08X", flags);
LOG_DEBUG(Service_GSP, "called, flags=0x%08X", flags);
}
void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x14, 0, 0);
thread_id = 0;
interrupt_event = nullptr;
SessionData* session_data = GetSessionData(ctx.Session());
session_data->interrupt_event = nullptr;
session_data->registered = false;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_GSP, "(STUBBED) called");
LOG_DEBUG(Service_GSP, "called");
}
void GSP_GPU::SignalInterruptForThread(InterruptId interrupt_id, u32 thread_id) {
SessionData* session_data = FindRegisteredThreadData(thread_id);
if (session_data == nullptr)
return;
auto interrupt_event = session_data->interrupt_event;
if (interrupt_event == nullptr) {
LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!");
return;
}
InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(shared_memory, thread_id);
u8 next = interrupt_relay_queue->index;
next += interrupt_relay_queue->number_interrupts;
next = next % 0x34; // 0x34 is the number of interrupt slots
interrupt_relay_queue->number_interrupts += 1;
interrupt_relay_queue->slot[next] = interrupt_id;
interrupt_relay_queue->error_code = 0x0; // No error
// Update framebuffer information if requested
// TODO(yuriks): Confirm where this code should be called. It is definitely updated without
// executing any GSP commands, only waiting on the event.
// TODO(Subv): The real GSP module triggers PDC0 after updating both the top and bottom
// screen, it is currently unknown what PDC1 does.
int screen_id =
(interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1;
if (screen_id != -1) {
FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
if (info->is_dirty) {
GSP::SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
info->is_dirty.Assign(false);
}
}
interrupt_event->Signal();
}
/**
@ -363,43 +415,26 @@ void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
* @todo This probably does not belong in the GSP module, instead move to video_core
*/
void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) {
if (!gpu_right_acquired) {
return;
}
if (nullptr == interrupt_event) {
LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!");
return;
}
if (nullptr == shared_memory) {
LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!");
return;
}
for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
InterruptRelayQueue* interrupt_relay_queue =
GetInterruptRelayQueue(shared_memory, thread_id);
u8 next = interrupt_relay_queue->index;
next += interrupt_relay_queue->number_interrupts;
next = next % 0x34; // 0x34 is the number of interrupt slots
interrupt_relay_queue->number_interrupts += 1;
interrupt_relay_queue->slot[next] = interrupt_id;
interrupt_relay_queue->error_code = 0x0; // No error
// Update framebuffer information if requested
// TODO(yuriks): Confirm where this code should be called. It is definitely updated without
// executing any GSP commands, only waiting on the event.
int screen_id =
(interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1;
if (screen_id != -1) {
FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
if (info->is_dirty) {
GSP::SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
info->is_dirty.Assign(false);
}
// The PDC0 and PDC1 interrupts are fired even if the GPU right hasn't been acquired.
// Normal interrupts are only signaled for the active thread (ie, the thread that has the GPU
// right), but the PDC0/1 interrupts are signaled for every registered thread.
if (interrupt_id == InterruptId::PDC0 || interrupt_id == InterruptId::PDC1) {
for (u32 thread_id = 0; thread_id < MaxGSPThreads; ++thread_id) {
SignalInterruptForThread(interrupt_id, thread_id);
}
return;
}
interrupt_event->Signal();
// For normal interrupts, don't do anything if no process has acquired the GPU right.
if (active_thread_id == -1)
return;
SignalInterruptForThread(interrupt_id, active_thread_id);
}
MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255));
@ -622,18 +657,34 @@ void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) {
u32 flag = rp.Pop<u32>();
auto process = rp.PopObject<Kernel::Process>();
gpu_right_acquired = true;
SessionData* session_data = GetSessionData(ctx.Session());
LOG_WARNING(Service_GSP, "called flag=%08X process=%u thread_id=%u", flag, process->process_id,
session_data->thread_id);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_GSP, "called flag=%08X process=%u", flag, process->process_id);
if (active_thread_id == session_data->thread_id) {
rb.Push(ResultCode(ErrorDescription::AlreadyDone, ErrorModule::GX, ErrorSummary::Success,
ErrorLevel::Success));
return;
}
// TODO(Subv): This case should put the caller thread to sleep until the right is released.
ASSERT_MSG(active_thread_id == -1, "GPU right has already been acquired");
active_thread_id = session_data->thread_id;
rb.Push(RESULT_SUCCESS);
}
void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x17, 0, 0);
gpu_right_acquired = false;
SessionData* session_data = GetSessionData(ctx.Session());
ASSERT_MSG(active_thread_id == session_data->thread_id,
"Wrong thread tried to release GPU right");
active_thread_id = -1;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
@ -655,6 +706,17 @@ void GSP_GPU::StoreDataCache(Kernel::HLERequestContext& ctx) {
size, process->process_id);
}
SessionData* GSP_GPU::FindRegisteredThreadData(u32 thread_id) {
for (auto& session_info : connected_sessions) {
SessionData* data = static_cast<SessionData*>(session_info.data.get());
if (!data->registered)
continue;
if (data->thread_id == thread_id)
return data;
}
return nullptr;
}
GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) {
static const FunctionInfo functions[] = {
{0x00010082, &GSP_GPU::WriteHWRegs, "WriteHWRegs"},
@ -691,17 +753,26 @@ GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) {
};
RegisterHandlers(functions);
interrupt_event = nullptr;
using Kernel::MemoryPermission;
shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite,
MemoryPermission::ReadWrite, 0,
Kernel::MemoryRegion::BASE, "GSP:SharedMemory");
thread_id = 0;
gpu_right_acquired = false;
first_initialization = true;
};
SessionData::SessionData() {
// Assign a new thread id to this session when it connects. Note: In the real GSP service this
// is done through a real thread (svcCreateThread) but we have to simulate it since our HLE
// services don't have threads.
thread_id = GetUnusedThreadId();
used_thread_ids[thread_id] = true;
}
SessionData::~SessionData() {
// Free the thread id slot so that other sessions can use it.
used_thread_ids[thread_id] = false;
}
} // namespace GSP
} // namespace Service

View file

@ -8,12 +8,12 @@
#include <string>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
namespace Kernel {
class Event;
class SharedMemory;
} // namespace Kernel
@ -179,7 +179,19 @@ struct CommandBuffer {
};
static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrect size");
class GSP_GPU final : public ServiceFramework<GSP_GPU> {
struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
SessionData();
~SessionData();
/// Event triggered when GSP interrupt has been signalled
Kernel::SharedPtr<Kernel::Event> interrupt_event;
/// Thread index into interrupt relay queue
u32 thread_id;
/// Whether RegisterInterruptRelayQueue was called for this session
bool registered = false;
};
class GSP_GPU final : public ServiceFramework<GSP_GPU, SessionData> {
public:
GSP_GPU();
~GSP_GPU() = default;
@ -201,6 +213,14 @@ public:
FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index);
private:
/**
* Signals that the specified interrupt type has occurred to userland code for the specified GSP
* thread id.
* @param interrupt_id ID of interrupt that is being signalled.
* @param thread_id GSP thread that will receive the interrupt.
*/
void SignalInterruptForThread(InterruptId interrupt_id, u32 thread_id);
/**
* GSP_GPU::WriteHWRegs service function
*
@ -351,14 +371,15 @@ private:
*/
void StoreDataCache(Kernel::HLERequestContext& ctx);
/// Event triggered when GSP interrupt has been signalled
Kernel::SharedPtr<Kernel::Event> interrupt_event;
/// GSP shared memoryings
Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
/// Thread index into interrupt relay queue
u32 thread_id = 0;
/// Returns the session data for the specified registered thread id, or nullptr if not found.
SessionData* FindRegisteredThreadData(u32 thread_id);
/// GSP shared memory
Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
/// Thread id that currently has GPU rights or -1 if none.
int active_thread_id = -1;
bool gpu_right_acquired = false;
bool first_initialization = true;
};

View file

@ -3,15 +3,12 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <atomic>
#include <cmath>
#include <memory>
#include "common/logging/log.h"
#include "core/3ds.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/input.h"
#include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/shared_memory.h"
@ -23,27 +20,7 @@
namespace Service {
namespace HID {
// Handle to shared memory region designated to HID_User service
static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
// Event handles
static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1;
static Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2;
static Kernel::SharedPtr<Kernel::Event> event_accelerometer;
static Kernel::SharedPtr<Kernel::Event> event_gyroscope;
static Kernel::SharedPtr<Kernel::Event> event_debug_pad;
static u32 next_pad_index;
static u32 next_touch_index;
static u32 next_accelerometer_index;
static u32 next_gyroscope_index;
static int enable_accelerometer_count; // positive means enabled
static int enable_gyroscope_count; // positive means enabled
static CoreTiming::EventType* pad_update_event;
static CoreTiming::EventType* accelerometer_update_event;
static CoreTiming::EventType* gyroscope_update_event;
static std::weak_ptr<Module> current_module;
// Updating period for each HID device. These empirical values are measured from a 11.2 3DS.
constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
@ -53,13 +30,6 @@ constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
constexpr float accelerometer_coef = 512.0f; // measured from hw test result
constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call
static std::atomic<bool> is_device_reload_pending;
static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
buttons;
static std::unique_ptr<Input::AnalogDevice> circle_pad;
static std::unique_ptr<Input::MotionDevice> motion_device;
static std::unique_ptr<Input::TouchDevice> touch_device;
DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
// 30 degree and 60 degree are angular thresholds for directions
constexpr float TAN30 = 0.577350269f;
@ -89,7 +59,7 @@ DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
return state;
}
static void LoadInputDevices() {
void Module::LoadInputDevices() {
std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
@ -99,16 +69,7 @@ static void LoadInputDevices() {
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
}
static void UnloadInputDevices() {
for (auto& button : buttons) {
button.reset();
}
circle_pad.reset();
motion_device.reset();
touch_device.reset();
}
static void UpdatePadCallback(u64 userdata, int cycles_late) {
void Module::UpdatePadCallback(u64 userdata, int cycles_late) {
SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
if (is_device_reload_pending.exchange(false))
@ -198,7 +159,7 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) {
CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
}
static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
void Module::UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
mem->accelerometer.index = next_accelerometer_index;
@ -240,7 +201,7 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
CoreTiming::ScheduleEvent(accelerometer_update_ticks - cycles_late, accelerometer_update_event);
}
static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
void Module::UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
mem->gyroscope.index = next_gyroscope_index;
@ -273,134 +234,122 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
CoreTiming::ScheduleEvent(gyroscope_update_ticks - cycles_late, gyroscope_update_event);
}
void GetIPCHandles(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header
// TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling)
cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).Unwrap();
cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).Unwrap();
cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).Unwrap();
cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).Unwrap();
cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).Unwrap();
cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).Unwrap();
void Module::Interface::GetIPCHandles(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx, 0xA, 0, 0};
IPC::RequestBuilder rb = rp.MakeBuilder(1, 7);
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(hid->shared_mem, hid->event_pad_or_touch_1, hid->event_pad_or_touch_2,
hid->event_accelerometer, hid->event_gyroscope, hid->event_debug_pad);
}
void EnableAccelerometer(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
void Module::Interface::EnableAccelerometer(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx, 0x11, 0, 0};
++enable_accelerometer_count;
++hid->enable_accelerometer_count;
// Schedules the accelerometer update event if the accelerometer was just enabled
if (enable_accelerometer_count == 1) {
CoreTiming::ScheduleEvent(accelerometer_update_ticks, accelerometer_update_event);
if (hid->enable_accelerometer_count == 1) {
CoreTiming::ScheduleEvent(accelerometer_update_ticks, hid->accelerometer_update_event);
}
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void DisableAccelerometer(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
void Module::Interface::DisableAccelerometer(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx, 0x12, 0, 0};
--enable_accelerometer_count;
--hid->enable_accelerometer_count;
// Unschedules the accelerometer update event if the accelerometer was just disabled
if (enable_accelerometer_count == 0) {
CoreTiming::UnscheduleEvent(accelerometer_update_event, 0);
if (hid->enable_accelerometer_count == 0) {
CoreTiming::UnscheduleEvent(hid->accelerometer_update_event, 0);
}
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void EnableGyroscopeLow(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
void Module::Interface::EnableGyroscopeLow(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx, 0x13, 0, 0};
++enable_gyroscope_count;
++hid->enable_gyroscope_count;
// Schedules the gyroscope update event if the gyroscope was just enabled
if (enable_gyroscope_count == 1) {
CoreTiming::ScheduleEvent(gyroscope_update_ticks, gyroscope_update_event);
if (hid->enable_gyroscope_count == 1) {
CoreTiming::ScheduleEvent(gyroscope_update_ticks, hid->gyroscope_update_event);
}
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void DisableGyroscopeLow(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
void Module::Interface::DisableGyroscopeLow(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx, 0x14, 0, 0};
--enable_gyroscope_count;
--hid->enable_gyroscope_count;
// Unschedules the gyroscope update event if the gyroscope was just disabled
if (enable_gyroscope_count == 0) {
CoreTiming::UnscheduleEvent(gyroscope_update_event, 0);
if (hid->enable_gyroscope_count == 0) {
CoreTiming::UnscheduleEvent(hid->gyroscope_update_event, 0);
}
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_HID, "called");
}
void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
void Module::Interface::GetGyroscopeLowRawToDpsCoefficient(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx, 0x15, 0, 0};
cmd_buff[1] = RESULT_SUCCESS.raw;
f32 coef = gyroscope_coef;
memcpy(&cmd_buff[2], &coef, 4);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.PushRaw<f32>(gyroscope_coef);
}
void GetGyroscopeLowCalibrateParam(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
void Module::Interface::GetGyroscopeLowCalibrateParam(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx, 0x16, 0, 0};
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(6, 0);
rb.Push(RESULT_SUCCESS);
const s16 param_unit = 6700; // an approximate value taken from hw
GyroscopeCalibrateParam param = {
{0, param_unit, -param_unit}, {0, param_unit, -param_unit}, {0, param_unit, -param_unit},
};
memcpy(&cmd_buff[2], &param, sizeof(param));
rb.PushRaw(param);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetSoundVolume(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
void Module::Interface::GetSoundVolume(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx, 0x17, 0, 0};
const u8 volume = 0x3F; // TODO(purpasmart): Find out if this is the max value for the volume
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = volume;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(volume);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void Init() {
Module::Interface::Interface(std::shared_ptr<Module> hid, const char* name, u32 max_session)
: ServiceFramework(name, max_session), hid(std::move(hid)) {}
Module::Module() {
using namespace Kernel;
AddService(new HID_U_Interface);
AddService(new HID_SPVR_Interface);
is_device_reload_pending.store(true);
using Kernel::MemoryPermission;
shared_mem =
SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read,
0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
next_pad_index = 0;
next_touch_index = 0;
next_accelerometer_index = 0;
next_gyroscope_index = 0;
enable_accelerometer_count = 0;
enable_gyroscope_count = 0;
0, MemoryRegion::BASE, "HID:SharedMemory");
// Create event handles
event_pad_or_touch_1 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch1");
@ -410,27 +359,35 @@ void Init() {
event_debug_pad = Event::Create(ResetType::OneShot, "HID:EventDebugPad");
// Register update callbacks
pad_update_event = CoreTiming::RegisterEvent("HID::UpdatePadCallback", UpdatePadCallback);
accelerometer_update_event =
CoreTiming::RegisterEvent("HID::UpdateAccelerometerCallback", UpdateAccelerometerCallback);
gyroscope_update_event =
CoreTiming::RegisterEvent("HID::UpdateGyroscopeCallback", UpdateGyroscopeCallback);
pad_update_event =
CoreTiming::RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
UpdatePadCallback(userdata, cycles_late);
});
accelerometer_update_event = CoreTiming::RegisterEvent(
"HID::UpdateAccelerometerCallback", [this](u64 userdata, int cycles_late) {
UpdateAccelerometerCallback(userdata, cycles_late);
});
gyroscope_update_event = CoreTiming::RegisterEvent(
"HID::UpdateGyroscopeCallback",
[this](u64 userdata, int cycles_late) { UpdateGyroscopeCallback(userdata, cycles_late); });
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
}
void Shutdown() {
shared_mem = nullptr;
event_pad_or_touch_1 = nullptr;
event_pad_or_touch_2 = nullptr;
event_accelerometer = nullptr;
event_gyroscope = nullptr;
event_debug_pad = nullptr;
UnloadInputDevices();
void Module::ReloadInputDevices() {
is_device_reload_pending.store(true);
}
void ReloadInputDevices() {
is_device_reload_pending.store(true);
if (auto hid = current_module.lock())
hid->ReloadInputDevices();
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto hid = std::make_shared<Module>();
std::make_shared<User>(hid)->InstallAsService(service_manager);
std::make_shared<Spvr>(hid)->InstallAsService(service_manager);
current_module = hid;
}
} // namespace HID

View file

@ -5,17 +5,29 @@
#pragma once
#include <array>
#include <atomic>
#ifndef _MSC_VER
#include <cstddef>
#endif
#include <memory>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/frontend/input.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/service.h"
#include "core/settings.h"
namespace Service {
namespace Kernel {
class Event;
class SharedMemory;
}
class Interface;
namespace CoreTiming {
class EventType;
};
namespace Service {
namespace HID {
@ -186,93 +198,140 @@ struct DirectionState {
/// Translates analog stick axes to directions. This is exposed for ir_rst module to use.
DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y);
/**
* HID::GetIPCHandles service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : IPC Command Structure translate-header
* 3 : Handle to HID shared memory
* 4 : Event signaled by HID
* 5 : Event signaled by HID
* 6 : Event signaled by HID
* 7 : Gyroscope event
* 8 : Event signaled by HID
*/
void GetIPCHandles(Interface* self);
class Module final {
public:
Module();
/**
* HID::EnableAccelerometer service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void EnableAccelerometer(Interface* self);
class Interface : public ServiceFramework<Interface> {
public:
Interface(std::shared_ptr<Module> hid, const char* name, u32 max_session);
/**
* HID::DisableAccelerometer service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void DisableAccelerometer(Interface* self);
protected:
/**
* HID::GetIPCHandles service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : IPC Command Structure translate-header
* 3 : Handle to HID shared memory
* 4 : Event signaled by HID
* 5 : Event signaled by HID
* 6 : Event signaled by HID
* 7 : Gyroscope event
* 8 : Event signaled by HID
*/
void GetIPCHandles(Kernel::HLERequestContext& ctx);
/**
* HID::EnableGyroscopeLow service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void EnableGyroscopeLow(Interface* self);
/**
* HID::EnableAccelerometer service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void EnableAccelerometer(Kernel::HLERequestContext& ctx);
/**
* HID::DisableGyroscopeLow service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void DisableGyroscopeLow(Interface* self);
/**
* HID::DisableAccelerometer service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void DisableAccelerometer(Kernel::HLERequestContext& ctx);
/**
* HID::GetSoundVolume service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 output value
*/
void GetSoundVolume(Interface* self);
/**
* HID::EnableGyroscopeLow service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void EnableGyroscopeLow(Kernel::HLERequestContext& ctx);
/**
* HID::GetGyroscopeLowRawToDpsCoefficient service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : float output value
*/
void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self);
/**
* HID::DisableGyroscopeLow service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void DisableGyroscopeLow(Kernel::HLERequestContext& ctx);
/**
* HID::GetGyroscopeLowCalibrateParam service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2~6 (18 bytes) : struct GyroscopeCalibrateParam
*/
void GetGyroscopeLowCalibrateParam(Service::Interface* self);
/**
* HID::GetSoundVolume service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : u8 output value
*/
void GetSoundVolume(Kernel::HLERequestContext& ctx);
/// Initialize HID service
void Init();
/**
* HID::GetGyroscopeLowRawToDpsCoefficient service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : float output value
*/
void GetGyroscopeLowRawToDpsCoefficient(Kernel::HLERequestContext& ctx);
/// Shutdown HID service
void Shutdown();
/**
* HID::GetGyroscopeLowCalibrateParam service function
* Inputs:
* None
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2~6 (18 bytes) : struct GyroscopeCalibrateParam
*/
void GetGyroscopeLowCalibrateParam(Kernel::HLERequestContext& ctx);
private:
std::shared_ptr<Module> hid;
};
void ReloadInputDevices();
private:
void LoadInputDevices();
void UpdatePadCallback(u64 userdata, int cycles_late);
void UpdateAccelerometerCallback(u64 userdata, int cycles_late);
void UpdateGyroscopeCallback(u64 userdata, int cycles_late);
// Handle to shared memory region designated to HID_User service
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
// Event handles
Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_1;
Kernel::SharedPtr<Kernel::Event> event_pad_or_touch_2;
Kernel::SharedPtr<Kernel::Event> event_accelerometer;
Kernel::SharedPtr<Kernel::Event> event_gyroscope;
Kernel::SharedPtr<Kernel::Event> event_debug_pad;
u32 next_pad_index = 0;
u32 next_touch_index = 0;
u32 next_accelerometer_index = 0;
u32 next_gyroscope_index = 0;
int enable_accelerometer_count = 0; // positive means enabled
int enable_gyroscope_count = 0; // positive means enabled
CoreTiming::EventType* pad_update_event;
CoreTiming::EventType* accelerometer_update_event;
CoreTiming::EventType* gyroscope_update_event;
std::atomic<bool> is_device_reload_pending{true};
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
buttons;
std::unique_ptr<Input::AnalogDevice> circle_pad;
std::unique_ptr<Input::MotionDevice> motion_device;
std::unique_ptr<Input::TouchDevice> touch_device;
};
void InstallInterfaces(SM::ServiceManager& service_manager);
/// Reload input devices. Used when input configuration changed
void ReloadInputDevices();

View file

@ -2,27 +2,26 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/hid_spvr.h"
namespace Service {
namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
{0x000A0000, GetIPCHandles, "GetIPCHandles"},
{0x000B0000, nullptr, "StartAnalogStickCalibration"},
{0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
{0x00110000, EnableAccelerometer, "EnableAccelerometer"},
{0x00120000, DisableAccelerometer, "DisableAccelerometer"},
{0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
{0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
{0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"},
{0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"},
{0x00170000, GetSoundVolume, "GetSoundVolume"},
};
HID_SPVR_Interface::HID_SPVR_Interface() {
Register(FunctionTable);
Spvr::Spvr(std::shared_ptr<Module> hid) : Module::Interface(std::move(hid), "hid:SPVR", 6) {
static const FunctionInfo functions[] = {
{0x000A0000, &Spvr::GetIPCHandles, "GetIPCHandles"},
{0x000B0000, nullptr, "StartAnalogStickCalibration"},
{0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
{0x00110000, &Spvr::EnableAccelerometer, "EnableAccelerometer"},
{0x00120000, &Spvr::DisableAccelerometer, "DisableAccelerometer"},
{0x00130000, &Spvr::EnableGyroscopeLow, "EnableGyroscopeLow"},
{0x00140000, &Spvr::DisableGyroscopeLow, "DisableGyroscopeLow"},
{0x00150000, &Spvr::GetGyroscopeLowRawToDpsCoefficient,
"GetGyroscopeLowRawToDpsCoefficient"},
{0x00160000, &Spvr::GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"},
{0x00170000, &Spvr::GetSoundVolume, "GetSoundVolume"},
};
RegisterHandlers(functions);
}
} // namespace HID

View file

@ -4,19 +4,15 @@
#pragma once
#include "core/hle/service/service.h"
#include "core/hle/service/hid/hid.h"
namespace Service {
namespace HID {
class HID_SPVR_Interface : public Service::Interface {
class Spvr final : public Module::Interface {
public:
HID_SPVR_Interface();
std::string GetPortName() const override {
return "hid:SPVR";
}
explicit Spvr(std::shared_ptr<Module> hid);
};
} // namespace HID
} // namespace Service
} // namespace Service

View file

@ -2,27 +2,26 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/hid_user.h"
namespace Service {
namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
{0x000A0000, GetIPCHandles, "GetIPCHandles"},
{0x000B0000, nullptr, "StartAnalogStickCalibration"},
{0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
{0x00110000, EnableAccelerometer, "EnableAccelerometer"},
{0x00120000, DisableAccelerometer, "DisableAccelerometer"},
{0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
{0x00140000, DisableGyroscopeLow, "DisableGyroscopeLow"},
{0x00150000, GetGyroscopeLowRawToDpsCoefficient, "GetGyroscopeLowRawToDpsCoefficient"},
{0x00160000, GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"},
{0x00170000, GetSoundVolume, "GetSoundVolume"},
};
HID_U_Interface::HID_U_Interface() {
Register(FunctionTable);
User::User(std::shared_ptr<Module> hid) : Module::Interface(std::move(hid), "hid:USER", 6) {
static const FunctionInfo functions[] = {
{0x000A0000, &User::GetIPCHandles, "GetIPCHandles"},
{0x000B0000, nullptr, "StartAnalogStickCalibration"},
{0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
{0x00110000, &User::EnableAccelerometer, "EnableAccelerometer"},
{0x00120000, &User::DisableAccelerometer, "DisableAccelerometer"},
{0x00130000, &User::EnableGyroscopeLow, "EnableGyroscopeLow"},
{0x00140000, &User::DisableGyroscopeLow, "DisableGyroscopeLow"},
{0x00150000, &User::GetGyroscopeLowRawToDpsCoefficient,
"GetGyroscopeLowRawToDpsCoefficient"},
{0x00160000, &User::GetGyroscopeLowCalibrateParam, "GetGyroscopeLowCalibrateParam"},
{0x00170000, &User::GetSoundVolume, "GetSoundVolume"},
};
RegisterHandlers(functions);
}
} // namespace HID

View file

@ -4,7 +4,7 @@
#pragma once
#include "core/hle/service/service.h"
#include "core/hle/service/hid/hid.h"
// This service is used for interfacing to physical user controls.
// Uses include game pad controls, touchscreen, accelerometers, gyroscopes, and debug pad.
@ -12,17 +12,10 @@
namespace Service {
namespace HID {
/**
* HID service interface.
*/
class HID_U_Interface : public Service::Interface {
class User final : public Module::Interface {
public:
HID_U_Interface();
std::string GetPortName() const override {
return "hid:USER";
}
explicit User(std::shared_ptr<Module> hid);
};
} // namespace HID
} // namespace Service
} // namespace Service

View file

@ -277,7 +277,7 @@ void Init() {
DLP::Init();
FRD::Init();
GSP::InstallInterfaces(*SM::g_service_manager);
HID::Init();
HID::InstallInterfaces(*SM::g_service_manager);
IR::InstallInterfaces(*SM::g_service_manager);
MVD::Init();
NDM::Init();
@ -307,7 +307,6 @@ void Shutdown() {
NIM::Shutdown();
NEWS::Shutdown();
NDM::Shutdown();
HID::Shutdown();
FRD::Shutdown();
DLP::Shutdown();
CFG::Shutdown();

View file

@ -84,86 +84,86 @@ ResultCode ConversionConfiguration::SetStandardCoefficient(
}
static void SetInputFormat(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1, 1, 0);
conversion.input_format = static_cast<InputFormat>(cmd_buff[1]);
conversion.input_format = rp.PopEnum<InputFormat>();
cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast<u8>(conversion.input_format));
}
static void GetInputFormat(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = static_cast<u32>(conversion.input_format);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.PushEnum(conversion.input_format);
LOG_DEBUG(Service_Y2R, "called input_format=%hhu", static_cast<u8>(conversion.input_format));
}
static void SetOutputFormat(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0);
conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]);
conversion.output_format = rp.PopEnum<OutputFormat>();
cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast<u8>(conversion.output_format));
}
static void GetOutputFormat(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = static_cast<u32>(conversion.output_format);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.PushEnum(conversion.output_format);
LOG_DEBUG(Service_Y2R, "called output_format=%hhu", static_cast<u8>(conversion.output_format));
}
static void SetRotation(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 1, 0);
conversion.rotation = static_cast<Rotation>(cmd_buff[1]);
conversion.rotation = rp.PopEnum<Rotation>();
cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast<u8>(conversion.rotation));
}
static void GetRotation(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x6, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = static_cast<u32>(conversion.rotation);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.PushEnum(conversion.rotation);
LOG_DEBUG(Service_Y2R, "called rotation=%hhu", static_cast<u8>(conversion.rotation));
}
static void SetBlockAlignment(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x7, 1, 0);
conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]);
conversion.block_alignment = rp.PopEnum<BlockAlignment>();
cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu",
static_cast<u8>(conversion.block_alignment));
}
static void GetBlockAlignment(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x8, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = static_cast<u32>(conversion.block_alignment);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.PushEnum(conversion.block_alignment);
LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu",
static_cast<u8>(conversion.block_alignment));
@ -177,11 +177,12 @@ static void GetBlockAlignment(Interface* self) {
* 1 : Result of function, 0 on success, otherwise error code
*/
static void SetSpacialDithering(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
spacial_dithering_enabled = cmd_buff[1] & 0xF;
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0);
cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
spacial_dithering_enabled = rp.Pop<u8>() & 0xF;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
@ -193,7 +194,9 @@ static void SetSpacialDithering(Interface* self) {
* 2 : u8, 0 = Disabled, 1 = Enabled
*/
static void GetSpacialDithering(Interface* self) {
IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0xA, 2, 0);
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xA, 0, 0);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(spacial_dithering_enabled != 0);
@ -208,11 +211,11 @@ static void GetSpacialDithering(Interface* self) {
* 1 : Result of function, 0 on success, otherwise error code
*/
static void SetTemporalDithering(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
temporal_dithering_enabled = cmd_buff[1] & 0xF;
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 1, 0);
temporal_dithering_enabled = rp.Pop<u8>() & 0xF;
cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
@ -224,11 +227,11 @@ static void SetTemporalDithering(Interface* self) {
* 2 : u8, 0 = Disabled, 1 = Enabled
*/
static void GetTemporalDithering(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = temporal_dithering_enabled;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(temporal_dithering_enabled);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
@ -241,11 +244,11 @@ static void GetTemporalDithering(Interface* self) {
* 1 : Result of function, 0 on success, otherwise error code
*/
static void SetTransferEndInterrupt(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
transfer_end_interrupt_enabled = cmd_buff[1] & 0xf;
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 1, 0);
transfer_end_interrupt_enabled = rp.Pop<u8>() & 0xF;
cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
@ -257,11 +260,11 @@ static void SetTransferEndInterrupt(Interface* self) {
* 2 : u8, 0 = Disabled, 1 = Enabled
*/
static void GetTransferEndInterrupt(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xE, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = transfer_end_interrupt_enabled;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(transfer_end_interrupt_enabled);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
@ -273,18 +276,18 @@ static void GetTransferEndInterrupt(Interface* self) {
* 3 : The handle of the completion event
*/
static void GetTransferEndEvent(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).Unwrap();
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
rb.Push(RESULT_SUCCESS);
rb.PushCopyHandles(Kernel::g_handle_table.Create(completion_event).Unwrap());
LOG_DEBUG(Service_Y2R, "called");
}
static void SetSendingY(Interface* self) {
// The helper should be passed by argument to the function
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00100102);
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x10, 4, 2);
conversion.src_Y.address = rp.Pop<u32>();
conversion.src_Y.image_size = rp.Pop<u32>();
conversion.src_Y.transfer_unit = rp.Pop<u32>();
@ -302,7 +305,7 @@ static void SetSendingY(Interface* self) {
static void SetSendingU(Interface* self) {
// The helper should be passed by argument to the function
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00110102);
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x11, 4, 2);
conversion.src_U.address = rp.Pop<u32>();
conversion.src_U.image_size = rp.Pop<u32>();
conversion.src_U.transfer_unit = rp.Pop<u32>();
@ -319,37 +322,41 @@ static void SetSendingU(Interface* self) {
}
static void SetSendingV(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
// The helper should be passed by argument to the function
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 4, 2);
conversion.src_V.address = cmd_buff[1];
conversion.src_V.image_size = cmd_buff[2];
conversion.src_V.transfer_unit = cmd_buff[3];
conversion.src_V.gap = cmd_buff[4];
conversion.src_V.address = rp.Pop<u32>();
conversion.src_V.image_size = rp.Pop<u32>();
conversion.src_V.transfer_unit = rp.Pop<u32>();
conversion.src_V.gap = rp.Pop<u32>();
Kernel::Handle src_process_handle = rp.PopHandle();
cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
"src_process_handle=0x%08X",
conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap,
cmd_buff[6]);
static_cast<u32>(src_process_handle));
}
static void SetSendingYUYV(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
// The helper should be passed by argument to the function
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x13, 4, 2);
conversion.src_YUYV.address = cmd_buff[1];
conversion.src_YUYV.image_size = cmd_buff[2];
conversion.src_YUYV.transfer_unit = cmd_buff[3];
conversion.src_YUYV.gap = cmd_buff[4];
conversion.src_YUYV.address = rp.Pop<u32>();
conversion.src_YUYV.image_size = rp.Pop<u32>();
conversion.src_YUYV.transfer_unit = rp.Pop<u32>();
conversion.src_YUYV.gap = rp.Pop<u32>();
Kernel::Handle src_process_handle = rp.PopHandle();
cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
"src_process_handle=0x%08X",
conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit,
conversion.src_YUYV.gap, cmd_buff[6]);
conversion.src_YUYV.gap, static_cast<u32>(src_process_handle));
}
/**
@ -359,11 +366,11 @@ static void SetSendingYUYV(Interface* self) {
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
static void IsFinishedSendingYuv(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x14, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 1;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(1);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
@ -375,11 +382,11 @@ static void IsFinishedSendingYuv(Interface* self) {
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
static void IsFinishedSendingY(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x15, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 1;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(1);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
@ -391,11 +398,11 @@ static void IsFinishedSendingY(Interface* self) {
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
static void IsFinishedSendingU(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 1;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(1);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
@ -407,30 +414,31 @@ static void IsFinishedSendingU(Interface* self) {
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
static void IsFinishedSendingV(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 1;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(1);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
static void SetReceiving(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 4, 2);
conversion.dst.address = cmd_buff[1];
conversion.dst.image_size = cmd_buff[2];
conversion.dst.transfer_unit = cmd_buff[3];
conversion.dst.gap = cmd_buff[4];
conversion.dst.address = rp.Pop<u32>();
conversion.dst.image_size = rp.Pop<u32>();
conversion.dst.transfer_unit = rp.Pop<u32>();
conversion.dst.gap = rp.Pop<u32>();
Kernel::Handle dst_process_handle = rp.PopHandle();
cmd_buff[0] = IPC::MakeHeader(0x18, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
"dst_process_handle=0x%08X",
conversion.dst.image_size, conversion.dst.transfer_unit, conversion.dst.gap,
cmd_buff[6]);
static_cast<u32>(dst_process_handle));
}
/**
@ -440,65 +448,67 @@ static void SetReceiving(Interface* self) {
* 2 : u8, 0 = Not Finished, 1 = Finished
*/
static void IsFinishedReceiving(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x19, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 1;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(1);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
static void SetInputLineWidth(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 1, 0);
u32 input_line_width = rp.Pop<u32>();
cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0);
cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(conversion.SetInputLineWidth(input_line_width));
LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]);
LOG_DEBUG(Service_Y2R, "called input_line_width=%u", input_line_width);
}
static void GetInputLineWidth(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = conversion.input_line_width;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(conversion.input_line_width);
LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width);
}
static void SetInputLines(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1C, 1, 0);
u32 input_lines = rp.Pop<u32>();
cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0);
cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(conversion.SetInputLines(input_lines));
LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]);
LOG_DEBUG(Service_Y2R, "called input_lines=%u", input_lines);
}
static void GetInputLines(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = static_cast<u32>(conversion.input_lines);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(conversion.input_lines));
LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines);
}
static void SetCoefficient(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 4, 0);
const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]);
std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet));
rp.PopRaw<CoefficientSet>(conversion.coefficients);
cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]",
coefficients[0], coefficients[1], coefficients[2], coefficients[3], coefficients[4],
coefficients[5], coefficients[6], coefficients[7]);
conversion.coefficients[0], conversion.coefficients[1], conversion.coefficients[2],
conversion.coefficients[3], conversion.coefficients[4], conversion.coefficients[5],
conversion.coefficients[6], conversion.coefficients[7]);
}
static void GetCoefficient(Interface* self) {
@ -512,12 +522,11 @@ static void GetCoefficient(Interface* self) {
}
static void SetStandardCoefficient(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x20, 1, 0);
u32 index = rp.Pop<u32>();
u32 index = cmd_buff[1];
cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0);
cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)index).raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(conversion.SetStandardCoefficient(static_cast<StandardCoefficient>(index)));
LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index);
}
@ -544,22 +553,21 @@ static void GetStandardCoefficient(Interface* self) {
}
static void SetAlpha(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x22, 1, 0);
conversion.alpha = rp.Pop<u32>();
conversion.alpha = cmd_buff[1];
cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha);
}
static void GetAlpha(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x23, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = conversion.alpha;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(conversion.alpha);
LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha);
}
@ -584,7 +592,7 @@ static void GetDitheringWeightParams(Interface* self) {
}
static void StartConversion(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x26, 0, 0);
// dst_image_size would seem to be perfect for this, but it doesn't include the gap :(
u32 total_output_size =
@ -596,17 +604,17 @@ static void StartConversion(Interface* self) {
completion_event->Signal();
cmd_buff[0] = IPC::MakeHeader(0x26, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called");
}
static void StopConversion(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x27, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called");
}
@ -618,11 +626,11 @@ static void StopConversion(Interface* self) {
* 2 : 1 if there's a conversion running, otherwise 0.
*/
static void IsBusyConversion(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x28, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 0; // StartConversion always finishes immediately
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0); // StartConversion always finishes immediately
LOG_DEBUG(Service_Y2R, "called");
}
@ -631,59 +639,60 @@ static void IsBusyConversion(Interface* self) {
* Y2R_U::SetPackageParameter service function
*/
static void SetPackageParameter(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x29, 7, 0);
auto params = rp.PopRaw<ConversionParameters>();
auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]);
conversion.input_format = params.input_format;
conversion.output_format = params.output_format;
conversion.rotation = params.rotation;
conversion.block_alignment = params.block_alignment;
conversion.input_format = params->input_format;
conversion.output_format = params->output_format;
conversion.rotation = params->rotation;
conversion.block_alignment = params->block_alignment;
ResultCode result = conversion.SetInputLineWidth(params->input_line_width);
ResultCode result = conversion.SetInputLineWidth(params.input_line_width);
if (result.IsError())
goto cleanup;
result = conversion.SetInputLines(params->input_lines);
result = conversion.SetInputLines(params.input_lines);
if (result.IsError())
goto cleanup;
result = conversion.SetStandardCoefficient(params->standard_coefficient);
result = conversion.SetStandardCoefficient(params.standard_coefficient);
if (result.IsError())
goto cleanup;
conversion.padding = params->padding;
conversion.alpha = params->alpha;
conversion.padding = params.padding;
conversion.alpha = params.alpha;
cleanup:
cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0);
cmd_buff[1] = result.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(result);
LOG_DEBUG(
Service_Y2R,
"called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu "
"input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX",
static_cast<u8>(params->input_format), static_cast<u8>(params->output_format),
static_cast<u8>(params->rotation), static_cast<u8>(params->block_alignment),
params->input_line_width, params->input_lines,
static_cast<u8>(params->standard_coefficient), params->padding, params->alpha);
static_cast<u8>(params.input_format), static_cast<u8>(params.output_format),
static_cast<u8>(params.rotation), static_cast<u8>(params.block_alignment),
params.input_line_width, params.input_lines, static_cast<u8>(params.standard_coefficient),
params.padding, params.alpha);
}
static void PingProcess(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2A, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 0;
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
LOG_WARNING(Service_Y2R, "(STUBBED) called");
}
static void DriverInitialize(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2B, 0, 0);
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
conversion.input_format = InputFormat::YUV422_Indiv8;
conversion.output_format = OutputFormat::RGBA8;
@ -702,17 +711,16 @@ static void DriverInitialize(Interface* self) {
completion_event->Clear();
cmd_buff[0] = IPC::MakeHeader(0x2B, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called");
}
static void DriverFinalize(Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2C, 0, 0);
cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0);
cmd_buff[1] = RESULT_SUCCESS.raw;
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_Y2R, "called");
}
@ -787,4 +795,4 @@ Y2R_U::~Y2R_U() {
}
} // namespace Y2R
} // namespace Service
} // namespace Service

View file

@ -8,7 +8,9 @@
#include "common/assert.h"
#include "common/file_util.h"
#include "common/scm_rev.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#endif
#include "core/core.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
@ -20,6 +22,7 @@
namespace Core {
#ifdef ARCHITECTURE_x86_64
static const char* CpuVendorToStr(Common::CPUVendor vendor) {
switch (vendor) {
case Common::CPUVendor::INTEL:
@ -31,6 +34,7 @@ static const char* CpuVendorToStr(Common::CPUVendor vendor) {
}
UNREACHABLE();
}
#endif
static u64 GenerateTelemetryId() {
u64 telemetry_id{};
@ -121,7 +125,8 @@ TelemetrySession::TelemetrySession() {
AddField(Telemetry::FieldType::App, "BuildDate", Common::g_build_date);
AddField(Telemetry::FieldType::App, "BuildName", Common::g_build_name);
// Log user system information
// Log user system information
#ifdef ARCHITECTURE_x86_64
AddField(Telemetry::FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string);
AddField(Telemetry::FieldType::UserSystem, "CPU_BrandString",
Common::GetCPUCaps().brand_string);
@ -143,6 +148,9 @@ TelemetrySession::TelemetrySession() {
Common::GetCPUCaps().sse4_1);
AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE42",
Common::GetCPUCaps().sse4_2);
#else
AddField(Telemetry::FieldType::UserSystem, "CPU_Model", "Other");
#endif
#ifdef __APPLE__
AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Apple");
#elif defined(_WIN32)

View file

@ -8,7 +8,7 @@ add_library(input_common STATIC
motion_emu.cpp
motion_emu.h
$<$<BOOL:SDL2_FOUND>:sdl/sdl.cpp sdl/sdl.h>
$<$<BOOL:${SDL2_FOUND}>:sdl/sdl.cpp sdl/sdl.h>
)
create_target_directory_groups(input_common)

View file

@ -17,11 +17,12 @@ namespace InputCommon {
// Implementation class of the motion emulation device
class MotionEmuDevice {
public:
MotionEmuDevice(int update_millisecond, float sensitivity)
MotionEmuDevice(int update_millisecond, float sensitivity, float tilt_clamp)
: update_millisecond(update_millisecond),
update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::milliseconds(update_millisecond))),
sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
sensitivity(sensitivity), tilt_clamp(tilt_clamp),
motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
~MotionEmuDevice() {
if (motion_emu_thread.joinable()) {
@ -44,7 +45,7 @@ public:
} else {
tilt_direction = mouse_move.Cast<float>();
tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * sensitivity, 0.0f,
MathUtil::PI * 0.5f);
MathUtil::PI * this->tilt_clamp / 180.0f);
}
}
}
@ -70,6 +71,7 @@ private:
std::mutex tilt_mutex;
Math::Vec2<float> tilt_direction;
float tilt_angle = 0;
float tilt_clamp = 90;
bool is_tilting = false;
@ -126,8 +128,8 @@ private:
// can forward all the inputs to the implementation only when it is valid.
class MotionEmuDeviceWrapper : public Input::MotionDevice {
public:
MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
MotionEmuDeviceWrapper(int update_millisecond, float sensitivity, float tilt_clamp) {
device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity, tilt_clamp);
}
std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const {
@ -140,7 +142,9 @@ public:
std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) {
int update_period = params.Get("update_period", 100);
float sensitivity = params.Get("sensitivity", 0.01f);
auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity);
float tilt_clamp = params.Get("tilt_clamp", 90.0f);
auto device_wrapper =
std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity, tilt_clamp);
// Previously created device is disconnected here. Having two motion devices for 3DS is not
// expected.
current_device = device_wrapper->device;

View file

@ -221,6 +221,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
MICROPROFILE_SCOPE(GPU_Drawing);
immediate_attribute_id = 0;
Shader::OutputVertex::ValidateSemantics(regs.rasterizer);
auto* shader_engine = Shader::GetEngine();
shader_engine->SetupBatch(g_state.vs, regs.vs.main_offset);
@ -289,6 +291,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// Later, these can be compiled and cached.
const u32 base_address = regs.pipeline.vertex_attributes.GetPhysicalBaseAddress();
VertexLoader loader(regs.pipeline);
Shader::OutputVertex::ValidateSemantics(regs.rasterizer);
// Load vertices
bool is_indexed = (id == PICA_REG_INDEX(pipeline.trigger_draw_indexed));

View file

@ -87,6 +87,8 @@ struct RasterizerRegs {
BitField<8, 5, Semantic> map_y;
BitField<16, 5, Semantic> map_z;
BitField<24, 5, Semantic> map_w;
u32 raw;
} vs_output_attributes[7];
INSERT_PADDING_WORDS(0xe);

View file

@ -65,6 +65,7 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {
PicaShaderConfig res;
auto& state = res.state;
// Memset structure to zero padding bits, so that they will be deterministic when hashing
std::memset(&state, 0, sizeof(PicaShaderConfig::State));
state.scissor_test_mode = regs.rasterizer.scissor_test.mode;

View file

@ -131,10 +131,6 @@ union PicaShaderConfig {
} state;
};
#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
static_assert(std::is_trivially_copyable<PicaShaderConfig::State>::value,
"PicaShaderConfig::State must be trivially copyable");
#endif
/**
* Generates the GLSL vertex shader program source code for the current Pica state
@ -156,7 +152,7 @@ namespace std {
template <>
struct hash<GLShader::PicaShaderConfig> {
size_t operator()(const GLShader::PicaShaderConfig& k) const {
return Common::ComputeHash64(&k.state, sizeof(GLShader::PicaShaderConfig::State));
return Common::ComputeStructHash64(k.state);
}
};
} // namespace std

View file

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cinttypes>
#include <cmath>
#include <cstring>
#include "common/bit_set.h"
@ -21,32 +22,41 @@ namespace Pica {
namespace Shader {
void OutputVertex::ValidateSemantics(const RasterizerRegs& regs) {
unsigned int num_attributes = regs.vs_output_total;
ASSERT(num_attributes <= 7);
for (size_t attrib = 0; attrib < num_attributes; ++attrib) {
u32 output_register_map = regs.vs_output_attributes[attrib].raw;
for (size_t comp = 0; comp < 4; ++comp) {
u32 semantic = (output_register_map >> (8 * comp)) & 0x1F;
ASSERT_MSG(semantic < 24 || semantic == RasterizerRegs::VSOutputAttributes::INVALID,
"Invalid/unknown semantic id: %" PRIu32, semantic);
}
}
}
OutputVertex OutputVertex::FromAttributeBuffer(const RasterizerRegs& regs,
const AttributeBuffer& input) {
// Setup output data
union {
OutputVertex ret{};
std::array<float24, 24> vertex_slots;
// Allow us to overflow OutputVertex to avoid branches, since
// RasterizerRegs::VSOutputAttributes::INVALID would write to slot 31, which
// would be out of bounds otherwise.
std::array<float24, 32> vertex_slots_overflow;
};
static_assert(sizeof(vertex_slots) == sizeof(ret), "Struct and array have different sizes.");
unsigned int num_attributes = regs.vs_output_total;
ASSERT(num_attributes <= 7);
for (unsigned int i = 0; i < num_attributes; ++i) {
const auto& output_register_map = regs.vs_output_attributes[i];
// Assert that OutputVertex has enough space for 24 semantic registers
static_assert(sizeof(std::array<float24, 24>) == sizeof(ret),
"Struct and array have different sizes.");
RasterizerRegs::VSOutputAttributes::Semantic semantics[4] = {
output_register_map.map_x, output_register_map.map_y, output_register_map.map_z,
output_register_map.map_w};
for (unsigned comp = 0; comp < 4; ++comp) {
RasterizerRegs::VSOutputAttributes::Semantic semantic = semantics[comp];
if (semantic < vertex_slots.size()) {
vertex_slots[semantic] = input.attr[i][comp];
} else if (semantic != RasterizerRegs::VSOutputAttributes::INVALID) {
LOG_ERROR(HW_GPU, "Invalid/unknown semantic id: %u", (unsigned int)semantic);
}
}
unsigned int num_attributes = regs.vs_output_total & 7;
for (size_t attrib = 0; attrib < num_attributes; ++attrib) {
const auto output_register_map = regs.vs_output_attributes[attrib];
vertex_slots_overflow[output_register_map.map_x] = input.attr[attrib][0];
vertex_slots_overflow[output_register_map.map_y] = input.attr[attrib][1];
vertex_slots_overflow[output_register_map.map_z] = input.attr[attrib][2];
vertex_slots_overflow[output_register_map.map_w] = input.attr[attrib][3];
}
// The hardware takes the absolute and saturates vertex colors like this, *before* doing

View file

@ -50,6 +50,7 @@ struct OutputVertex {
INSERT_PADDING_WORDS(1);
Math::Vec2<float24> tc2;
static void ValidateSemantics(const RasterizerRegs& regs);
static OutputVertex FromAttributeBuffer(const RasterizerRegs& regs,
const AttributeBuffer& output);
};