From 70523f68c97a0f49228c5ef65951465f0ec20718 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Mon, 23 Oct 2017 22:16:36 +0100 Subject: [PATCH 01/42] appveyor: Determine dlls to include in package programmatically This is done by recursively calling bindump to determine the dependencies of each binary that is required from each of the executables. Doing this allows us to avoid hard-coding a list of required DLL files to copy into the release archives. --- .appveyor/UtilityFunctions.ps1 | 39 ++++++++++++++++++++++++++++++++++ appveyor.yml | 23 +++++++------------- 2 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 .appveyor/UtilityFunctions.ps1 diff --git a/.appveyor/UtilityFunctions.ps1 b/.appveyor/UtilityFunctions.ps1 new file mode 100644 index 000000000..fd7476314 --- /dev/null +++ b/.appveyor/UtilityFunctions.ps1 @@ -0,0 +1,39 @@ +# Set-up Visual Studio Command Prompt environment for PowerShell +pushd "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\" +cmd /c "VsDevCmd.bat -arch=x64 & set" | foreach { + if ($_ -match "=") { + $v = $_.split("="); Set-Item -Force -Path "ENV:\$($v[0])" -Value "$($v[1])" + } +} +popd + +function Which ($search_path, $name) { + ($search_path).Split(";") | Get-ChildItem -Filter $name | Select -First 1 -Exp FullName +} + +function GetDeps ($search_path, $binary) { + ((dumpbin /dependents $binary).Where({ $_ -match "dependencies:"}, "SkipUntil") | Select-String "[^ ]*\.dll").Matches | foreach { + Which $search_path $_.Value + } +} + +function RecursivelyGetDeps ($search_path, $binary) { + $final_deps = @() + $deps_to_process = GetDeps $search_path $binary + while ($deps_to_process.Count -gt 0) { + $current, $deps_to_process = $deps_to_process + if ($final_deps -contains $current) { continue } + + # Is this a system dll file? + # We use the same algorithm that cmake uses to determine this. + if ($current -match "$([regex]::Escape($env:SystemRoot))\\sys") { continue } + if ($current -match "$([regex]::Escape($env:WinDir))\\sys") { continue } + if ($current -match "\\msvc[^\\]+dll") { continue } + if ($current -match "\\api-ms-win-[^\\]+dll") { continue } + + $final_deps += $current + $new_deps = GetDeps $search_path $current + $deps_to_process += ($new_deps | ?{-not ($final_deps -contains $_)}) + } + return $final_deps +} diff --git a/appveyor.yml b/appveyor.yml index 5f6b35847..26588e56b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -116,23 +116,16 @@ after_build: Get-ChildItem "$CMAKE_BINARY_DIR" -Recurse -Filter "libcurl.dll" | Copy-Item -destination $RELEASE_DIST Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST + # copy all the dll dependencies to the release folder - # hardcoded list because we don't build static and determining the list of dlls from the binary is a pain. - $MingwDLLs = "Qt5Core.dll","Qt5Widgets.dll","Qt5Gui.dll","Qt5OpenGL.dll", - # QT dll dependencies - "libbz2-*.dll","libicudt*.dll","libicuin*.dll","libicuuc*.dll","libffi-*.dll", - "libfreetype-*.dll","libglib-*.dll","libgobject-*.dll","libgraphite2.dll","libiconv-*.dll", - "libharfbuzz-*.dll","libintl-*.dll","libpcre-*.dll","libpcre16-*.dll","libpcre2-16-*.dll","libpng16-*.dll", - # Runtime/Other dependencies - "libgcc_s_seh-*.dll","libstdc++-*.dll","libwinpthread-*.dll","SDL2.dll","zlib1.dll" + . "./.appveyor/UtilityFunctions.ps1" + $DLLSearchPath = "$CMAKE_BINARY_DIR\externals\curl-7_55_1\lib;C:\msys64\mingw64\bin;$env:PATH" + $MingwDLLs = RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\citra.exe" + $MingwDLLs += RecursivelyGetDeps $DLLSearchPath "$RELEASE_DIST\citra-qt.exe" + Write-Host "Detected the following dependencies:" + Write-Host $MingwDLLs foreach ($file in $MingwDLLs) { - Copy-Item -path "C:/msys64/mingw64/bin/$file" -force -destination "$RELEASE_DIST" - } - # the above list copies a few extra debug dlls that aren't needed (thanks globbing patterns!) - # so we can remove them by hardcoding another list of extra dlls to remove - $DebugDLLs = "libicudtd*.dll","libicuind*.dll","libicuucd*.dll" - foreach ($file in $DebugDLLs) { - Remove-Item -path "$RELEASE_DIST/$file" + Copy-Item -path "$file" -force -destination "$RELEASE_DIST" } # copy the qt windows plugin dll to platforms From 1856aa7b32d91360298cd078632737e9608764b0 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 15 Dec 2017 10:58:09 -0500 Subject: [PATCH 02/42] Kernel/SVC: Partially implemented svcExitProcess. Terminating processes with ready threads is not currently implemented and will assert. It is currently unknown how the 3DS kernel stops ready threads or threads running in another core. --- src/core/hle/kernel/process.cpp | 3 +++ src/core/hle/kernel/process.h | 4 ++++ src/core/hle/kernel/svc.cpp | 35 ++++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 478680550..a66a4bc16 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -40,6 +40,7 @@ SharedPtr Process::Create(SharedPtr 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); } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 461391fd3..a21b8e704 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -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++; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 06c905284..cb5dbad5d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -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, @@ -1232,7 +1262,7 @@ static const FunctionDef SVC_Table[] = { {0x00, nullptr, "Unknown"}, {0x01, HLE::Wrap, "ControlMemory"}, {0x02, HLE::Wrap, "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 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) { From 62890b253ea59b5c49194775dabc31c8b5956e4d Mon Sep 17 00:00:00 2001 From: wwylele Date: Sat, 23 Sep 2017 16:13:59 +0300 Subject: [PATCH 03/42] citra_qt: load translation from resource --- src/citra_qt/configuration/config.cpp | 2 + .../configuration/configure_audio.cpp | 4 ++ src/citra_qt/configuration/configure_audio.h | 1 + .../configuration/configure_debug.cpp | 4 ++ src/citra_qt/configuration/configure_debug.h | 1 + .../configuration/configure_dialog.cpp | 14 ++++++ src/citra_qt/configuration/configure_dialog.h | 6 +++ .../configuration/configure_general.cpp | 31 ++++++++++++ .../configuration/configure_general.h | 7 +++ .../configuration/configure_general.ui | 18 ++++++- .../configuration/configure_graphics.cpp | 4 ++ .../configuration/configure_graphics.h | 1 + .../configuration/configure_input.cpp | 4 ++ src/citra_qt/configuration/configure_input.h | 1 + .../configuration/configure_system.cpp | 4 ++ src/citra_qt/configuration/configure_system.h | 1 + src/citra_qt/configuration/configure_web.cpp | 4 ++ src/citra_qt/configuration/configure_web.h | 1 + src/citra_qt/main.cpp | 47 ++++++++++++++++++- src/citra_qt/main.h | 6 +++ src/citra_qt/ui_settings.h | 1 + 21 files changed, 158 insertions(+), 4 deletions(-) diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 97993e426..0cb4083aa 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -182,6 +182,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 +334,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"); diff --git a/src/citra_qt/configuration/configure_audio.cpp b/src/citra_qt/configuration/configure_audio.cpp index 3fd1d127a..34b6bdef5 100644 --- a/src/citra_qt/configuration/configure_audio.cpp +++ b/src/citra_qt/configuration/configure_audio.cpp @@ -75,3 +75,7 @@ void ConfigureAudio::updateAudioDevices(int sink_index) { ui->audio_device_combo_box->addItem(device.c_str()); } } + +void ConfigureAudio::retranslateUi() { + ui->retranslateUi(this); +} diff --git a/src/citra_qt/configuration/configure_audio.h b/src/citra_qt/configuration/configure_audio.h index 8190e694f..d4790db5c 100644 --- a/src/citra_qt/configuration/configure_audio.h +++ b/src/citra_qt/configuration/configure_audio.h @@ -19,6 +19,7 @@ public: ~ConfigureAudio(); void applyConfiguration(); + void retranslateUi(); public slots: void updateAudioDevices(int sink_index); diff --git a/src/citra_qt/configuration/configure_debug.cpp b/src/citra_qt/configuration/configure_debug.cpp index 263f73f38..48f57739e 100644 --- a/src/citra_qt/configuration/configure_debug.cpp +++ b/src/citra_qt/configuration/configure_debug.cpp @@ -24,3 +24,7 @@ void ConfigureDebug::applyConfiguration() { Settings::values.gdbstub_port = ui->gdbport_spinbox->value(); Settings::Apply(); } + +void ConfigureDebug::retranslateUi() { + ui->retranslateUi(this); +} diff --git a/src/citra_qt/configuration/configure_debug.h b/src/citra_qt/configuration/configure_debug.h index d167eb996..fc95c91ca 100644 --- a/src/citra_qt/configuration/configure_debug.h +++ b/src/citra_qt/configuration/configure_debug.h @@ -19,6 +19,7 @@ public: ~ConfigureDebug(); void applyConfiguration(); + void retranslateUi(); private: void setConfiguration(); diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp index b87dc0e6c..ca468bf81 100644 --- a/src/citra_qt/configuration/configure_dialog.cpp +++ b/src/citra_qt/configuration/configure_dialog.cpp @@ -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(); +} diff --git a/src/citra_qt/configuration/configure_dialog.h b/src/citra_qt/configuration/configure_dialog.h index 21fa1f501..481e30154 100644 --- a/src/citra_qt/configuration/configure_dialog.h +++ b/src/citra_qt/configuration/configure_dialog.h @@ -20,6 +20,12 @@ public: void applyConfiguration(); +private slots: + void onLanguageChanged(const QString& locale); + +signals: + void languageChanged(const QString& locale); + private: void setConfiguration(); diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp index f8f6c305d..ce8da9467 100644 --- a/src/citra_qt/configuration/configure_general.cpp +++ b/src/citra_qt/configuration/configure_general.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #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(""), 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, interface language change need to be reflect 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(&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); +} diff --git a/src/citra_qt/configuration/configure_general.h b/src/citra_qt/configuration/configure_general.h index 447552d8c..55fd93890 100644 --- a/src/citra_qt/configuration/configure_general.h +++ b/src/citra_qt/configuration/configure_general.h @@ -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(); diff --git a/src/citra_qt/configuration/configure_general.ui b/src/citra_qt/configuration/configure_general.ui index 00e5b49f5..c2bf24b52 100644 --- a/src/citra_qt/configuration/configure_general.ui +++ b/src/citra_qt/configuration/configure_general.ui @@ -6,8 +6,8 @@ 0 0 - 300 - 377 + 345 + 493 @@ -38,6 +38,20 @@ + + + + + + Interface language + + + + + + + + diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index b5a5ab1e1..d1546f3aa 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -113,3 +113,7 @@ void ConfigureGraphics::applyConfiguration() { Settings::values.swap_screen = ui->swap_screen->isChecked(); Settings::Apply(); } + +void ConfigureGraphics::retranslateUi() { + ui->retranslateUi(this); +} diff --git a/src/citra_qt/configuration/configure_graphics.h b/src/citra_qt/configuration/configure_graphics.h index 5497a55f7..36bdef890 100644 --- a/src/citra_qt/configuration/configure_graphics.h +++ b/src/citra_qt/configuration/configure_graphics.h @@ -19,6 +19,7 @@ public: ~ConfigureGraphics(); void applyConfiguration(); + void retranslateUi(); private: void setConfiguration(); diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp index 5a7d269cd..64a22cb03 100644 --- a/src/citra_qt/configuration/configure_input.cpp +++ b/src/citra_qt/configuration/configure_input.cpp @@ -277,3 +277,7 @@ void ConfigureInput::keyPressEvent(QKeyEvent* event) { } setPollingResult({}, true); } + +void ConfigureInput::retranslateUi() { + ui->retranslateUi(this); +} diff --git a/src/citra_qt/configuration/configure_input.h b/src/citra_qt/configuration/configure_input.h index a0bef86d5..2efebf20d 100644 --- a/src/citra_qt/configuration/configure_input.h +++ b/src/citra_qt/configuration/configure_input.h @@ -33,6 +33,7 @@ public: /// Save all button configurations to settings file void applyConfiguration(); + void retranslateUi(); private: std::unique_ptr ui; diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index 88a067c12..6793fb50d 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -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); +} diff --git a/src/citra_qt/configuration/configure_system.h b/src/citra_qt/configuration/configure_system.h index f13de17d4..094887791 100644 --- a/src/citra_qt/configuration/configure_system.h +++ b/src/citra_qt/configuration/configure_system.h @@ -20,6 +20,7 @@ public: void applyConfiguration(); void setConfiguration(); + void retranslateUi(); public slots: void updateBirthdayComboBox(int birthmonth_index); diff --git a/src/citra_qt/configuration/configure_web.cpp b/src/citra_qt/configuration/configure_web.cpp index bf8c21ac7..8bfbb94c3 100644 --- a/src/citra_qt/configuration/configure_web.cpp +++ b/src/citra_qt/configuration/configure_web.cpp @@ -100,3 +100,7 @@ void ConfigureWeb::OnLoginVerified() { "correctly, and that your internet connection is working.")); } } + +void ConfigureWeb::retranslateUi() { + ui->retranslateUi(this); +} diff --git a/src/citra_qt/configuration/configure_web.h b/src/citra_qt/configuration/configure_web.h index ad2d58f6e..b8e71ffdd 100644 --- a/src/citra_qt/configuration/configure_web.h +++ b/src/citra_qt/configuration/configure_web.h @@ -20,6 +20,7 @@ public: ~ConfigureWeb(); void applyConfiguration(); + void retranslateUi(); public slots: void RefreshTelemetryID(); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index af7b8cf7b..4b7d4cc0a 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -97,6 +97,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { // register size_t to use in slots and signals qRegisterMetaType("size_t"); + LoadTranslation(); + Pica::g_debug_context = Pica::DebugContext::Construct(); setAcceptDrops(true); ui.setupUi(this); @@ -114,8 +116,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); @@ -871,6 +873,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(); @@ -1084,6 +1088,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 diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 70fb309e7..b0bceaac5 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "core/core.h" #include "core/hle/service/am/am.h" #include "ui_main.h" @@ -151,9 +152,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; @@ -192,6 +196,8 @@ private: QAction* actions_recent_files[max_recent_files_item]; + QTranslator translator; + protected: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; diff --git a/src/citra_qt/ui_settings.h b/src/citra_qt/ui_settings.h index dba4c5d3c..caf6aea6a 100644 --- a/src/citra_qt/ui_settings.h +++ b/src/citra_qt/ui_settings.h @@ -48,6 +48,7 @@ struct Values { QString gamedir; bool gamedir_deepscan; QStringList recent_files; + QString language; QString theme; From 500a7f07c22e6d506692d30eddcd65b34a7cb471 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sat, 23 Sep 2017 16:14:20 +0300 Subject: [PATCH 04/42] cmake: generate translation file and add to resource for citra_qt --- CMakeLists.txt | 6 ++++++ dist/languages/.gitignore | 3 +++ dist/languages/.tx/config | 9 +++++++++ dist/languages/README.md | 1 + src/citra_qt/CMakeLists.txt | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 53 insertions(+) create mode 100644 dist/languages/.gitignore create mode 100644 dist/languages/.tx/config create mode 100644 dist/languages/README.md diff --git a/CMakeLists.txt b/CMakeLists.txt index b7c7c48ab..c67c129d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,10 +10,12 @@ option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) option(CITRA_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" OFF) option(ENABLE_QT "Enable the Qt frontend" ON) +option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF) option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(CITRA_USE_BUNDLED_CURL "FOR MINGW ONLY: Download curl configured against winssl instead of openssl" OFF) + if (ENABLE_WEB_SERVICE AND CITRA_USE_BUNDLED_CURL AND WINDOWS AND MSVC) message("Turning off use bundled curl as msvc can compile curl on cpr") SET(CITRA_USE_BUNDLED_CURL OFF CACHE BOOL "" FORCE) @@ -230,6 +232,10 @@ if (ENABLE_QT) endif() find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) + + if (ENABLE_QT_TRANSLATION) + find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) + endif() endif() if (ENABLE_WEB_SERVICE) diff --git a/dist/languages/.gitignore b/dist/languages/.gitignore new file mode 100644 index 000000000..8fc7e6a17 --- /dev/null +++ b/dist/languages/.gitignore @@ -0,0 +1,3 @@ +# Ignore the source language file +en.ts + diff --git a/dist/languages/.tx/config b/dist/languages/.tx/config new file mode 100644 index 000000000..84d8896b4 --- /dev/null +++ b/dist/languages/.tx/config @@ -0,0 +1,9 @@ +[main] +host = https://www.transifex.com + +[citra.emulator] +file_filter = .ts +source_file = en.ts +source_lang = en +type = QT + diff --git a/dist/languages/README.md b/dist/languages/README.md new file mode 100644 index 000000000..2156fd386 --- /dev/null +++ b/dist/languages/README.md @@ -0,0 +1 @@ +This directory stores translation patches (TS files) for citra Qt frontend. This directory is linked with [citra project on transifex](https://www.transifex.com/citra/citra), so you can update the translation by executing `tx pull -a`. If you want to contribute to the translation, please go the transifex link and submit your translation there. This directory on the main repo will be synchronized with transifex periodically. Do not directly open PRs on github to modify the translation. diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 9da384620..ac472ad5c 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -87,12 +87,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} "\n") + foreach (QM ${LANGUAGES_QM}) + get_filename_component(QM_FILE ${QM} NAME) + file(APPEND ${LANGUAGES_QRC} "${QM_FILE}\n") + endforeach (QM) + file(APPEND ${LANGUAGES_QRC} "") + + # 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) From 9fba503c9ebcdf405a89652f841bd65864bed59c Mon Sep 17 00:00:00 2001 From: wwylele Date: Fri, 15 Dec 2017 10:06:52 +0200 Subject: [PATCH 05/42] Travis: push translation source change to transifex --- .travis.yml | 16 ++++++++++++++++ .travis/transifex/build.sh | 6 ++++++ .travis/transifex/deps.sh | 4 ++++ .travis/transifex/upload.sh | 5 +++++ 4 files changed, 31 insertions(+) create mode 100755 .travis/transifex/build.sh create mode 100755 .travis/transifex/deps.sh create mode 100755 .travis/transifex/upload.sh diff --git a/.travis.yml b/.travis.yml index 9624d4483..111d89528 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,22 @@ matrix: install: "./.travis/linux/deps.sh" script: "./.travis/linux/build.sh" after_success: "./.travis/linux/upload.sh" + - if: branch = master AND type = push + os: linux + env: NAME="transifex push" + sudo: required + dist: trusty + addons: + apt: + packages: + - libsdl2-dev + - qtbase5-dev + - libqt5opengl5-dev + - qttools5-dev + - qttools5-dev-tools + install: "./.travis/transifex/deps.sh" + script: "./.travis/transifex/build.sh" + after_success: "./.travis/transifex/upload.sh" - os: osx env: NAME="macos build" sudo: false diff --git a/.travis/transifex/build.sh b/.travis/transifex/build.sh new file mode 100755 index 000000000..2e9822360 --- /dev/null +++ b/.travis/transifex/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash -ex + +mkdir build && cd build +cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release +make translation + diff --git a/.travis/transifex/deps.sh b/.travis/transifex/deps.sh new file mode 100755 index 000000000..3fa6b1109 --- /dev/null +++ b/.travis/transifex/deps.sh @@ -0,0 +1,4 @@ +#!/bin/bash -ex + +sudo pip install transifex-client +echo $'[https://www.transifex.com]\nhostname = https://www.transifex.com\nusername = api\npassword = '"$TRANSIFEX_API_TOKEN"$'\n' > ~/.transifexrc diff --git a/.travis/transifex/upload.sh b/.travis/transifex/upload.sh new file mode 100755 index 000000000..f51847850 --- /dev/null +++ b/.travis/transifex/upload.sh @@ -0,0 +1,5 @@ +#!/bin/bash -ex + +cd dist/languages +tx push -s + From 798a1c6ae97a2554175ca5e017cd14d8db0da831 Mon Sep 17 00:00:00 2001 From: wwylele Date: Fri, 15 Dec 2017 10:07:31 +0200 Subject: [PATCH 06/42] Travis, Appveyor: build with languages --- .travis/linux/docker.sh | 4 ++-- .travis/macos/build.sh | 2 +- appveyor.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis/linux/docker.sh b/.travis/linux/docker.sh index e27af5600..ac8940719 100755 --- a/.travis/linux/docker.sh +++ b/.travis/linux/docker.sh @@ -3,7 +3,7 @@ cd /citra apt-get update -apt-get install -y build-essential libsdl2-dev qtbase5-dev libqt5opengl5-dev libcurl4-openssl-dev libssl-dev wget git +apt-get install -y build-essential libsdl2-dev qtbase5-dev libqt5opengl5-dev qttools5-dev qttools5-dev-tools libcurl4-openssl-dev libssl-dev wget git # Get a recent version of CMake wget https://cmake.org/files/v3.9/cmake-3.9.0-Linux-x86_64.sh @@ -11,7 +11,7 @@ echo y | sh cmake-3.9.0-Linux-x86_64.sh --prefix=cmake export PATH=/citra/cmake/cmake-3.9.0-Linux-x86_64/bin:$PATH mkdir build && cd build -cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_BUILD_TYPE=Release +cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON make -j4 ctest -VV -C Release diff --git a/.travis/macos/build.sh b/.travis/macos/build.sh index c957cdf21..37e84f6e8 100755 --- a/.travis/macos/build.sh +++ b/.travis/macos/build.sh @@ -6,7 +6,7 @@ export MACOSX_DEPLOYMENT_TARGET=10.9 export Qt5_DIR=$(brew --prefix)/opt/qt5 mkdir build && cd build -cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h" -DCMAKE_BUILD_TYPE=Release +cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h" -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON make -j4 ctest -VV -C Release diff --git a/appveyor.yml b/appveyor.yml index 5fd7c6490..7eed440bc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -45,7 +45,7 @@ before_build: # redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DCITRA_USE_BUNDLED_QT=1 -DCITRA_USE_BUNDLED_SDL2=1 -DCMAKE_USE_OPENSSL=0 .. 2>&1 && exit 0' } else { - C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DUSE_SYSTEM_CURL=1 -DCITRA_USE_BUNDLED_CURL=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1" + C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DUSE_SYSTEM_CURL=1 -DCITRA_USE_BUNDLED_CURL=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON .. 2>&1" } - cd .. From 5345d2e9574e9e1e9a6e9c402e0594b00b4a591e Mon Sep 17 00:00:00 2001 From: wwylele Date: Fri, 22 Dec 2017 12:34:49 +0200 Subject: [PATCH 07/42] fixup!citra_qt: load translation from resource --- src/citra_qt/configuration/configure_general.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp index ce8da9467..ad008a011 100644 --- a/src/citra_qt/configuration/configure_general.cpp +++ b/src/citra_qt/configuration/configure_general.cpp @@ -24,9 +24,9 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) ui->language_combobox->addItem(lang, locale); } - // Unlike other configuration, interface language change need to be reflect on the interface - // immediately. This is done by passing a signal to the main window, and then retranslating when - // passing back. + // 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(&QComboBox::currentIndexChanged), this, &ConfigureGeneral::onLanguageChanged); From bf23f8d5426f3ff1dc88eb3e59544154daa98a28 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sat, 16 Dec 2017 16:55:19 +0200 Subject: [PATCH 08/42] HID: convert to ServiceFramework --- src/core/hle/service/hid/hid.cpp | 207 ++++++++++-------------- src/core/hle/service/hid/hid.h | 219 ++++++++++++++++---------- src/core/hle/service/hid/hid_spvr.cpp | 31 ++-- src/core/hle/service/hid/hid_spvr.h | 12 +- src/core/hle/service/hid/hid_user.cpp | 31 ++-- src/core/hle/service/hid/hid_user.h | 15 +- src/core/hle/service/service.cpp | 3 +- 7 files changed, 260 insertions(+), 258 deletions(-) diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 9074cd2b0..1d553864b 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -3,15 +3,12 @@ // Refer to the license.txt file included. #include -#include #include -#include #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 shared_mem; - -// Event handles -static Kernel::SharedPtr event_pad_or_touch_1; -static Kernel::SharedPtr event_pad_or_touch_2; -static Kernel::SharedPtr event_accelerometer; -static Kernel::SharedPtr event_gyroscope; -static Kernel::SharedPtr 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 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 is_device_reload_pending; -static std::array, Settings::NativeButton::NUM_BUTTONS_HID> - buttons; -static std::unique_ptr circle_pad; -static std::unique_ptr motion_device; -static std::unique_ptr 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); @@ -99,16 +69,7 @@ static void LoadInputDevices() { touch_device = Input::CreateDevice(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(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(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(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(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], ¶m, 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 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(); + std::make_shared(hid)->InstallAsService(service_manager); + std::make_shared(hid)->InstallAsService(service_manager); + current_module = hid; } } // namespace HID diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index ef25926b5..1518111d3 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -5,17 +5,29 @@ #pragma once #include +#include #ifndef _MSC_VER #include #endif +#include #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 { + public: + Interface(std::shared_ptr 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 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 shared_mem; + + // Event handles + Kernel::SharedPtr event_pad_or_touch_1; + Kernel::SharedPtr event_pad_or_touch_2; + Kernel::SharedPtr event_accelerometer; + Kernel::SharedPtr event_gyroscope; + Kernel::SharedPtr 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 is_device_reload_pending{true}; + std::array, Settings::NativeButton::NUM_BUTTONS_HID> + buttons; + std::unique_ptr circle_pad; + std::unique_ptr motion_device; + std::unique_ptr touch_device; +}; + +void InstallInterfaces(SM::ServiceManager& service_manager); /// Reload input devices. Used when input configuration changed void ReloadInputDevices(); diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp index 09007e304..3d5900fd9 100644 --- a/src/core/hle/service/hid/hid_spvr.cpp +++ b/src/core/hle/service/hid/hid_spvr.cpp @@ -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 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 diff --git a/src/core/hle/service/hid/hid_spvr.h b/src/core/hle/service/hid/hid_spvr.h index ba61583d2..c0d831146 100644 --- a/src/core/hle/service/hid/hid_spvr.h +++ b/src/core/hle/service/hid/hid_spvr.h @@ -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 hid); }; } // namespace HID -} // namespace Service \ No newline at end of file +} // namespace Service diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp index 42591543c..88213dab2 100644 --- a/src/core/hle/service/hid/hid_user.cpp +++ b/src/core/hle/service/hid/hid_user.cpp @@ -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 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 diff --git a/src/core/hle/service/hid/hid_user.h b/src/core/hle/service/hid/hid_user.h index baf7fed79..8e4ca52bb 100644 --- a/src/core/hle/service/hid/hid_user.h +++ b/src/core/hle/service/hid/hid_user.h @@ -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 hid); }; } // namespace HID -} // namespace Service \ No newline at end of file +} // namespace Service diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 600628234..8794c84db 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -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(); From 80ed0f03acb77d7592d28d289c1c1994d35fbe7c Mon Sep 17 00:00:00 2001 From: wwylele Date: Sat, 23 Dec 2017 03:00:15 +0200 Subject: [PATCH 09/42] cmake: add missing ${...} for variables inside generator expressions --- src/audio_core/CMakeLists.txt | 2 +- src/input_common/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 2a80ebbfe..ec89b7a4c 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -23,7 +23,7 @@ add_library(audio_core STATIC time_stretch.cpp time_stretch.h - $<$:sdl2_sink.cpp sdl2_sink.h> + $<$:sdl2_sink.cpp sdl2_sink.h> ) create_target_directory_groups(audio_core) diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index e38d83158..1c7db28c0 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -8,7 +8,7 @@ add_library(input_common STATIC motion_emu.cpp motion_emu.h - $<$:sdl/sdl.cpp sdl/sdl.h> + $<$:sdl/sdl.cpp sdl/sdl.h> ) create_target_directory_groups(input_common) From aabb07cca45f936a0acc159e0632e0395f1faf85 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 21 Dec 2017 11:24:12 -0500 Subject: [PATCH 10/42] HLE/GSP: Make RegisterInterruptRelayQueue work in a per-session basis. The registered interrupt event is unique to each session that calls RegisterInterruptRelayQueue, and only that event should be reset when UnregisterInterruptRelayQueue is called. --- src/core/hle/service/gsp/gsp_gpu.cpp | 55 +++++++++++++++++++--------- src/core/hle/service/gsp/gsp_gpu.h | 25 +++++++++---- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index bb3cedbe3..4e854164e 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -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" @@ -319,12 +318,19 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x13, 1, 2); u32 flags = rp.Pop(); - interrupt_event = rp.PopObject(); + auto interrupt_event = rp.PopObject(); // 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"; + u32 thread_id = next_thread_id++; + + SessionData* session_data = GetSessionData(ctx.Session()); + session_data->thread_id = thread_id; + session_data->interrupt_event = std::move(interrupt_event); + session_data->registered = true; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); if (first_initialization) { @@ -338,22 +344,23 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { rb.Push(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->thread_id = 0; + session_data->interrupt_event = nullptr; + session_data->registered = false; + + // TODO(Subv): Reset next_thread_id so that it doesn't go past the maximum of 4. IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_GSP, "(STUBBED) called"); + LOG_DEBUG(Service_GSP, "called"); } /** @@ -366,15 +373,20 @@ 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) { + SessionData* session_data = FindRegisteredThreadData(thread_id); + if (session_data == nullptr) + continue; + + auto interrupt_event = session_data->interrupt_event; + if (interrupt_event == nullptr) { + LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); + continue; + } InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(shared_memory, thread_id); u8 next = interrupt_relay_queue->index; @@ -398,8 +410,8 @@ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { info->is_dirty.Assign(false); } } + interrupt_event->Signal(); } - interrupt_event->Signal(); } MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255)); @@ -655,6 +667,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(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 +714,13 @@ 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; }; - } // namespace GSP } // namespace Service diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 98756a8ff..737525653 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -8,12 +8,12 @@ #include #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,16 @@ struct CommandBuffer { }; static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrect size"); -class GSP_GPU final : public ServiceFramework { +struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { + /// Event triggered when GSP interrupt has been signalled + Kernel::SharedPtr interrupt_event; + /// Thread index into interrupt relay queue + u32 thread_id = 0; + /// Whether RegisterInterruptRelayQueue was called for this session + bool registered = false; +}; + +class GSP_GPU final : public ServiceFramework { public: GSP_GPU(); ~GSP_GPU() = default; @@ -351,12 +360,14 @@ private: */ void StoreDataCache(Kernel::HLERequestContext& ctx); - /// Event triggered when GSP interrupt has been signalled - Kernel::SharedPtr interrupt_event; - /// GSP shared memoryings + /// Returns the session data for the specified registered thread id, or nullptr if not found. + SessionData* FindRegisteredThreadData(u32 thread_id); + + /// Next threadid value to use when RegisterInterruptRelayQueue is called. + u32 next_thread_id = 0; + + /// GSP shared memory Kernel::SharedPtr shared_memory; - /// Thread index into interrupt relay queue - u32 thread_id = 0; bool gpu_right_acquired = false; bool first_initialization = true; From 05a44ed353853a8b693cb5f627ca41c6c045791b Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 21 Dec 2017 11:43:30 -0500 Subject: [PATCH 11/42] HLE/GSP: Keep track of the thread that currently has the GPU right. This thread is the only one for which the GSP interrupts should be signaled, except for the PDC0/1 interrupts. --- src/core/hle/service/gsp/gsp_gpu.cpp | 20 ++++++++++++-------- src/core/hle/service/gsp/gsp_gpu.h | 4 +++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 4e854164e..15e8de322 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -370,9 +370,6 @@ 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 == shared_memory) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); return; @@ -634,18 +631,26 @@ void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) { u32 flag = rp.Pop(); auto process = rp.PopObject(); - gpu_right_acquired = true; + // 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"); + + SessionData* session_data = GetSessionData(ctx.Session()); + active_thread_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); + LOG_WARNING(Service_GSP, "called flag=%08X process=%u thread_id=%u", flag, process->process_id, + active_thread_id); } 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); @@ -669,7 +674,7 @@ void GSP_GPU::StoreDataCache(Kernel::HLERequestContext& ctx) { SessionData* GSP_GPU::FindRegisteredThreadData(u32 thread_id) { for (auto& session_info : connected_sessions) { - SessionData* data = static_cast(session_info.data.get()) + SessionData* data = static_cast(session_info.data.get()); if (!data->registered) continue; if (data->thread_id == thread_id) @@ -719,7 +724,6 @@ GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) { MemoryPermission::ReadWrite, 0, Kernel::MemoryRegion::BASE, "GSP:SharedMemory"); - gpu_right_acquired = false; first_initialization = true; }; } // namespace GSP diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 737525653..01bab8652 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -369,7 +369,9 @@ private: /// GSP shared memory Kernel::SharedPtr shared_memory; - bool gpu_right_acquired = false; + /// Thread id that currently has GPU rights or -1 if none. + int active_thread_id = -1; + bool first_initialization = true; }; From 68fc3b36e635bebd071cb78b38d28b0424553af9 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 21 Dec 2017 11:55:44 -0500 Subject: [PATCH 12/42] HLE/GSP: Only trigger GSP interrupts for the current active GSP thread. This is true for all interrupts except PDC0 and PDC1, which should be triggered for all registered threads. TODO: The real GSP module seems to only trigger PDC0 after updating the screens (both top and bottom). PDC1 doesn't seem to be triggered at all. --- src/core/hle/service/gsp/gsp_gpu.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 15e8de322..fb4343e7b 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -50,6 +50,9 @@ 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; + /// Gets a pointer to a thread command buffer in GSP shared memory static inline u8* GetCommandBuffer(Kernel::SharedPtr shared_memory, u32 thread_id) { @@ -325,6 +328,7 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { interrupt_event->name = "GSP_GSP_GPU::interrupt_event"; u32 thread_id = next_thread_id++; + ASSERT_MSG(thread_id < MaxGSPThreads, "GSP thread id overflow"); SessionData* session_data = GetSessionData(ctx.Session()); session_data->thread_id = thread_id; @@ -370,11 +374,23 @@ 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) { + // Don't do anything if no process has acquired the GPU right. + if (active_thread_id == -1) + 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) { + + // 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. + for (int thread_id = 0; thread_id < MaxGSPThreads; ++thread_id) { + if (interrupt_id != InterruptId::PDC0 && interrupt_id != InterruptId::PDC1) { + // Ignore threads that aren't the current active thread + if (thread_id != active_thread_id) + continue; + } SessionData* session_data = FindRegisteredThreadData(thread_id); if (session_data == nullptr) continue; @@ -398,6 +414,8 @@ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) { // 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) { From fbef978b984bc80a6f4159fd143bde1c0c7e34b7 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 21 Dec 2017 15:09:03 -0500 Subject: [PATCH 13/42] GSP: Return the correct result code if AcquireRight is called multiple times from the same thread. --- src/core/hle/service/gsp/gsp_gpu.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index fb4343e7b..11f9f4ae6 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -649,17 +649,25 @@ void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) { u32 flag = rp.Pop(); auto process = rp.PopObject(); + 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); + + 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"); - SessionData* session_data = GetSessionData(ctx.Session()); active_thread_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 thread_id=%u", flag, process->process_id, - active_thread_id); } void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) { From bbe6c8890c420e644a531839f31801c69d9d7412 Mon Sep 17 00:00:00 2001 From: Senjosei Date: Fri, 29 Dec 2017 06:15:59 +1100 Subject: [PATCH 14/42] Pause emulation OnCoreError --- src/citra_qt/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index e7c327540..ed334b428 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -918,6 +918,7 @@ void GMainWindow::UpdateStatusBar() { } void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { + emu_thread->SetRunning(false); QMessageBox::StandardButton answer; QString status_message; const QString common_message = @@ -974,6 +975,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); } From f17600db1a94269f012cf39b81ef072916839483 Mon Sep 17 00:00:00 2001 From: Senjosei Date: Fri, 29 Dec 2017 06:17:21 +1100 Subject: [PATCH 15/42] Moved when to pause because it wasnt fast enough --- src/citra_qt/bootmanager.cpp | 1 + src/citra_qt/main.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 64c0aa541..510d6587a 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -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()); } diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index ed334b428..e66b00903 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -918,7 +918,6 @@ void GMainWindow::UpdateStatusBar() { } void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { - emu_thread->SetRunning(false); QMessageBox::StandardButton answer; QString status_message; const QString common_message = From fb72a9d9fa73bcd0f8c2aa57b00b7e4b2961f121 Mon Sep 17 00:00:00 2001 From: Senjosei Date: Fri, 29 Dec 2017 06:31:18 +1100 Subject: [PATCH 16/42] Fixed clang whitespace --- src/citra_qt/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index e66b00903..1067c5064 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -974,7 +974,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); + emu_thread->SetRunning(true); message_label->setText(status_message); message_label->setVisible(true); } From f93d656e53381256342af9dc18ea14c17e86075f Mon Sep 17 00:00:00 2001 From: mailwl Date: Fri, 29 Dec 2017 21:04:10 +0300 Subject: [PATCH 17/42] citra(SDL) small fixes: fix fall-through, remove u16 'always true' compare --- src/citra/citra.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 85d7ab099..f1c5a64db 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -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 -#else #include +#ifndef _MSC_VER #include #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': From e0e4b70f6a3cbad2853f333ce4bbee46f550dc66 Mon Sep 17 00:00:00 2001 From: PizzicatoWolf Date: Sat, 30 Dec 2017 14:10:05 +1030 Subject: [PATCH 18/42] Resized main config window --- src/citra_qt/configuration/configure.ui | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/citra_qt/configuration/configure.ui b/src/citra_qt/configuration/configure.ui index 6abd1917e..91c30d501 100644 --- a/src/citra_qt/configuration/configure.ui +++ b/src/citra_qt/configuration/configure.ui @@ -6,7 +6,7 @@ 0 0 - 740 + 461 500 @@ -17,7 +17,7 @@ - 0 + 4 @@ -34,15 +34,15 @@ Input - - - Graphics - - + + + Graphics + + - - Audio - + + Audio + From 8fa160395b963414fc5e32a1c3a041c4fdb93ce0 Mon Sep 17 00:00:00 2001 From: PizzicatoWolf Date: Sat, 30 Dec 2017 14:51:45 +1030 Subject: [PATCH 19/42] Revert index change --- src/citra_qt/configuration/configure.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/citra_qt/configuration/configure.ui b/src/citra_qt/configuration/configure.ui index 91c30d501..dce2b7739 100644 --- a/src/citra_qt/configuration/configure.ui +++ b/src/citra_qt/configuration/configure.ui @@ -17,7 +17,7 @@ - 4 + 0 From 7daa2a51d748d283183e2ee9b3c5b7f9d3bce850 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sat, 30 Dec 2017 23:42:08 +0200 Subject: [PATCH 20/42] Kernel/SVC: fix typo in GetResourceLimitLimitValues --- src/core/hle/kernel/svc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index cb5dbad5d..7f219ffde 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -715,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); } From ee779a5a4dfd3f5bebc82113e3cc71d3906eaccf Mon Sep 17 00:00:00 2001 From: tgsm Date: Sat, 9 Dec 2017 22:25:42 -0500 Subject: [PATCH 21/42] y2r: convert some functions to use IPC helper --- src/core/hle/service/y2r_u.cpp | 366 +++++++++++++++++---------------- 1 file changed, 187 insertions(+), 179 deletions(-) diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 3f1a0c452..786ca1186 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -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(cmd_buff[1]); + conversion.input_format = rp.PopEnum(); - 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(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(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(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(cmd_buff[1]); + conversion.output_format = rp.PopEnum(); - 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(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(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(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(cmd_buff[1]); + conversion.rotation = rp.PopEnum(); - 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(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(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(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(cmd_buff[1]); + conversion.block_alignment = rp.PopEnum(); - 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(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(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(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() & 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() & 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() & 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(); conversion.src_Y.image_size = rp.Pop(); conversion.src_Y.transfer_unit = rp.Pop(); @@ -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(); conversion.src_U.image_size = rp.Pop(); conversion.src_U.transfer_unit = rp.Pop(); @@ -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(); + conversion.src_V.image_size = rp.Pop(); + conversion.src_V.transfer_unit = rp.Pop(); + conversion.src_V.gap = rp.Pop(); + 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(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(); + conversion.src_YUYV.image_size = rp.Pop(); + conversion.src_YUYV.transfer_unit = rp.Pop(); + conversion.src_YUYV.gap = rp.Pop(); + 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(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(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(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(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(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(); + conversion.dst.image_size = rp.Pop(); + conversion.dst.transfer_unit = rp.Pop(); + conversion.dst.gap = rp.Pop(); + 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(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(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(); - 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(); - 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(conversion.input_lines); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast(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(&cmd_buff[1]); - std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet)); + rp.PopRaw(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 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(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(); - 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(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(); - auto params = reinterpret_cast(&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(params->input_format), static_cast(params->output_format), - static_cast(params->rotation), static_cast(params->block_alignment), - params->input_line_width, params->input_lines, - static_cast(params->standard_coefficient), params->padding, params->alpha); + static_cast(params.input_format), static_cast(params.output_format), + static_cast(params.rotation), static_cast(params.block_alignment), + params.input_line_width, params.input_lines, static_cast(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(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 \ No newline at end of file +} // namespace Service From d17f148e48afe5c43673da4dbb9ee45c640fa52f Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 2 Jan 2018 12:07:26 -0500 Subject: [PATCH 22/42] Services/GSP: Assign a thread id to each connected session when the session is created. Most applications call AcquireRight before calling RegisterInterruptRelayQueue so we can't assign the thread id there. This fixes the bug with LLE applets not launching properly. --- src/core/hle/service/gsp/gsp_gpu.cpp | 26 +++++++++++++++++++++----- src/core/hle/service/gsp/gsp_gpu.h | 7 +++---- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 11f9f4ae6..7156f3f5c 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -53,6 +53,17 @@ constexpr ResultCode ERR_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorM /// 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 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 shared_memory, u32 thread_id) { @@ -327,11 +338,7 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { interrupt_event->name = "GSP_GSP_GPU::interrupt_event"; - u32 thread_id = next_thread_id++; - ASSERT_MSG(thread_id < MaxGSPThreads, "GSP thread id overflow"); - SessionData* session_data = GetSessionData(ctx.Session()); - session_data->thread_id = thread_id; session_data->interrupt_event = std::move(interrupt_event); session_data->registered = true; @@ -345,7 +352,7 @@ 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); LOG_DEBUG(Service_GSP, "called, flags=0x%08X", flags); @@ -752,5 +759,14 @@ GSP_GPU::GSP_GPU() : ServiceFramework("gsp::Gpu", 2) { 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; +} + } // namespace GSP } // namespace Service diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 01bab8652..c057dffc4 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -180,10 +180,12 @@ struct CommandBuffer { static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrect size"); struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { + SessionData(); + /// Event triggered when GSP interrupt has been signalled Kernel::SharedPtr interrupt_event; /// Thread index into interrupt relay queue - u32 thread_id = 0; + u32 thread_id; /// Whether RegisterInterruptRelayQueue was called for this session bool registered = false; }; @@ -363,9 +365,6 @@ private: /// Returns the session data for the specified registered thread id, or nullptr if not found. SessionData* FindRegisteredThreadData(u32 thread_id); - /// Next threadid value to use when RegisterInterruptRelayQueue is called. - u32 next_thread_id = 0; - /// GSP shared memory Kernel::SharedPtr shared_memory; From 75f68c48605aa09e183b3a2c390b43aa9a0d5d0e Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 2 Jan 2018 12:09:43 -0500 Subject: [PATCH 23/42] Services/GSP: Mark the thread ids as unused when a GSP session is destroyed. This fixes the games that call RegisterInterruptRelayQueue and UnregisterInterruptRelayQueue frequently. --- src/core/hle/service/gsp/gsp_gpu.cpp | 8 +++++--- src/core/hle/service/gsp/gsp_gpu.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 7156f3f5c..41de8f561 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -362,12 +362,9 @@ void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x14, 0, 0); SessionData* session_data = GetSessionData(ctx.Session()); - session_data->thread_id = 0; session_data->interrupt_event = nullptr; session_data->registered = false; - // TODO(Subv): Reset next_thread_id so that it doesn't go past the maximum of 4. - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -768,5 +765,10 @@ SessionData::SessionData() { 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 diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index c057dffc4..55c2343a4 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -181,6 +181,7 @@ static_assert(sizeof(CommandBuffer) == 0x200, "CommandBuffer struct has incorrec struct SessionData : public Kernel::SessionRequestHandler::SessionDataBase { SessionData(); + ~SessionData(); /// Event triggered when GSP interrupt has been signalled Kernel::SharedPtr interrupt_event; From 41929371dcc267a1f367dbfb458d22b9a3cce9e8 Mon Sep 17 00:00:00 2001 From: Dwayne Slater Date: Tue, 2 Jan 2018 18:32:33 -0500 Subject: [PATCH 24/42] Optimize AttributeBuffer to OutputVertex conversion (#3283) Optimize AttributeBuffer to OutputVertex conversion First I unrolled the inner loop, then I pushed semantics validation outside of the hotloop. I also added overflow slots to avoid conditional branches. Super Mario 3D Land's intro runs at almost full speed when compiled with Clang, and theres a noticible speed increase in MSVC. GCC hasn't been tested but I'm confident in its ability to optimize this code. --- src/video_core/command_processor.cpp | 3 ++ src/video_core/regs_rasterizer.h | 2 ++ src/video_core/shader/shader.cpp | 46 +++++++++++++++++----------- src/video_core/shader/shader.h | 1 + 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index c34a277db..3ccd1297e 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -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)); diff --git a/src/video_core/regs_rasterizer.h b/src/video_core/regs_rasterizer.h index 4fef00d76..9f68494ce 100644 --- a/src/video_core/regs_rasterizer.h +++ b/src/video_core/regs_rasterizer.h @@ -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); diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index 2d0ffe821..194c9df11 100644 --- a/src/video_core/shader/shader.cpp +++ b/src/video_core/shader/shader.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #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 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 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) == 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 diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h index 8740a1618..21ac01064 100644 --- a/src/video_core/shader/shader.h +++ b/src/video_core/shader/shader.h @@ -50,6 +50,7 @@ struct OutputVertex { INSERT_PADDING_WORDS(1); Math::Vec2 tc2; + static void ValidateSemantics(const RasterizerRegs& regs); static OutputVertex FromAttributeBuffer(const RasterizerRegs& regs, const AttributeBuffer& output); }; From aa90198ec5e1bab2a7b875611ac07ac6da05fe6e Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 3 Jan 2018 10:52:46 -0500 Subject: [PATCH 25/42] Services: Make SessionDataBase's destructor virtual. --- src/core/hle/kernel/hle_ipc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index ccff81a21..95d9624db 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -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. From 8ca20ca6f059dcdab9846ed651fa00fbe2cbef53 Mon Sep 17 00:00:00 2001 From: wwylele Date: Thu, 4 Jan 2018 15:26:15 +0200 Subject: [PATCH 26/42] README: add info about translation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ad2f03a56..9b249f126 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ Most of the development happens on GitHub. It's also where [our central reposito If you want to contribute please take a look at the [Contributor's Guide](CONTRIBUTING.md) and [Developer Information](https://github.com/citra-emu/citra/wiki/Developer-Information). You should as well contact any of the developers in the forum in order to know about the current state of the emulator because the [TODO list](https://docs.google.com/document/d/1SWIop0uBI9IW8VGg97TAtoT_CHNoP42FzYmvG1F4QDA) isn't maintained anymore. +If you want to contribute to the user interface translation, please checkout [citra project on transifex](https://www.transifex.com/citra/citra). We centralize the translation work there, and periodically upstream translation. + ### Building * __Windows__: [Windows Build](https://github.com/citra-emu/citra/wiki/Building-For-Windows) From 34685f48dce91a12d0c7b0e014c6ce9ada7177e7 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 6 Jan 2018 13:51:33 -0500 Subject: [PATCH 27/42] GSP: Allow the signaling of the PDC0/1 interrupts even if the GPU right hasn't been acquired. This was verified with a hwtest. --- src/core/hle/service/gsp/gsp_gpu.cpp | 92 +++++++++++++++------------- src/core/hle/service/gsp/gsp_gpu.h | 8 +++ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index 41de8f561..3a919e87e 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -371,6 +371,43 @@ void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) { 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(); +} + /** * Signals that the specified interrupt type has occurred to userland code * @param interrupt_id ID of interrupt that is being signalled @@ -378,59 +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) { - // Don't do anything if no process has acquired the GPU right. - if (active_thread_id == -1) - return; - if (nullptr == shared_memory) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); return; } + // 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. - for (int thread_id = 0; thread_id < MaxGSPThreads; ++thread_id) { - if (interrupt_id != InterruptId::PDC0 && interrupt_id != InterruptId::PDC1) { - // Ignore threads that aren't the current active thread - if (thread_id != active_thread_id) - continue; + if (interrupt_id == InterruptId::PDC0 || interrupt_id == InterruptId::PDC1) { + for (u32 thread_id = 0; thread_id < MaxGSPThreads; ++thread_id) { + SignalInterruptForThread(interrupt_id, thread_id); } - SessionData* session_data = FindRegisteredThreadData(thread_id); - if (session_data == nullptr) - continue; - - auto interrupt_event = session_data->interrupt_event; - if (interrupt_event == nullptr) { - LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); - continue; - } - 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(); + return; } + + // 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)); diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 55c2343a4..214d96c0e 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -213,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 * From 6773546d5c727b2ec0e8807f26f1e1796135bc5d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 8 Jan 2018 17:54:37 +0100 Subject: [PATCH 28/42] CMakeLists: Disable architecture checks with -DENABLE_GENERIC=1 --- CMakeLists.txt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b7c7c48ab..25a5ef57e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,14 +65,16 @@ function(detect_architecture symbol arch) endif() endfunction() -if (MSVC) - detect_architecture("_M_AMD64" x86_64) - detect_architecture("_M_IX86" x86) - detect_architecture("_M_ARM" ARM) -else() - detect_architecture("__x86_64__" x86_64) - detect_architecture("__i386__" x86) - detect_architecture("__arm__" ARM) +if (NOT ENABLE_GENERIC) + if (MSVC) + detect_architecture("_M_AMD64" x86_64) + detect_architecture("_M_IX86" x86) + detect_architecture("_M_ARM" ARM) + else() + detect_architecture("__x86_64__" x86_64) + detect_architecture("__i386__" x86) + detect_architecture("__arm__" ARM) + endif() endif() if (NOT DEFINED ARCHITECTURE) set(ARCHITECTURE "GENERIC") From 2cd2a7491c075c0f3600356616fbd3b694482424 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 8 Jan 2018 17:58:00 +0100 Subject: [PATCH 29/42] =?UTF-8?q?CMakeLists:=20Disable=20dynarmic=20on=20?= =?UTF-8?q?=C2=ACx86=5F64?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- externals/CMakeLists.txt | 12 +++++++----- src/core/CMakeLists.txt | 16 +++++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 4a4ba1101..88cc52588 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -11,11 +11,13 @@ target_include_directories(catch-single-include INTERFACE catch/single_include) add_subdirectory(cryptopp) # Dynarmic -# Dynarmic will skip defining xbyak if it's already defined, we then define it below -add_library(xbyak INTERFACE) -option(DYNARMIC_TESTS OFF) -set(DYNARMIC_NO_BUNDLED_FMT ON) -add_subdirectory(dynarmic) +if (ARCHITECTURE_x86_64) + # Dynarmic will skip defining xbyak if it's already defined, we then define it below + add_library(xbyak INTERFACE) + option(DYNARMIC_TESTS OFF) + set(DYNARMIC_NO_BUNDLED_FMT ON) + add_subdirectory(dynarmic) +endif() # libfmt add_subdirectory(fmt) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6057b183f..f6d6fd3d4 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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) + add_library(core STATIC + 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() From e5a3dc2956428d5a0a65e30ffd666d845db9d286 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 8 Jan 2018 17:58:24 +0100 Subject: [PATCH 30/42] Core: Disable the JIT when dynarmic is not available --- src/core/CMakeLists.txt | 2 +- src/core/core.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f6d6fd3d4..54df27193 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -406,7 +406,7 @@ if (ENABLE_WEB_SERVICE) endif() if (ARCHITECTURE_x86_64) - add_library(core STATIC + target_sources(core PRIVATE arm/dynarmic/arm_dynarmic.cpp arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic_cp15.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 0c658d1ff..ad7474ff4 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -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(USER32MODE); +#else + cpu_core = std::make_unique(USER32MODE); + LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); +#endif } else { cpu_core = std::make_unique(USER32MODE); } From 7f9fae5f7c2d4d4e4ad385fc48c397d8761fd13a Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Mon, 8 Jan 2018 17:59:42 +0100 Subject: [PATCH 31/42] =?UTF-8?q?Telemetry:=20Disable=20CPU=20detection=20?= =?UTF-8?q?on=20=C2=ACx86=5F64?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/telemetry_session.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index ca517ff44..92abbeb5e 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -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) From c361dad7d2e035e45dffe329a0224224cb307b8d Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 13 Jan 2018 21:41:51 +0100 Subject: [PATCH 32/42] Citra: Print the actual error preventing SDL from working --- src/citra/emu_window/emu_window_sdl2.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 25643715a..1a6fd0fa3 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -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(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); } From 7f77820460a045ea6f0f1e15015b86232d42ec31 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 15 Jan 2018 01:03:08 -0800 Subject: [PATCH 33/42] Common: Replace MurmurHash3 with CityHash64 CityHash64 is faster than Murmur3 at all sizes, but especially for short keys. --- src/common/CMakeLists.txt | 3 +- src/common/cityhash.cpp | 650 ++++++++++++++++++++++++++++++++++++++ src/common/cityhash.h | 112 +++++++ src/common/hash.cpp | 141 --------- src/common/hash.h | 7 +- 5 files changed, 766 insertions(+), 147 deletions(-) create mode 100644 src/common/cityhash.cpp create mode 100644 src/common/cityhash.h delete mode 100644 src/common/hash.cpp diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index bdec3c43f..2fe864f92 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -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 diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp new file mode 100644 index 000000000..84f044ed0 --- /dev/null +++ b/src/common/cityhash.cpp @@ -0,0 +1,650 @@ +// 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 +#include // 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; + +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 _MSC_VER + +#include +#define bswap_32(x) _byteswap_ulong(x) +#define bswap_64(x) _byteswap_uint64(x) + +#elif defined(__APPLE__) + +// Mac OS X / Darwin features +#include +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) + +#elif defined(__sun) || defined(sun) + +#include +#define bswap_32(x) BSWAP_32(x) +#define bswap_64(x) BSWAP_64(x) + +#elif defined(__FreeBSD__) + +#include +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) + +#elif defined(__OpenBSD__) + +#include +#define bswap_32(x) swap32(x) +#define bswap_64(x) swap64(x) + +#elif defined(__NetBSD__) + +#include +#include +#if defined(__BSWAP_RENAME) && !defined(__bswap_32) +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) +#endif + +#else + +#include + +#endif + +#ifdef WORDS_BIGENDIAN +#define uint32_in_expected_order(x) (bswap_32(x)) +#define uint64_in_expected_order(x) (bswap_64(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; + +// Magic numbers for 32-bit hashing. Copied from Murmur3. +static const uint32 c1 = 0xcc9e2d51; +static const uint32 c2 = 0x1b873593; + +// A 32-bit to 32-bit integer hash copied from Murmur3. +static uint32 fmix(uint32 h) { + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; +} + +static uint32 Rotate32(uint32 val, int shift) { + // Avoid shifting by 32: doing so yields an undefined result. + return shift == 0 ? val : ((val >> shift) | (val << (32 - shift))); +} + +#undef PERMUTE3 +#define PERMUTE3(a, b, c) \ + do { \ + std::swap(a, b); \ + std::swap(a, c); \ + } while (0) + +static uint32 Mur(uint32 a, uint32 h) { + // Helper from Murmur3 for combining two 32-bit values. + a *= c1; + a = Rotate32(a, 17); + a *= c2; + h ^= a; + h = Rotate32(h, 19); + return h * 5 + 0xe6546b64; +} + +static uint32 Hash32Len13to24(const char* s, size_t len) { + uint32 a = Fetch32(s - 4 + (len >> 1)); + uint32 b = Fetch32(s + 4); + uint32 c = Fetch32(s + len - 8); + uint32 d = Fetch32(s + (len >> 1)); + uint32 e = Fetch32(s); + uint32 f = Fetch32(s + len - 4); + uint32 h = len; + + return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); +} + +static uint32 Hash32Len0to4(const char* s, size_t len) { + uint32 b = 0; + uint32 c = 9; + for (size_t i = 0; i < len; i++) { + signed char v = s[i]; + b = b * c1 + v; + c ^= b; + } + return fmix(Mur(b, Mur(len, c))); +} + +static uint32 Hash32Len5to12(const char* s, size_t len) { + uint32 a = len, b = len * 5, c = 9, d = b; + a += Fetch32(s); + b += Fetch32(s + len - 4); + c += Fetch32(s + ((len >> 1) & 4)); + return fmix(Mur(c, Mur(b, Mur(a, d)))); +} + +uint32 CityHash32(const char* s, size_t len) { + if (len <= 24) { + return len <= 12 ? (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) + : Hash32Len13to24(s, len); + } + + // len > 24 + uint32 h = len, g = c1 * len, f = g; + uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2; + uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2; + uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2; + uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2; + uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2; + h ^= a0; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + h ^= a2; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + g ^= a1; + g = Rotate32(g, 19); + g = g * 5 + 0xe6546b64; + g ^= a3; + g = Rotate32(g, 19); + g = g * 5 + 0xe6546b64; + f += a4; + f = Rotate32(f, 19); + f = f * 5 + 0xe6546b64; + size_t iters = (len - 1) / 20; + do { + uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2; + uint32 a1 = Fetch32(s + 4); + uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2; + uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2; + uint32 a4 = Fetch32(s + 16); + h ^= a0; + h = Rotate32(h, 18); + h = h * 5 + 0xe6546b64; + f += a1; + f = Rotate32(f, 19); + f = f * c1; + g += a2; + g = Rotate32(g, 18); + g = g * 5 + 0xe6546b64; + h ^= a3 + a1; + h = Rotate32(h, 19); + h = h * 5 + 0xe6546b64; + g ^= a4; + g = bswap_32(g) * 5; + h += a4 * 5; + h = bswap_32(h); + f += a0; + PERMUTE3(f, h, g); + s += 20; + } while (--iters != 0); + g = Rotate32(g, 11) * c1; + g = Rotate32(g, 17) * c1; + f = Rotate32(f, 11) * c1; + f = Rotate32(f, 17) * c1; + h = Rotate32(h + g, 19); + h = h * 5 + 0xe6546b64; + h = Rotate32(h, 17) * c1; + h = Rotate32(h + f, 19); + h = h * 5 + 0xe6546b64; + h = Rotate32(h, 17) * c1; + return h; +} + +// 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(a) + (static_cast(b) << 8); + uint32 z = len + (static_cast(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 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 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 = bswap_64((u + v) * mul) + h; + uint64 x = Rotate(e + f, 42) + c; + uint64 y = (bswap_64((v + w) * mul) + g) * mul; + uint64 z = e + f + c; + a = bswap_64((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 v = WeakHashLen32WithSeeds(s + len - 64, len, z); + pair 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(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 = 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 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)); +} + +#ifdef __SSE4_2__ +#include +#include + +// Requires len >= 240. +static void CityHashCrc256Long(const char* s, size_t len, uint32 seed, uint64* result) { + uint64 a = Fetch64(s + 56) + k0; + uint64 b = Fetch64(s + 96) + k0; + uint64 c = result[0] = HashLen16(b, len); + uint64 d = result[1] = Fetch64(s + 120) * k0 + len; + uint64 e = Fetch64(s + 184) + seed; + uint64 f = 0; + uint64 g = 0; + uint64 h = c + d; + uint64 x = seed; + uint64 y = 0; + uint64 z = 0; + + // 240 bytes of input per iter. + size_t iters = len / 240; + len -= iters * 240; + do { +#undef CHUNK +#define CHUNK(r) \ + PERMUTE3(x, z, y); \ + b += Fetch64(s); \ + c += Fetch64(s + 8); \ + d += Fetch64(s + 16); \ + e += Fetch64(s + 24); \ + f += Fetch64(s + 32); \ + a += b; \ + h += f; \ + b += c; \ + f += d; \ + g += e; \ + e += z; \ + g += x; \ + z = _mm_crc32_u64(z, b + g); \ + y = _mm_crc32_u64(y, e + h); \ + x = _mm_crc32_u64(x, f + a); \ + e = Rotate(e, r); \ + c += e; \ + s += 40 + + CHUNK(0); + PERMUTE3(a, h, c); + CHUNK(33); + PERMUTE3(a, h, f); + CHUNK(0); + PERMUTE3(b, h, f); + CHUNK(42); + PERMUTE3(b, h, d); + CHUNK(0); + PERMUTE3(b, h, e); + CHUNK(33); + PERMUTE3(a, h, e); + } while (--iters > 0); + + while (len >= 40) { + CHUNK(29); + e ^= Rotate(a, 20); + h += Rotate(b, 30); + g ^= Rotate(c, 40); + f += Rotate(d, 34); + PERMUTE3(c, h, g); + len -= 40; + } + if (len > 0) { + s = s + len - 40; + CHUNK(33); + e ^= Rotate(a, 43); + h += Rotate(b, 42); + g ^= Rotate(c, 41); + f += Rotate(d, 40); + } + result[0] ^= h; + result[1] ^= g; + g += h; + a = HashLen16(a, g + z); + x += y << 32; + b += x; + c = HashLen16(c, z) + h; + d = HashLen16(d, e + result[0]); + g += e; + h += HashLen16(x, f); + e = HashLen16(a, d) + g; + z = HashLen16(b, c) + a; + y = HashLen16(g, h) + c; + result[0] = e + z + y + x; + a = ShiftMix((a + y) * k0) * k0 + b; + result[1] += a + result[0]; + a = ShiftMix(a * k0) * k0 + c; + result[2] = a + result[1]; + a = ShiftMix((a + e) * k0) * k0; + result[3] = a + result[2]; +} + +// Requires len < 240. +static void CityHashCrc256Short(const char* s, size_t len, uint64* result) { + char buf[240]; + memcpy(buf, s, len); + memset(buf + len, 0, 240 - len); + CityHashCrc256Long(buf, 240, ~static_cast(len), result); +} + +void CityHashCrc256(const char* s, size_t len, uint64* result) { + if (LIKELY(len >= 240)) { + CityHashCrc256Long(s, len, 0, result); + } else { + CityHashCrc256Short(s, len, result); + } +} + +uint128 CityHashCrc128WithSeed(const char* s, size_t len, uint128 seed) { + if (len <= 900) { + return CityHash128WithSeed(s, len, seed); + } else { + uint64 result[4]; + CityHashCrc256(s, len, result); + uint64 u = Uint128High64(seed) + result[0]; + uint64 v = Uint128Low64(seed) + result[1]; + return uint128(HashLen16(u, v + result[2]), HashLen16(Rotate(v, 32), u * k0 + result[3])); + } +} + +uint128 CityHashCrc128(const char* s, size_t len) { + if (len <= 900) { + return CityHash128(s, len); + } else { + uint64 result[4]; + CityHashCrc256(s, len, result); + return uint128(result[2], result[3]); + } +} + +#endif diff --git a/src/common/cityhash.h b/src/common/cityhash.h new file mode 100644 index 000000000..94499ce41 --- /dev/null +++ b/src/common/cityhash.h @@ -0,0 +1,112 @@ +// 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. + +#ifndef CITY_HASH_H_ +#define CITY_HASH_H_ + +#include // for size_t. +#include +#include + +typedef uint8_t uint8; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef std::pair uint128; + +inline uint64 Uint128Low64(const uint128& x) { return x.first; } +inline uint64 Uint128High64(const uint128& x) { return x.second; } + +// Hash function for a byte array. +uint64 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 CityHash64WithSeed(const char *buf, size_t len, uint64 seed); + +// Hash function for a byte array. For convenience, two seeds are also +// hashed into the result. +uint64 CityHash64WithSeeds(const char *buf, size_t len, + uint64 seed0, uint64 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 function for a byte array. Most useful in 32-bit binaries. +uint32 CityHash32(const char *buf, size_t len); + +// Hash 128 input bits down to 64 bits of output. +// This is intended to be a reasonably good hash function. +inline uint64 Hash128to64(const uint128& x) { + // Murmur-inspired hashing. + const uint64 kMul = 0x9ddfea08eb382d69ULL; + uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; + a ^= (a >> 47); + uint64 b = (Uint128High64(x) ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + return b; +} + +#endif // CITY_HASH_H_ diff --git a/src/common/hash.cpp b/src/common/hash.cpp deleted file mode 100644 index a02e9e5b9..000000000 --- a/src/common/hash.cpp +++ /dev/null @@ -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 -#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 diff --git a/src/common/hash.h b/src/common/hash.h index ee2560dad..556772574 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -5,12 +5,11 @@ #pragma once #include +#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,7 @@ 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(data), len); } } // namespace Common From d93ee651647059a86c1f2454177f18a73c7a019c Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 15 Jan 2018 01:05:15 -0800 Subject: [PATCH 34/42] Common: Add convenience function for hashing a struct --- src/common/hash.h | 13 +++++++++++++ src/video_core/renderer_opengl/gl_shader_gen.cpp | 1 + src/video_core/renderer_opengl/gl_shader_gen.h | 6 +----- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/common/hash.h b/src/common/hash.h index 556772574..cc3cece68 100644 --- a/src/common/hash.h +++ b/src/common/hash.h @@ -20,4 +20,17 @@ static inline u64 ComputeHash64(const void* data, size_t len) { return CityHash64(static_cast(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 +static inline u64 ComputeStructHash64(const T& data) { + static_assert( + std::is_trivially_copyable::value && std::is_standard_layout::value, + "Type passed to ComputeStructHash64 must be trivially copyable and standard layout"); + return ComputeHash64(&data, sizeof(data)); +} + } // namespace Common diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 9a61c0cfc..3053b0038 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -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; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 2302ae453..c67ceb54d 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -131,10 +131,6 @@ union PicaShaderConfig { } state; }; -#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) -static_assert(std::is_trivially_copyable::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 { size_t operator()(const GLShader::PicaShaderConfig& k) const { - return Common::ComputeHash64(&k.state, sizeof(GLShader::PicaShaderConfig::State)); + return Common::ComputeStructHash64(k.state); } }; } // namespace std From f081388afea591903c5902aabcb1650d70eb2f53 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 15 Jan 2018 01:27:30 -0800 Subject: [PATCH 35/42] Common: Adapt CityHash code to match our codebase better - Use #pragma once instead of guards - Move header typedefs to implementation file - Enclose in Common namespace --- src/common/cityhash.cpp | 8 ++++++ src/common/cityhash.h | 55 +++++++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp index 84f044ed0..ab8db7774 100644 --- a/src/common/cityhash.cpp +++ b/src/common/cityhash.cpp @@ -42,6 +42,12 @@ 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)); @@ -648,3 +654,5 @@ uint128 CityHashCrc128(const char* s, size_t len) { } #endif + +} // namespace Common diff --git a/src/common/cityhash.h b/src/common/cityhash.h index 94499ce41..e6baf6a37 100644 --- a/src/common/cityhash.h +++ b/src/common/cityhash.h @@ -59,54 +59,55 @@ // 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. -#ifndef CITY_HASH_H_ -#define CITY_HASH_H_ +#pragma once -#include // for size_t. -#include #include +#include +#include // for size_t. -typedef uint8_t uint8; -typedef uint32_t uint32; -typedef uint64_t uint64; -typedef std::pair uint128; +namespace Common { -inline uint64 Uint128Low64(const uint128& x) { return x.first; } -inline uint64 Uint128High64(const uint128& x) { return x.second; } +typedef std::pair 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 CityHash64(const char *buf, size_t len); +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 CityHash64WithSeed(const char *buf, size_t len, uint64 seed); +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 CityHash64WithSeeds(const char *buf, size_t len, - uint64 seed0, uint64 seed1); +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); +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); +uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed); // Hash function for a byte array. Most useful in 32-bit binaries. -uint32 CityHash32(const char *buf, size_t len); +uint32_t CityHash32(const char* buf, size_t len); // Hash 128 input bits down to 64 bits of output. // This is intended to be a reasonably good hash function. -inline uint64 Hash128to64(const uint128& x) { - // Murmur-inspired hashing. - const uint64 kMul = 0x9ddfea08eb382d69ULL; - uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; - a ^= (a >> 47); - uint64 b = (Uint128High64(x) ^ a) * kMul; - b ^= (b >> 47); - b *= kMul; - return b; +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; } -#endif // CITY_HASH_H_ +} // namespace Common From 712e6ee960744b8b6167fa8d8553313b06e9424a Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 15 Jan 2018 01:38:54 -0800 Subject: [PATCH 36/42] Common: Remove CityHash32 and CityHashCrc128 variants In 64-bit systems, CityHash64 is always strictly better than CityHash32. CityHashCrc128 requires SSE 4.2. --- src/common/cityhash.cpp | 272 ---------------------------------------- src/common/cityhash.h | 3 - 2 files changed, 275 deletions(-) diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp index ab8db7774..596b943e7 100644 --- a/src/common/cityhash.cpp +++ b/src/common/cityhash.cpp @@ -135,141 +135,6 @@ static const uint64 k0 = 0xc3a5c85c97cb3127ULL; static const uint64 k1 = 0xb492b66fbe98f273ULL; static const uint64 k2 = 0x9ae16a3b2f90404fULL; -// Magic numbers for 32-bit hashing. Copied from Murmur3. -static const uint32 c1 = 0xcc9e2d51; -static const uint32 c2 = 0x1b873593; - -// A 32-bit to 32-bit integer hash copied from Murmur3. -static uint32 fmix(uint32 h) { - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - return h; -} - -static uint32 Rotate32(uint32 val, int shift) { - // Avoid shifting by 32: doing so yields an undefined result. - return shift == 0 ? val : ((val >> shift) | (val << (32 - shift))); -} - -#undef PERMUTE3 -#define PERMUTE3(a, b, c) \ - do { \ - std::swap(a, b); \ - std::swap(a, c); \ - } while (0) - -static uint32 Mur(uint32 a, uint32 h) { - // Helper from Murmur3 for combining two 32-bit values. - a *= c1; - a = Rotate32(a, 17); - a *= c2; - h ^= a; - h = Rotate32(h, 19); - return h * 5 + 0xe6546b64; -} - -static uint32 Hash32Len13to24(const char* s, size_t len) { - uint32 a = Fetch32(s - 4 + (len >> 1)); - uint32 b = Fetch32(s + 4); - uint32 c = Fetch32(s + len - 8); - uint32 d = Fetch32(s + (len >> 1)); - uint32 e = Fetch32(s); - uint32 f = Fetch32(s + len - 4); - uint32 h = len; - - return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h))))))); -} - -static uint32 Hash32Len0to4(const char* s, size_t len) { - uint32 b = 0; - uint32 c = 9; - for (size_t i = 0; i < len; i++) { - signed char v = s[i]; - b = b * c1 + v; - c ^= b; - } - return fmix(Mur(b, Mur(len, c))); -} - -static uint32 Hash32Len5to12(const char* s, size_t len) { - uint32 a = len, b = len * 5, c = 9, d = b; - a += Fetch32(s); - b += Fetch32(s + len - 4); - c += Fetch32(s + ((len >> 1) & 4)); - return fmix(Mur(c, Mur(b, Mur(a, d)))); -} - -uint32 CityHash32(const char* s, size_t len) { - if (len <= 24) { - return len <= 12 ? (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) - : Hash32Len13to24(s, len); - } - - // len > 24 - uint32 h = len, g = c1 * len, f = g; - uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2; - uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2; - uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2; - uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2; - uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2; - h ^= a0; - h = Rotate32(h, 19); - h = h * 5 + 0xe6546b64; - h ^= a2; - h = Rotate32(h, 19); - h = h * 5 + 0xe6546b64; - g ^= a1; - g = Rotate32(g, 19); - g = g * 5 + 0xe6546b64; - g ^= a3; - g = Rotate32(g, 19); - g = g * 5 + 0xe6546b64; - f += a4; - f = Rotate32(f, 19); - f = f * 5 + 0xe6546b64; - size_t iters = (len - 1) / 20; - do { - uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2; - uint32 a1 = Fetch32(s + 4); - uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2; - uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2; - uint32 a4 = Fetch32(s + 16); - h ^= a0; - h = Rotate32(h, 18); - h = h * 5 + 0xe6546b64; - f += a1; - f = Rotate32(f, 19); - f = f * c1; - g += a2; - g = Rotate32(g, 18); - g = g * 5 + 0xe6546b64; - h ^= a3 + a1; - h = Rotate32(h, 19); - h = h * 5 + 0xe6546b64; - g ^= a4; - g = bswap_32(g) * 5; - h += a4 * 5; - h = bswap_32(h); - f += a0; - PERMUTE3(f, h, g); - s += 20; - } while (--iters != 0); - g = Rotate32(g, 11) * c1; - g = Rotate32(g, 17) * c1; - f = Rotate32(f, 11) * c1; - f = Rotate32(f, 17) * c1; - h = Rotate32(h + g, 19); - h = h * 5 + 0xe6546b64; - h = Rotate32(h, 17) * c1; - h = Rotate32(h + f, 19); - h = h * 5 + 0xe6546b64; - h = Rotate32(h, 17) * c1; - return h; -} - // 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) { @@ -518,141 +383,4 @@ uint128 CityHash128(const char* s, size_t len) { : CityHash128WithSeed(s, len, uint128(k0, k1)); } -#ifdef __SSE4_2__ -#include -#include - -// Requires len >= 240. -static void CityHashCrc256Long(const char* s, size_t len, uint32 seed, uint64* result) { - uint64 a = Fetch64(s + 56) + k0; - uint64 b = Fetch64(s + 96) + k0; - uint64 c = result[0] = HashLen16(b, len); - uint64 d = result[1] = Fetch64(s + 120) * k0 + len; - uint64 e = Fetch64(s + 184) + seed; - uint64 f = 0; - uint64 g = 0; - uint64 h = c + d; - uint64 x = seed; - uint64 y = 0; - uint64 z = 0; - - // 240 bytes of input per iter. - size_t iters = len / 240; - len -= iters * 240; - do { -#undef CHUNK -#define CHUNK(r) \ - PERMUTE3(x, z, y); \ - b += Fetch64(s); \ - c += Fetch64(s + 8); \ - d += Fetch64(s + 16); \ - e += Fetch64(s + 24); \ - f += Fetch64(s + 32); \ - a += b; \ - h += f; \ - b += c; \ - f += d; \ - g += e; \ - e += z; \ - g += x; \ - z = _mm_crc32_u64(z, b + g); \ - y = _mm_crc32_u64(y, e + h); \ - x = _mm_crc32_u64(x, f + a); \ - e = Rotate(e, r); \ - c += e; \ - s += 40 - - CHUNK(0); - PERMUTE3(a, h, c); - CHUNK(33); - PERMUTE3(a, h, f); - CHUNK(0); - PERMUTE3(b, h, f); - CHUNK(42); - PERMUTE3(b, h, d); - CHUNK(0); - PERMUTE3(b, h, e); - CHUNK(33); - PERMUTE3(a, h, e); - } while (--iters > 0); - - while (len >= 40) { - CHUNK(29); - e ^= Rotate(a, 20); - h += Rotate(b, 30); - g ^= Rotate(c, 40); - f += Rotate(d, 34); - PERMUTE3(c, h, g); - len -= 40; - } - if (len > 0) { - s = s + len - 40; - CHUNK(33); - e ^= Rotate(a, 43); - h += Rotate(b, 42); - g ^= Rotate(c, 41); - f += Rotate(d, 40); - } - result[0] ^= h; - result[1] ^= g; - g += h; - a = HashLen16(a, g + z); - x += y << 32; - b += x; - c = HashLen16(c, z) + h; - d = HashLen16(d, e + result[0]); - g += e; - h += HashLen16(x, f); - e = HashLen16(a, d) + g; - z = HashLen16(b, c) + a; - y = HashLen16(g, h) + c; - result[0] = e + z + y + x; - a = ShiftMix((a + y) * k0) * k0 + b; - result[1] += a + result[0]; - a = ShiftMix(a * k0) * k0 + c; - result[2] = a + result[1]; - a = ShiftMix((a + e) * k0) * k0; - result[3] = a + result[2]; -} - -// Requires len < 240. -static void CityHashCrc256Short(const char* s, size_t len, uint64* result) { - char buf[240]; - memcpy(buf, s, len); - memset(buf + len, 0, 240 - len); - CityHashCrc256Long(buf, 240, ~static_cast(len), result); -} - -void CityHashCrc256(const char* s, size_t len, uint64* result) { - if (LIKELY(len >= 240)) { - CityHashCrc256Long(s, len, 0, result); - } else { - CityHashCrc256Short(s, len, result); - } -} - -uint128 CityHashCrc128WithSeed(const char* s, size_t len, uint128 seed) { - if (len <= 900) { - return CityHash128WithSeed(s, len, seed); - } else { - uint64 result[4]; - CityHashCrc256(s, len, result); - uint64 u = Uint128High64(seed) + result[0]; - uint64 v = Uint128Low64(seed) + result[1]; - return uint128(HashLen16(u, v + result[2]), HashLen16(Rotate(v, 32), u * k0 + result[3])); - } -} - -uint128 CityHashCrc128(const char* s, size_t len) { - if (len <= 900) { - return CityHash128(s, len); - } else { - uint64 result[4]; - CityHashCrc256(s, len, result); - return uint128(result[2], result[3]); - } -} - -#endif - } // namespace Common diff --git a/src/common/cityhash.h b/src/common/cityhash.h index e6baf6a37..bcebdb150 100644 --- a/src/common/cityhash.h +++ b/src/common/cityhash.h @@ -94,9 +94,6 @@ uint128 CityHash128(const char* s, size_t len); // hashed into the result. uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed); -// Hash function for a byte array. Most useful in 32-bit binaries. -uint32_t CityHash32(const char* buf, size_t len); - // 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) { From 3e456cd3fe89d8fd32b9a6fcfeae261f8f5db9ab Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 15 Jan 2018 01:46:50 -0800 Subject: [PATCH 37/42] Common: Fix 2 implicit conversion warnings in CityHash --- src/common/cityhash.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp index 596b943e7..d6f599a29 100644 --- a/src/common/cityhash.cpp +++ b/src/common/cityhash.cpp @@ -179,7 +179,7 @@ static uint64 HashLen0to16(const char* s, size_t len) { uint8 b = s[len >> 1]; uint8 c = s[len - 1]; uint32 y = static_cast(a) + (static_cast(b) << 8); - uint32 z = len + (static_cast(c) << 2); + uint32 z = static_cast(len) + (static_cast(c) << 2); return ShiftMix(y * k2 ^ z * k0) * k2; } return k2; @@ -290,7 +290,7 @@ static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { uint64 b = Uint128High64(seed); uint64 c = 0; uint64 d = 0; - signed long l = len - 16; + signed long l = static_cast(len) - 16; if (l <= 0) { // len <= 16 a = ShiftMix(a * k1) * k1; c = b * k1 + HashLen0to16(s, len); From 3a001c41bb7b6680b96641c51cf48a58064ed5ba Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Mon, 15 Jan 2018 02:20:25 -0800 Subject: [PATCH 38/42] Common: Use common swap.h macros in CityHash --- src/common/cityhash.cpp | 56 ++++------------------------------------- 1 file changed, 5 insertions(+), 51 deletions(-) diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp index d6f599a29..de31ffbd8 100644 --- a/src/common/cityhash.cpp +++ b/src/common/cityhash.cpp @@ -60,55 +60,9 @@ static uint32 UNALIGNED_LOAD32(const char* p) { return result; } -#ifdef _MSC_VER - -#include -#define bswap_32(x) _byteswap_ulong(x) -#define bswap_64(x) _byteswap_uint64(x) - -#elif defined(__APPLE__) - -// Mac OS X / Darwin features -#include -#define bswap_32(x) OSSwapInt32(x) -#define bswap_64(x) OSSwapInt64(x) - -#elif defined(__sun) || defined(sun) - -#include -#define bswap_32(x) BSWAP_32(x) -#define bswap_64(x) BSWAP_64(x) - -#elif defined(__FreeBSD__) - -#include -#define bswap_32(x) bswap32(x) -#define bswap_64(x) bswap64(x) - -#elif defined(__OpenBSD__) - -#include -#define bswap_32(x) swap32(x) -#define bswap_64(x) swap64(x) - -#elif defined(__NetBSD__) - -#include -#include -#if defined(__BSWAP_RENAME) && !defined(__bswap_32) -#define bswap_32(x) bswap32(x) -#define bswap_64(x) bswap64(x) -#endif - -#else - -#include - -#endif - #ifdef WORDS_BIGENDIAN -#define uint32_in_expected_order(x) (bswap_32(x)) -#define uint64_in_expected_order(x) (bswap_64(x)) +#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) @@ -228,11 +182,11 @@ static uint64 HashLen33to64(const char* s, size_t len) { 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 = bswap_64((u + v) * mul) + h; + uint64 w = swap64((u + v) * mul) + h; uint64 x = Rotate(e + f, 42) + c; - uint64 y = (bswap_64((v + w) * mul) + g) * mul; + uint64 y = (swap64((v + w) * mul) + g) * mul; uint64 z = e + f + c; - a = bswap_64((x + z) * mul + y) + b; + a = swap64((x + z) * mul + y) + b; b = ShiftMix((z + a) * mul + d + h) * mul; return b + x; } From e3f90ff6b6c54f45101f06e354553e0645416068 Mon Sep 17 00:00:00 2001 From: CDAGaming Date: Sun, 31 Dec 2017 11:53:41 -0600 Subject: [PATCH 39/42] Travis Quick Fix + Minor Submodule Updates ## Info (Edited) This commit fixes several Travis-Related Warnings/Errors/Deprecations in MacOS(Now running on Sierra instead of El Capitan). These Issues included Path Not Set Warnings, shell_session warnings, and a few others. This Commit also Updates the following Submodules: - enet (Includes Minor CMakeList Changes for cleaner Solutions) - Nihstro (Includes a bugfix on compiling with Boost v1.66.0) --- .travis.yml | 2 +- externals/enet | 2 +- externals/nihstro | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9624d4483..2846a42fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ matrix: - os: osx env: NAME="macos build" sudo: false - osx_image: xcode7.3 + osx_image: xcode9.2 install: "./.travis/macos/deps.sh" script: "./.travis/macos/build.sh" after_success: "./.travis/macos/upload.sh" diff --git a/externals/enet b/externals/enet index a84c120ef..39a72ab19 160000 --- a/externals/enet +++ b/externals/enet @@ -1 +1 @@ -Subproject commit a84c120eff13d2fa3eadb41ef7afe0f7819f4d6c +Subproject commit 39a72ab1990014eb399cee9d538fd529df99c6a0 diff --git a/externals/nihstro b/externals/nihstro index 7e24743af..28ac07a52 160000 --- a/externals/nihstro +++ b/externals/nihstro @@ -1 +1 @@ -Subproject commit 7e24743af21a7c2e3cef21ef174ae4269d0cfdac +Subproject commit 28ac07a5273b6250cfafb4630bee8da820ad33d7 From 51169de364c1d24a0e1baf11335ffebf85cbe5ef Mon Sep 17 00:00:00 2001 From: CDAGaming Date: Mon, 15 Jan 2018 21:20:49 -0600 Subject: [PATCH 40/42] Update nihstro --- externals/nihstro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/nihstro b/externals/nihstro index 28ac07a52..fd69de1a1 160000 --- a/externals/nihstro +++ b/externals/nihstro @@ -1 +1 @@ -Subproject commit 28ac07a5273b6250cfafb4630bee8da820ad33d7 +Subproject commit fd69de1a1b960ec296cc67d32257b0f9e2d89ac6 From 93cca23dd6f11c62ddf6976bd5916d1d7244cf8f Mon Sep 17 00:00:00 2001 From: Adityarup Laha <30696515+adityaruplaha@users.noreply.github.com> Date: Thu, 18 Jan 2018 04:00:50 +0530 Subject: [PATCH 41/42] Variable tilt clamp (#3366) * Add tilt clamp setting * Updated `clamp` to use variable * Fixed a minor typo * Include possibly necessary header * expose setting in sdl2 * expose setting in qt * incorporate @wwylele 's suggestion in sdl2 * Incorporate @wwylele 's suggestion (?) Please review the code, this is the first time I'm working with `ParamPackage`s so I may just screw up the code. * Forgot to change in qt * Fixed an fatal error * fixed clang format error * remove the old setting * fixed typos cusing errors * removed old setting * Changed init style * Forgot this one * Removed unnecessary header inclusion * Update config.cpp * update qt-config * Update motion_emu.cpp * Update motion_emu.cpp --- src/citra/config.cpp | 5 +++-- src/citra_qt/configuration/config.cpp | 4 +++- src/input_common/motion_emu.cpp | 16 ++++++++++------ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 45c28ad09..0a84887d9 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -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"); diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 97993e426..d96405110 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -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 = diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp index 59a035e70..27c81e73b 100644 --- a/src/input_common/motion_emu.cpp +++ b/src/input_common/motion_emu.cpp @@ -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::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(); 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 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(update_millisecond, sensitivity); + MotionEmuDeviceWrapper(int update_millisecond, float sensitivity, float tilt_clamp) { + device = std::make_shared(update_millisecond, sensitivity, tilt_clamp); } std::tuple, Math::Vec3> GetStatus() const { @@ -140,7 +142,9 @@ public: std::unique_ptr 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(update_period, sensitivity); + float tilt_clamp = params.Get("tilt_clamp", 90.0f); + auto device_wrapper = + std::make_unique(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; From f2fb8308599f81d163e3a41e9cddbb1cd6f4e961 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Wed, 17 Jan 2018 20:52:40 -0500 Subject: [PATCH 42/42] externals: Update catch to 2.1.0 Keeps the unit testing library up to date. --- externals/catch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/catch b/externals/catch index 19ab2117c..cd76f5730 160000 --- a/externals/catch +++ b/externals/catch @@ -1 +1 @@ -Subproject commit 19ab2117c5bac2f376f8da4a4b25e183137bcec0 +Subproject commit cd76f5730c9a3afa19f3b9c83608d9c7ab325a19