diff options
65 files changed, 809 insertions, 338 deletions
diff --git a/.travis.descriptor.json.in b/.travis.descriptor.json.in deleted file mode 100644 index fac2eee93..000000000 --- a/.travis.descriptor.json.in +++ /dev/null | |||
| @@ -1,23 +0,0 @@ | |||
| 1 | { | ||
| 2 | "package": { | ||
| 3 | "name": "nightly", | ||
| 4 | "repo": "citra", | ||
| 5 | "subject": "citra-emu", | ||
| 6 | "desc": "Build created on @BUILD_DATE@", | ||
| 7 | "website_url": "www.citra-emu.org", | ||
| 8 | "public_download_numbers": false, | ||
| 9 | "public_stats": false | ||
| 10 | }, | ||
| 11 | "version": { | ||
| 12 | "name": "nightly-@GIT_REV@", | ||
| 13 | "released": "@BUILD_DATE@", | ||
| 14 | "gpgSign": false | ||
| 15 | }, | ||
| 16 | "files": [ | ||
| 17 | { | ||
| 18 | "includePattern": "artifacts/(.*\\.tar.xz)", | ||
| 19 | "uploadPattern": "$1" | ||
| 20 | } | ||
| 21 | ], | ||
| 22 | "publish": true | ||
| 23 | } | ||
diff --git a/.travis.yml b/.travis.yml index 414168997..cf1e1e26c 100644 --- a/.travis.yml +++ b/.travis.yml | |||
| @@ -29,8 +29,11 @@ script: "./.travis-build.sh" | |||
| 29 | after_success: "./.travis-upload.sh" | 29 | after_success: "./.travis-upload.sh" |
| 30 | 30 | ||
| 31 | deploy: | 31 | deploy: |
| 32 | provider: bintray | 32 | provider: releases |
| 33 | file: "${HOME}/build/${TRAVIS_REPO_SLUG}/.travis.descriptor.json" | 33 | api_key: |
| 34 | user: citra-emu | 34 | secure: Mck15DIWaJdxDiS3aYVlM9N3G6y8VKUI1rnwII7/iolfm1s94U+tgvbheZDmT7SSbFyaGaYO/E8HrV/uZR9Vvs7ev20sHsTN1u60OTWfDIIyHs9SqjhcGbtq95m9/dMFschOYqTOR+gAs5BsxjuoeAotHdhpQEwvkO2oo5oR0zhGy45gjFnVvtcxT/IfpZBIpVgcK3aLb9zT6ekcJbSiPmEB15iLq3xXd0nFUNtEZdX3D6Veye4n5jB6n72qN8JVoKvPZAwaC2K0pZxpcGJaXDchLsw1q+4eCvdz6UJfUemeQ/uMAmjfeQ3wrzYGXe3nCM3WmX5wosCsB0mw4zYatzl3si6CZ1W+0GkV4Rwlx03dfp7v3EeFhTsXYCaXqhwuLZnWOLUik8t9vaSoFUx4nUIRwfO9kAMUJQSpLuHNO2nT01s3GxvqxzczuLQ9he5nGSi0RRodUzDwek1qUp6I4uV3gRHKz4B07YIc1i2fK88NLXjyQ0uLVZ+7Oq1+kgDp6+N7vvXXZ5qZ17tdaysSbKEE0Y8zsoXw7Rk1tPN19vrCS+TSpomNMyQyne1k+I5iZ/qkxPTLAS5qI6Utc2dL3GJdxWRAEfGNO9AIX3GV/jmmKfdcvwGsCYP8hxqs5vLYfgacw3D8NLf1941lQUwavC17jm9EV9g5G3Pn1Cp516E= |
| 35 | key: | 35 | file_glob: true |
| 36 | secure: "Hdj+/UyDYbvOOXJdlrV0n//t6tAnUqMEy4+Ppb84aF+na4B2hoc+RQ6rfbVXnFE0YKAKJme0J00GVi4JcXViOil2P/wHWxniWBlpz1vHLPT15+p1qi8cPQ0yKMAq9+1kYe6APkHjjsZxHAR6cf1fckI/rPfx/lyuDQOpVq07pJg=" \ No newline at end of file | 36 | file: "artifacts/*.tar.xz" |
| 37 | skip_cleanup: true | ||
| 38 | on: | ||
| 39 | repo: citra-emu/citra-nightly \ No newline at end of file | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index e232d99a4..52a1fd492 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -253,11 +253,6 @@ get_git_head_revision(GIT_REF_SPEC GIT_REV) | |||
| 253 | git_describe(GIT_DESC --always --long --dirty) | 253 | git_describe(GIT_DESC --always --long --dirty) |
| 254 | git_branch_name(GIT_BRANCH) | 254 | git_branch_name(GIT_BRANCH) |
| 255 | 255 | ||
| 256 | # configure the bintray travis release json | ||
| 257 | string(TIMESTAMP BUILD_DATE "%Y-%m-%d") | ||
| 258 | configure_file("${CMAKE_SOURCE_DIR}/.travis.descriptor.json.in" "${CMAKE_SOURCE_DIR}/.travis.descriptor.json" @ONLY) | ||
| 259 | unset(BUILD_DATE) | ||
| 260 | |||
| 261 | set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih") | 256 | set(INI_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/externals/inih") |
| 262 | include_directories(${INI_PREFIX}) | 257 | include_directories(${INI_PREFIX}) |
| 263 | add_subdirectory(${INI_PREFIX}) | 258 | add_subdirectory(${INI_PREFIX}) |
diff --git a/appveyor.yml b/appveyor.yml index e4e1045de..c07559479 100644 --- a/appveyor.yml +++ b/appveyor.yml | |||
| @@ -42,7 +42,7 @@ after_build: | |||
| 42 | # set the build names as env vars so the artifacts can upload them | 42 | # set the build names as env vars so the artifacts can upload them |
| 43 | $env:MSVC_BUILD_NAME = $MSVC_BUILD_NAME | 43 | $env:MSVC_BUILD_NAME = $MSVC_BUILD_NAME |
| 44 | $env:MSVC_BUILD_PDB = $MSVC_BUILD_PDB | 44 | $env:MSVC_BUILD_PDB = $MSVC_BUILD_PDB |
| 45 | $env:BINTRAY_VERSION = $BINTRAY_VERSION | 45 | $env:GITREV = $GITREV |
| 46 | 46 | ||
| 47 | 7z a -tzip $MSVC_BUILD_PDB .\build\bin\release\*.pdb | 47 | 7z a -tzip $MSVC_BUILD_PDB .\build\bin\release\*.pdb |
| 48 | rm .\build\bin\release\*.pdb | 48 | rm .\build\bin\release\*.pdb |
| @@ -60,14 +60,17 @@ artifacts: | |||
| 60 | type: zip | 60 | type: zip |
| 61 | 61 | ||
| 62 | deploy: | 62 | deploy: |
| 63 | - provider: BinTray | 63 | provider: GitHub |
| 64 | username: citra-emu | 64 | release: nightly-$(appveyor_build_number) |
| 65 | api_key: | 65 | description: | |
| 66 | secure: qaZZfXp5Vv9xw5EmCxKLhGnNYhiNMDCdP84l0+h6HetfmvFlvHxyohWEVokLjl7d | 66 | Citra nightly releases. Please choose the correct download for your operating system from the list below. |
| 67 | subject: citra-emu | 67 | |
| 68 | repo: citra | 68 | Short Commit Hash $(GITREV) |
| 69 | package: nightly | 69 | auth_token: |
| 70 | version: $(BINTRAY_VERSION) | 70 | secure: "dbpsMC/MgPKWFNJCXpQl4cR8FYhepkPLjgNp/pRMktZ8oLKTqPYErfreaIxb/4P1" |
| 71 | publish: true | 71 | artifact: msvcbuild |
| 72 | draft: false | ||
| 73 | prerelease: false | ||
| 72 | on: | 74 | on: |
| 73 | branch: master | 75 | branch: master |
| 76 | appveyor_repo_name: citra-emu/citra-nightly | ||
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 98f093258..bd8ac563b 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp | |||
| @@ -63,8 +63,7 @@ void Config::ReadValues() { | |||
| 63 | // Renderer | 63 | // Renderer |
| 64 | Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); | 64 | Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", true); |
| 65 | Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); | 65 | Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true); |
| 66 | Settings::values.use_scaled_resolution = | 66 | Settings::values.resolution_factor = sdl2_config->GetReal("Renderer", "resolution_factor", 1.0); |
| 67 | sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); | ||
| 68 | Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); | 67 | Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); |
| 69 | Settings::values.toggle_framelimit = | 68 | Settings::values.toggle_framelimit = |
| 70 | sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); | 69 | sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); |
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index bb4720d25..7996813b4 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h | |||
| @@ -51,9 +51,10 @@ use_hw_renderer = | |||
| 51 | # 0: Interpreter (slow), 1 (default): JIT (fast) | 51 | # 0: Interpreter (slow), 1 (default): JIT (fast) |
| 52 | use_shader_jit = | 52 | use_shader_jit = |
| 53 | 53 | ||
| 54 | # Whether to use native 3DS screen resolution or to scale rendering resolution to the displayed screen size. | 54 | # Resolution scale factor |
| 55 | # 0 (default): Native, 1: Scaled | 55 | # 0: Auto (scales resolution to window size), 1: Native 3DS screen resolution, Otherwise a scale |
| 56 | use_scaled_resolution = | 56 | # factor for the 3DS resolution |
| 57 | resolution_factor = | ||
| 57 | 58 | ||
| 58 | # Whether to enable V-Sync (caps the framerate at 60FPS) or not. | 59 | # Whether to enable V-Sync (caps the framerate at 60FPS) or not. |
| 59 | # 0 (default): Off, 1: On | 60 | # 0 (default): Off, 1: On |
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index b0d82b670..81a3abe3f 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp | |||
| @@ -19,16 +19,22 @@ | |||
| 19 | 19 | ||
| 20 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | 20 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { |
| 21 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); | 21 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); |
| 22 | motion_emu->Tilt(x, y); | ||
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { | 25 | void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { |
| 25 | if (button != SDL_BUTTON_LEFT) | 26 | if (button == SDL_BUTTON_LEFT) { |
| 26 | return; | 27 | if (state == SDL_PRESSED) { |
| 27 | 28 | TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); | |
| 28 | if (state == SDL_PRESSED) { | 29 | } else { |
| 29 | TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); | 30 | TouchReleased(); |
| 30 | } else { | 31 | } |
| 31 | TouchReleased(); | 32 | } else if (button == SDL_BUTTON_RIGHT) { |
| 33 | if (state == SDL_PRESSED) { | ||
| 34 | motion_emu->BeginTilt(x, y); | ||
| 35 | } else { | ||
| 36 | motion_emu->EndTilt(); | ||
| 37 | } | ||
| 32 | } | 38 | } |
| 33 | } | 39 | } |
| 34 | 40 | ||
| @@ -54,6 +60,7 @@ EmuWindow_SDL2::EmuWindow_SDL2() { | |||
| 54 | keyboard_id = KeyMap::NewDeviceId(); | 60 | keyboard_id = KeyMap::NewDeviceId(); |
| 55 | 61 | ||
| 56 | ReloadSetKeymaps(); | 62 | ReloadSetKeymaps(); |
| 63 | motion_emu = std::make_unique<Motion::MotionEmu>(*this); | ||
| 57 | 64 | ||
| 58 | SDL_SetMainReady(); | 65 | SDL_SetMainReady(); |
| 59 | 66 | ||
| @@ -109,6 +116,7 @@ EmuWindow_SDL2::EmuWindow_SDL2() { | |||
| 109 | EmuWindow_SDL2::~EmuWindow_SDL2() { | 116 | EmuWindow_SDL2::~EmuWindow_SDL2() { |
| 110 | SDL_GL_DeleteContext(gl_context); | 117 | SDL_GL_DeleteContext(gl_context); |
| 111 | SDL_Quit(); | 118 | SDL_Quit(); |
| 119 | motion_emu = nullptr; | ||
| 112 | } | 120 | } |
| 113 | 121 | ||
| 114 | void EmuWindow_SDL2::SwapBuffers() { | 122 | void EmuWindow_SDL2::SwapBuffers() { |
diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h index c8cd919c6..b1cbf16d7 100644 --- a/src/citra/emu_window/emu_window_sdl2.h +++ b/src/citra/emu_window/emu_window_sdl2.h | |||
| @@ -4,8 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 7 | #include <utility> | 8 | #include <utility> |
| 8 | #include "core/frontend/emu_window.h" | 9 | #include "core/frontend/emu_window.h" |
| 10 | #include "core/frontend/motion_emu.h" | ||
| 9 | 11 | ||
| 10 | struct SDL_Window; | 12 | struct SDL_Window; |
| 11 | 13 | ||
| @@ -61,4 +63,7 @@ private: | |||
| 61 | 63 | ||
| 62 | /// Device id of keyboard for use with KeyMap | 64 | /// Device id of keyboard for use with KeyMap |
| 63 | int keyboard_id; | 65 | int keyboard_id; |
| 66 | |||
| 67 | /// Motion sensors emulation | ||
| 68 | std::unique_ptr<Motion::MotionEmu> motion_emu; | ||
| 64 | }; | 69 | }; |
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 57fde6caa..948db384d 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp | |||
| @@ -99,7 +99,7 @@ private: | |||
| 99 | }; | 99 | }; |
| 100 | 100 | ||
| 101 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 101 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) |
| 102 | : QWidget(parent), keyboard_id(0), emu_thread(emu_thread), child(nullptr) { | 102 | : QWidget(parent), child(nullptr), keyboard_id(0), emu_thread(emu_thread) { |
| 103 | 103 | ||
| 104 | std::string window_title = | 104 | std::string window_title = |
| 105 | Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); | 105 | Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc); |
| @@ -191,6 +191,7 @@ qreal GRenderWindow::windowPixelRatio() { | |||
| 191 | } | 191 | } |
| 192 | 192 | ||
| 193 | void GRenderWindow::closeEvent(QCloseEvent* event) { | 193 | void GRenderWindow::closeEvent(QCloseEvent* event) { |
| 194 | motion_emu = nullptr; | ||
| 194 | emit Closed(); | 195 | emit Closed(); |
| 195 | QWidget::closeEvent(event); | 196 | QWidget::closeEvent(event); |
| 196 | } | 197 | } |
| @@ -204,11 +205,13 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | |||
| 204 | } | 205 | } |
| 205 | 206 | ||
| 206 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | 207 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { |
| 208 | auto pos = event->pos(); | ||
| 207 | if (event->button() == Qt::LeftButton) { | 209 | if (event->button() == Qt::LeftButton) { |
| 208 | auto pos = event->pos(); | ||
| 209 | qreal pixelRatio = windowPixelRatio(); | 210 | qreal pixelRatio = windowPixelRatio(); |
| 210 | this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), | 211 | this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), |
| 211 | static_cast<unsigned>(pos.y() * pixelRatio)); | 212 | static_cast<unsigned>(pos.y() * pixelRatio)); |
| 213 | } else if (event->button() == Qt::RightButton) { | ||
| 214 | motion_emu->BeginTilt(pos.x(), pos.y()); | ||
| 212 | } | 215 | } |
| 213 | } | 216 | } |
| 214 | 217 | ||
| @@ -217,11 +220,14 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | |||
| 217 | qreal pixelRatio = windowPixelRatio(); | 220 | qreal pixelRatio = windowPixelRatio(); |
| 218 | this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), | 221 | this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), |
| 219 | std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); | 222 | std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); |
| 223 | motion_emu->Tilt(pos.x(), pos.y()); | ||
| 220 | } | 224 | } |
| 221 | 225 | ||
| 222 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | 226 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { |
| 223 | if (event->button() == Qt::LeftButton) | 227 | if (event->button() == Qt::LeftButton) |
| 224 | this->TouchReleased(); | 228 | this->TouchReleased(); |
| 229 | else if (event->button() == Qt::RightButton) | ||
| 230 | motion_emu->EndTilt(); | ||
| 225 | } | 231 | } |
| 226 | 232 | ||
| 227 | void GRenderWindow::ReloadSetKeymaps() { | 233 | void GRenderWindow::ReloadSetKeymaps() { |
| @@ -279,11 +285,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest( | |||
| 279 | } | 285 | } |
| 280 | 286 | ||
| 281 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { | 287 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { |
| 288 | motion_emu = std::make_unique<Motion::MotionEmu>(*this); | ||
| 282 | this->emu_thread = emu_thread; | 289 | this->emu_thread = emu_thread; |
| 283 | child->DisablePainting(); | 290 | child->DisablePainting(); |
| 284 | } | 291 | } |
| 285 | 292 | ||
| 286 | void GRenderWindow::OnEmulationStopping() { | 293 | void GRenderWindow::OnEmulationStopping() { |
| 294 | motion_emu = nullptr; | ||
| 287 | emu_thread = nullptr; | 295 | emu_thread = nullptr; |
| 288 | child->EnablePainting(); | 296 | child->EnablePainting(); |
| 289 | } | 297 | } |
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 43015390b..7dac1c480 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <QThread> | 11 | #include <QThread> |
| 12 | #include "common/thread.h" | 12 | #include "common/thread.h" |
| 13 | #include "core/frontend/emu_window.h" | 13 | #include "core/frontend/emu_window.h" |
| 14 | #include "core/frontend/motion_emu.h" | ||
| 14 | 15 | ||
| 15 | class QKeyEvent; | 16 | class QKeyEvent; |
| 16 | class QScreen; | 17 | class QScreen; |
| @@ -156,6 +157,9 @@ private: | |||
| 156 | 157 | ||
| 157 | EmuThread* emu_thread; | 158 | EmuThread* emu_thread; |
| 158 | 159 | ||
| 160 | /// Motion sensors emulation | ||
| 161 | std::unique_ptr<Motion::MotionEmu> motion_emu; | ||
| 162 | |||
| 159 | protected: | 163 | protected: |
| 160 | void showEvent(QShowEvent* event) override; | 164 | void showEvent(QShowEvent* event) override; |
| 161 | }; | 165 | }; |
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index c904c4b00..8021667d0 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp | |||
| @@ -44,8 +44,7 @@ void Config::ReadValues() { | |||
| 44 | qt_config->beginGroup("Renderer"); | 44 | qt_config->beginGroup("Renderer"); |
| 45 | Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool(); | 45 | Settings::values.use_hw_renderer = qt_config->value("use_hw_renderer", true).toBool(); |
| 46 | Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool(); | 46 | Settings::values.use_shader_jit = qt_config->value("use_shader_jit", true).toBool(); |
| 47 | Settings::values.use_scaled_resolution = | 47 | Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat(); |
| 48 | qt_config->value("use_scaled_resolution", false).toBool(); | ||
| 49 | Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); | 48 | Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); |
| 50 | Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); | 49 | Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); |
| 51 | 50 | ||
| @@ -152,7 +151,7 @@ void Config::SaveValues() { | |||
| 152 | qt_config->beginGroup("Renderer"); | 151 | qt_config->beginGroup("Renderer"); |
| 153 | qt_config->setValue("use_hw_renderer", Settings::values.use_hw_renderer); | 152 | qt_config->setValue("use_hw_renderer", Settings::values.use_hw_renderer); |
| 154 | qt_config->setValue("use_shader_jit", Settings::values.use_shader_jit); | 153 | qt_config->setValue("use_shader_jit", Settings::values.use_shader_jit); |
| 155 | qt_config->setValue("use_scaled_resolution", Settings::values.use_scaled_resolution); | 154 | qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor); |
| 156 | qt_config->setValue("use_vsync", Settings::values.use_vsync); | 155 | qt_config->setValue("use_vsync", Settings::values.use_vsync); |
| 157 | qt_config->setValue("toggle_framelimit", Settings::values.toggle_framelimit); | 156 | qt_config->setValue("toggle_framelimit", Settings::values.toggle_framelimit); |
| 158 | 157 | ||
diff --git a/src/citra_qt/configure_graphics.cpp b/src/citra_qt/configure_graphics.cpp index cea7db388..54f799b47 100644 --- a/src/citra_qt/configure_graphics.cpp +++ b/src/citra_qt/configure_graphics.cpp | |||
| @@ -18,10 +18,81 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | |||
| 18 | 18 | ||
| 19 | ConfigureGraphics::~ConfigureGraphics() {} | 19 | ConfigureGraphics::~ConfigureGraphics() {} |
| 20 | 20 | ||
| 21 | enum class Resolution : int { | ||
| 22 | Auto, | ||
| 23 | Scale1x, | ||
| 24 | Scale2x, | ||
| 25 | Scale3x, | ||
| 26 | Scale4x, | ||
| 27 | Scale5x, | ||
| 28 | Scale6x, | ||
| 29 | Scale7x, | ||
| 30 | Scale8x, | ||
| 31 | Scale9x, | ||
| 32 | Scale10x, | ||
| 33 | }; | ||
| 34 | |||
| 35 | float ToResolutionFactor(Resolution option) { | ||
| 36 | switch (option) { | ||
| 37 | case Resolution::Auto: | ||
| 38 | return 0.f; | ||
| 39 | case Resolution::Scale1x: | ||
| 40 | return 1.f; | ||
| 41 | case Resolution::Scale2x: | ||
| 42 | return 2.f; | ||
| 43 | case Resolution::Scale3x: | ||
| 44 | return 3.f; | ||
| 45 | case Resolution::Scale4x: | ||
| 46 | return 4.f; | ||
| 47 | case Resolution::Scale5x: | ||
| 48 | return 5.f; | ||
| 49 | case Resolution::Scale6x: | ||
| 50 | return 6.f; | ||
| 51 | case Resolution::Scale7x: | ||
| 52 | return 7.f; | ||
| 53 | case Resolution::Scale8x: | ||
| 54 | return 8.f; | ||
| 55 | case Resolution::Scale9x: | ||
| 56 | return 9.f; | ||
| 57 | case Resolution::Scale10x: | ||
| 58 | return 10.f; | ||
| 59 | } | ||
| 60 | return 0.f; | ||
| 61 | } | ||
| 62 | |||
| 63 | Resolution FromResolutionFactor(float factor) { | ||
| 64 | if (factor == 0.f) { | ||
| 65 | return Resolution::Auto; | ||
| 66 | } else if (factor == 1.f) { | ||
| 67 | return Resolution::Scale1x; | ||
| 68 | } else if (factor == 2.f) { | ||
| 69 | return Resolution::Scale2x; | ||
| 70 | } else if (factor == 3.f) { | ||
| 71 | return Resolution::Scale3x; | ||
| 72 | } else if (factor == 4.f) { | ||
| 73 | return Resolution::Scale4x; | ||
| 74 | } else if (factor == 5.f) { | ||
| 75 | return Resolution::Scale5x; | ||
| 76 | } else if (factor == 6.f) { | ||
| 77 | return Resolution::Scale6x; | ||
| 78 | } else if (factor == 7.f) { | ||
| 79 | return Resolution::Scale7x; | ||
| 80 | } else if (factor == 8.f) { | ||
| 81 | return Resolution::Scale8x; | ||
| 82 | } else if (factor == 9.f) { | ||
| 83 | return Resolution::Scale9x; | ||
| 84 | } else if (factor == 10.f) { | ||
| 85 | return Resolution::Scale10x; | ||
| 86 | } | ||
| 87 | return Resolution::Auto; | ||
| 88 | } | ||
| 89 | |||
| 21 | void ConfigureGraphics::setConfiguration() { | 90 | void ConfigureGraphics::setConfiguration() { |
| 22 | ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer); | 91 | ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer); |
| 92 | ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer); | ||
| 23 | ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); | 93 | ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); |
| 24 | ui->toggle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution); | 94 | ui->resolution_factor_combobox->setCurrentIndex( |
| 95 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); | ||
| 25 | ui->toggle_vsync->setChecked(Settings::values.use_vsync); | 96 | ui->toggle_vsync->setChecked(Settings::values.use_vsync); |
| 26 | ui->toggle_framelimit->setChecked(Settings::values.toggle_framelimit); | 97 | ui->toggle_framelimit->setChecked(Settings::values.toggle_framelimit); |
| 27 | ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option)); | 98 | ui->layout_combobox->setCurrentIndex(static_cast<int>(Settings::values.layout_option)); |
| @@ -31,7 +102,8 @@ void ConfigureGraphics::setConfiguration() { | |||
| 31 | void ConfigureGraphics::applyConfiguration() { | 102 | void ConfigureGraphics::applyConfiguration() { |
| 32 | Settings::values.use_hw_renderer = ui->toggle_hw_renderer->isChecked(); | 103 | Settings::values.use_hw_renderer = ui->toggle_hw_renderer->isChecked(); |
| 33 | Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); | 104 | Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); |
| 34 | Settings::values.use_scaled_resolution = ui->toggle_scaled_resolution->isChecked(); | 105 | Settings::values.resolution_factor = |
| 106 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); | ||
| 35 | Settings::values.use_vsync = ui->toggle_vsync->isChecked(); | 107 | Settings::values.use_vsync = ui->toggle_vsync->isChecked(); |
| 36 | Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked(); | 108 | Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked(); |
| 37 | Settings::values.layout_option = | 109 | Settings::values.layout_option = |
diff --git a/src/citra_qt/configure_graphics.ui b/src/citra_qt/configure_graphics.ui index 964aa0bbd..62021fe22 100644 --- a/src/citra_qt/configure_graphics.ui +++ b/src/citra_qt/configure_graphics.ui | |||
| @@ -37,13 +37,6 @@ | |||
| 37 | </widget> | 37 | </widget> |
| 38 | </item> | 38 | </item> |
| 39 | <item> | 39 | <item> |
| 40 | <widget class="QCheckBox" name="toggle_scaled_resolution"> | ||
| 41 | <property name="text"> | ||
| 42 | <string>Enable scaled resolution</string> | ||
| 43 | </property> | ||
| 44 | </widget> | ||
| 45 | </item> | ||
| 46 | <item> | ||
| 47 | <widget class="QCheckBox" name="toggle_vsync"> | 40 | <widget class="QCheckBox" name="toggle_vsync"> |
| 48 | <property name="text"> | 41 | <property name="text"> |
| 49 | <string>Enable V-Sync</string> | 42 | <string>Enable V-Sync</string> |
| @@ -57,6 +50,76 @@ | |||
| 57 | </property> | 50 | </property> |
| 58 | </widget> | 51 | </widget> |
| 59 | </item> | 52 | </item> |
| 53 | <item> | ||
| 54 | <layout class="QHBoxLayout" name="horizontalLayout"> | ||
| 55 | <item> | ||
| 56 | <widget class="QLabel" name="label"> | ||
| 57 | <property name="text"> | ||
| 58 | <string>Internal Resolution:</string> | ||
| 59 | </property> | ||
| 60 | </widget> | ||
| 61 | </item> | ||
| 62 | <item> | ||
| 63 | <widget class="QComboBox" name="resolution_factor_combobox"> | ||
| 64 | <item> | ||
| 65 | <property name="text"> | ||
| 66 | <string notr="true">Auto (Window Size)</string> | ||
| 67 | </property> | ||
| 68 | </item> | ||
| 69 | <item> | ||
| 70 | <property name="text"> | ||
| 71 | <string notr="true">Native (400x240)</string> | ||
| 72 | </property> | ||
| 73 | </item> | ||
| 74 | <item> | ||
| 75 | <property name="text"> | ||
| 76 | <string notr="true">2x Native (800x480)</string> | ||
| 77 | </property> | ||
| 78 | </item> | ||
| 79 | <item> | ||
| 80 | <property name="text"> | ||
| 81 | <string notr="true">3x Native (1200x720)</string> | ||
| 82 | </property> | ||
| 83 | </item> | ||
| 84 | <item> | ||
| 85 | <property name="text"> | ||
| 86 | <string notr="true">4x Native (1600x960)</string> | ||
| 87 | </property> | ||
| 88 | </item> | ||
| 89 | <item> | ||
| 90 | <property name="text"> | ||
| 91 | <string notr="true">5x Native (2000x1200)</string> | ||
| 92 | </property> | ||
| 93 | </item> | ||
| 94 | <item> | ||
| 95 | <property name="text"> | ||
| 96 | <string notr="true">6x Native (2400x1440)</string> | ||
| 97 | </property> | ||
| 98 | </item> | ||
| 99 | <item> | ||
| 100 | <property name="text"> | ||
| 101 | <string notr="true">7x Native (2800x1680)</string> | ||
| 102 | </property> | ||
| 103 | </item> | ||
| 104 | <item> | ||
| 105 | <property name="text"> | ||
| 106 | <string notr="true">8x Native (3200x1920)</string> | ||
| 107 | </property> | ||
| 108 | </item> | ||
| 109 | <item> | ||
| 110 | <property name="text"> | ||
| 111 | <string notr="true">9x Native (3600x2160)</string> | ||
| 112 | </property> | ||
| 113 | </item> | ||
| 114 | <item> | ||
| 115 | <property name="text"> | ||
| 116 | <string notr="true">10x Native (4000x2400)</string> | ||
| 117 | </property> | ||
| 118 | </item> | ||
| 119 | </widget> | ||
| 120 | </item> | ||
| 121 | </layout> | ||
| 122 | </item> | ||
| 60 | </layout> | 123 | </layout> |
| 61 | </widget> | 124 | </widget> |
| 62 | </item> | 125 | </item> |
| @@ -128,5 +191,12 @@ | |||
| 128 | </layout> | 191 | </layout> |
| 129 | </widget> | 192 | </widget> |
| 130 | <resources/> | 193 | <resources/> |
| 131 | <connections/> | 194 | <connections> |
| 195 | <connection> | ||
| 196 | <sender>toggle_hw_renderer</sender> | ||
| 197 | <signal>toggled(bool)</signal> | ||
| 198 | <receiver>resolution_factor_combobox</receiver> | ||
| 199 | <slot>setEnabled(bool)</slot> | ||
| 200 | </connection> | ||
| 201 | </connections> | ||
| 132 | </ui> | 202 | </ui> |
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp index c1db93583..08d2e7a22 100644 --- a/src/citra_qt/debugger/callstack.cpp +++ b/src/citra_qt/debugger/callstack.cpp | |||
| @@ -45,7 +45,6 @@ void CallstackWidget::OnDebugModeEntered() { | |||
| 45 | if (ARM_Disasm::Decode(insn) == OP_BL) { | 45 | if (ARM_Disasm::Decode(insn) == OP_BL) { |
| 46 | std::string name; | 46 | std::string name; |
| 47 | // ripped from disasm | 47 | // ripped from disasm |
| 48 | u8 cond = (insn >> 28) & 0xf; | ||
| 49 | u32 i_offset = insn & 0xffffff; | 48 | u32 i_offset = insn & 0xffffff; |
| 50 | // Sign-extend the 24-bit offset | 49 | // Sign-extend the 24-bit offset |
| 51 | if ((i_offset >> 23) & 1) | 50 | if ((i_offset >> 23) & 1) |
diff --git a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp index dab529e3a..f5a2ec761 100644 --- a/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics/graphics_cmdlists.cpp | |||
| @@ -135,11 +135,6 @@ void GPUCommandListWidget::OnCommandDoubleClicked(const QModelIndex& index) { | |||
| 135 | UNREACHABLE_MSG("Unknown texture command"); | 135 | UNREACHABLE_MSG("Unknown texture command"); |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | const auto texture = Pica::g_state.regs.GetTextures()[texture_index]; | ||
| 139 | const auto config = texture.config; | ||
| 140 | const auto format = texture.format; | ||
| 141 | const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config, format); | ||
| 142 | |||
| 143 | // TODO: Open a surface debugger | 138 | // TODO: Open a surface debugger |
| 144 | } | 139 | } |
| 145 | } | 140 | } |
diff --git a/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp index b75b94ef8..ff2e7e363 100644 --- a/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp +++ b/src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp | |||
| @@ -276,9 +276,6 @@ QVariant GraphicsVertexShaderModel::data(const QModelIndex& index, int role) con | |||
| 276 | output << 'b' << instr.flow_control.bool_uniform_id << ' '; | 276 | output << 'b' << instr.flow_control.bool_uniform_id << ' '; |
| 277 | } | 277 | } |
| 278 | 278 | ||
| 279 | u32 target_addr = instr.flow_control.dest_offset; | ||
| 280 | u32 target_addr_else = instr.flow_control.dest_offset; | ||
| 281 | |||
| 282 | if (opcode_info.subtype & OpCode::Info::HasAlternative) { | 279 | if (opcode_info.subtype & OpCode::Info::HasAlternative) { |
| 283 | output << "else jump to 0x" << std::setw(4) << std::right | 280 | output << "else jump to 0x" << std::setw(4) << std::right |
| 284 | << std::setfill('0') << std::hex | 281 | << std::setfill('0') << std::hex |
| @@ -473,7 +470,6 @@ GraphicsVertexShaderWidget::GraphicsVertexShaderWidget( | |||
| 473 | } | 470 | } |
| 474 | 471 | ||
| 475 | void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { | 472 | void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { |
| 476 | auto input = static_cast<Pica::Shader::InputVertex*>(data); | ||
| 477 | if (event == Pica::DebugContext::Event::VertexShaderInvocation) { | 473 | if (event == Pica::DebugContext::Event::VertexShaderInvocation) { |
| 478 | Reload(true, data); | 474 | Reload(true, data); |
| 479 | } else { | 475 | } else { |
diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp index 1d2de5185..b6ecf3819 100644 --- a/src/citra_qt/debugger/wait_tree.cpp +++ b/src/citra_qt/debugger/wait_tree.cpp | |||
| @@ -153,7 +153,8 @@ QString WaitTreeThread::GetText() const { | |||
| 153 | case THREADSTATUS_WAIT_SLEEP: | 153 | case THREADSTATUS_WAIT_SLEEP: |
| 154 | status = tr("sleeping"); | 154 | status = tr("sleeping"); |
| 155 | break; | 155 | break; |
| 156 | case THREADSTATUS_WAIT_SYNCH: | 156 | case THREADSTATUS_WAIT_SYNCH_ALL: |
| 157 | case THREADSTATUS_WAIT_SYNCH_ANY: | ||
| 157 | status = tr("waiting for objects"); | 158 | status = tr("waiting for objects"); |
| 158 | break; | 159 | break; |
| 159 | case THREADSTATUS_DORMANT: | 160 | case THREADSTATUS_DORMANT: |
| @@ -180,7 +181,8 @@ QColor WaitTreeThread::GetColor() const { | |||
| 180 | return QColor(Qt::GlobalColor::darkRed); | 181 | return QColor(Qt::GlobalColor::darkRed); |
| 181 | case THREADSTATUS_WAIT_SLEEP: | 182 | case THREADSTATUS_WAIT_SLEEP: |
| 182 | return QColor(Qt::GlobalColor::darkYellow); | 183 | return QColor(Qt::GlobalColor::darkYellow); |
| 183 | case THREADSTATUS_WAIT_SYNCH: | 184 | case THREADSTATUS_WAIT_SYNCH_ALL: |
| 185 | case THREADSTATUS_WAIT_SYNCH_ANY: | ||
| 184 | return QColor(Qt::GlobalColor::red); | 186 | return QColor(Qt::GlobalColor::red); |
| 185 | case THREADSTATUS_DORMANT: | 187 | case THREADSTATUS_DORMANT: |
| 186 | return QColor(Qt::GlobalColor::darkCyan); | 188 | return QColor(Qt::GlobalColor::darkCyan); |
| @@ -228,7 +230,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 228 | } else { | 230 | } else { |
| 229 | list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes)); | 231 | list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes)); |
| 230 | } | 232 | } |
| 231 | if (thread.status == THREADSTATUS_WAIT_SYNCH) { | 233 | if (thread.status == THREADSTATUS_WAIT_SYNCH_ANY || |
| 234 | thread.status == THREADSTATUS_WAIT_SYNCH_ALL) { | ||
| 232 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, | 235 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, |
| 233 | thread.IsSleepingOnWaitAll())); | 236 | thread.IsSleepingOnWaitAll())); |
| 234 | } | 237 | } |
diff --git a/src/citra_qt/util/spinbox.cpp b/src/citra_qt/util/spinbox.cpp index feb0ea1b3..212709007 100644 --- a/src/citra_qt/util/spinbox.cpp +++ b/src/citra_qt/util/spinbox.cpp | |||
| @@ -165,13 +165,6 @@ void CSpinBox::UpdateText() { | |||
| 165 | // Uppercase digits greater than 9. | 165 | // Uppercase digits greater than 9. |
| 166 | mask += ">"; | 166 | mask += ">"; |
| 167 | 167 | ||
| 168 | // The greatest signed 64-bit number has 19 decimal digits. | ||
| 169 | // TODO: Could probably make this more generic with some logarithms. | ||
| 170 | // For reference, unsigned 64-bit can have up to 20 decimal digits. | ||
| 171 | int digits = (num_digits != 0) | ||
| 172 | ? num_digits | ||
| 173 | : (base == 16) ? 16 : (base == 10) ? 19 : 0xFF; // fallback case... | ||
| 174 | |||
| 175 | // Match num_digits digits | 168 | // Match num_digits digits |
| 176 | // Digits irrelevant to the chosen number base are filtered in the validator | 169 | // Digits irrelevant to the chosen number base are filtered in the validator |
| 177 | mask += QString("H").repeated(std::max(num_digits, 1)); | 170 | mask += QString("H").repeated(std::max(num_digits, 1)); |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 5aecf6e6e..a7a4a688c 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -46,6 +46,7 @@ set(HEADERS | |||
| 46 | microprofileui.h | 46 | microprofileui.h |
| 47 | platform.h | 47 | platform.h |
| 48 | profiler_reporting.h | 48 | profiler_reporting.h |
| 49 | quaternion.h | ||
| 49 | scm_rev.h | 50 | scm_rev.h |
| 50 | scope_exit.h | 51 | scope_exit.h |
| 51 | string_util.h | 52 | string_util.h |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 3ea102229..2ef3e6b05 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -45,6 +45,7 @@ namespace Log { | |||
| 45 | SUB(Service, LDR) \ | 45 | SUB(Service, LDR) \ |
| 46 | SUB(Service, MIC) \ | 46 | SUB(Service, MIC) \ |
| 47 | SUB(Service, NDM) \ | 47 | SUB(Service, NDM) \ |
| 48 | SUB(Service, NFC) \ | ||
| 48 | SUB(Service, NIM) \ | 49 | SUB(Service, NIM) \ |
| 49 | SUB(Service, NWM) \ | 50 | SUB(Service, NWM) \ |
| 50 | SUB(Service, CAM) \ | 51 | SUB(Service, CAM) \ |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 9d8c18d8e..4330ef879 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -62,6 +62,7 @@ enum class Class : ClassType { | |||
| 62 | Service_LDR, ///< The LDR (3ds dll loader) service | 62 | Service_LDR, ///< The LDR (3ds dll loader) service |
| 63 | Service_MIC, ///< The MIC (Microphone) service | 63 | Service_MIC, ///< The MIC (Microphone) service |
| 64 | Service_NDM, ///< The NDM (Network daemon manager) service | 64 | Service_NDM, ///< The NDM (Network daemon manager) service |
| 65 | Service_NFC, ///< The NFC service | ||
| 65 | Service_NIM, ///< The NIM (Network interface manager) service | 66 | Service_NIM, ///< The NIM (Network interface manager) service |
| 66 | Service_NWM, ///< The NWM (Network wlan manager) service | 67 | Service_NWM, ///< The NWM (Network wlan manager) service |
| 67 | Service_CAM, ///< The CAM (Camera) service | 68 | Service_CAM, ///< The CAM (Camera) service |
diff --git a/src/common/math_util.h b/src/common/math_util.h index cdeaeb733..45a1ed367 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | 10 | ||
| 11 | namespace MathUtil { | 11 | namespace MathUtil { |
| 12 | 12 | ||
| 13 | static constexpr float PI = 3.14159265f; | ||
| 14 | |||
| 13 | inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, | 15 | inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, |
| 14 | unsigned length1) { | 16 | unsigned length1) { |
| 15 | return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1)); | 17 | return (std::max(start0, start1) < std::min(start0 + length0, start1 + length1)); |
diff --git a/src/common/quaternion.h b/src/common/quaternion.h new file mode 100644 index 000000000..84ac82ed3 --- /dev/null +++ b/src/common/quaternion.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/vector_math.h" | ||
| 8 | |||
| 9 | namespace Math { | ||
| 10 | |||
| 11 | template <typename T> | ||
| 12 | class Quaternion { | ||
| 13 | public: | ||
| 14 | Math::Vec3<T> xyz; | ||
| 15 | T w; | ||
| 16 | |||
| 17 | Quaternion<decltype(-T{})> Inverse() const { | ||
| 18 | return {-xyz, w}; | ||
| 19 | } | ||
| 20 | |||
| 21 | Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const { | ||
| 22 | return {xyz + other.xyz, w + other.w}; | ||
| 23 | } | ||
| 24 | |||
| 25 | Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const { | ||
| 26 | return {xyz - other.xyz, w - other.w}; | ||
| 27 | } | ||
| 28 | |||
| 29 | Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(const Quaternion& other) const { | ||
| 30 | return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz), | ||
| 31 | w * other.w - Dot(xyz, other.xyz)}; | ||
| 32 | } | ||
| 33 | }; | ||
| 34 | |||
| 35 | template <typename T> | ||
| 36 | auto QuaternionRotate(const Quaternion<T>& q, const Math::Vec3<T>& v) { | ||
| 37 | return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w); | ||
| 38 | } | ||
| 39 | |||
| 40 | inline Quaternion<float> MakeQuaternion(const Math::Vec3<float>& axis, float angle) { | ||
| 41 | return {axis * std::sin(angle / 2), std::cos(angle / 2)}; | ||
| 42 | } | ||
| 43 | |||
| 44 | } // namspace Math | ||
diff --git a/src/common/thread.h b/src/common/thread.h index 9c08be7e3..fa475ab51 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <chrono> | ||
| 7 | #include <condition_variable> | 8 | #include <condition_variable> |
| 8 | #include <cstddef> | 9 | #include <cstddef> |
| 9 | #include <mutex> | 10 | #include <mutex> |
| @@ -54,6 +55,15 @@ public: | |||
| 54 | is_set = false; | 55 | is_set = false; |
| 55 | } | 56 | } |
| 56 | 57 | ||
| 58 | template <class Clock, class Duration> | ||
| 59 | bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) { | ||
| 60 | std::unique_lock<std::mutex> lk(mutex); | ||
| 61 | if (!condvar.wait_until(lk, time, [this] { return is_set; })) | ||
| 62 | return false; | ||
| 63 | is_set = false; | ||
| 64 | return true; | ||
| 65 | } | ||
| 66 | |||
| 57 | void Reset() { | 67 | void Reset() { |
| 58 | std::unique_lock<std::mutex> lk(mutex); | 68 | std::unique_lock<std::mutex> lk(mutex); |
| 59 | // no other action required, since wait loops on the predicate and any lingering signal will | 69 | // no other action required, since wait loops on the predicate and any lingering signal will |
diff --git a/src/common/vector_math.h b/src/common/vector_math.h index a57d86d88..7ca8e15f5 100644 --- a/src/common/vector_math.h +++ b/src/common/vector_math.h | |||
| @@ -186,6 +186,18 @@ Vec2<T> operator*(const V& f, const Vec2<T>& vec) { | |||
| 186 | 186 | ||
| 187 | typedef Vec2<float> Vec2f; | 187 | typedef Vec2<float> Vec2f; |
| 188 | 188 | ||
| 189 | template <> | ||
| 190 | inline float Vec2<float>::Length() const { | ||
| 191 | return std::sqrt(x * x + y * y); | ||
| 192 | } | ||
| 193 | |||
| 194 | template <> | ||
| 195 | inline float Vec2<float>::Normalize() { | ||
| 196 | float length = Length(); | ||
| 197 | *this /= length; | ||
| 198 | return length; | ||
| 199 | } | ||
| 200 | |||
| 189 | template <typename T> | 201 | template <typename T> |
| 190 | class Vec3 { | 202 | class Vec3 { |
| 191 | public: | 203 | public: |
| @@ -388,6 +400,13 @@ inline Vec3<float> Vec3<float>::Normalized() const { | |||
| 388 | return *this / Length(); | 400 | return *this / Length(); |
| 389 | } | 401 | } |
| 390 | 402 | ||
| 403 | template <> | ||
| 404 | inline float Vec3<float>::Normalize() { | ||
| 405 | float length = Length(); | ||
| 406 | *this /= length; | ||
| 407 | return length; | ||
| 408 | } | ||
| 409 | |||
| 391 | typedef Vec3<float> Vec3f; | 410 | typedef Vec3<float> Vec3f; |
| 392 | 411 | ||
| 393 | template <typename T> | 412 | template <typename T> |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3621449b3..4c5b633e0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -31,6 +31,7 @@ set(SRCS | |||
| 31 | file_sys/savedata_archive.cpp | 31 | file_sys/savedata_archive.cpp |
| 32 | frontend/emu_window.cpp | 32 | frontend/emu_window.cpp |
| 33 | frontend/key_map.cpp | 33 | frontend/key_map.cpp |
| 34 | frontend/motion_emu.cpp | ||
| 34 | gdbstub/gdbstub.cpp | 35 | gdbstub/gdbstub.cpp |
| 35 | hle/config_mem.cpp | 36 | hle/config_mem.cpp |
| 36 | hle/applets/applet.cpp | 37 | hle/applets/applet.cpp |
| @@ -202,6 +203,7 @@ set(HEADERS | |||
| 202 | file_sys/savedata_archive.h | 203 | file_sys/savedata_archive.h |
| 203 | frontend/emu_window.h | 204 | frontend/emu_window.h |
| 204 | frontend/key_map.h | 205 | frontend/key_map.h |
| 206 | frontend/motion_emu.h | ||
| 205 | gdbstub/gdbstub.h | 207 | gdbstub/gdbstub.h |
| 206 | hle/config_mem.h | 208 | hle/config_mem.h |
| 207 | hle/function_wrappers.h | 209 | hle/function_wrappers.h |
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index f6f90f9e1..1541cc39d 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cmath> | 6 | #include <cmath> |
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/profiler_reporting.h" | ||
| 8 | #include "core/frontend/emu_window.h" | 9 | #include "core/frontend/emu_window.h" |
| 9 | #include "core/frontend/key_map.h" | 10 | #include "core/frontend/key_map.h" |
| 10 | #include "video_core/video_core.h" | 11 | #include "video_core/video_core.h" |
| @@ -89,6 +90,30 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { | |||
| 89 | TouchPressed(framebuffer_x, framebuffer_y); | 90 | TouchPressed(framebuffer_x, framebuffer_y); |
| 90 | } | 91 | } |
| 91 | 92 | ||
| 93 | void EmuWindow::AccelerometerChanged(float x, float y, float z) { | ||
| 94 | constexpr float coef = 512; | ||
| 95 | |||
| 96 | std::lock_guard<std::mutex> lock(accel_mutex); | ||
| 97 | |||
| 98 | // TODO(wwylele): do a time stretch as it in GyroscopeChanged | ||
| 99 | // The time stretch formula should be like | ||
| 100 | // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity | ||
| 101 | accel_x = x * coef; | ||
| 102 | accel_y = y * coef; | ||
| 103 | accel_z = z * coef; | ||
| 104 | } | ||
| 105 | |||
| 106 | void EmuWindow::GyroscopeChanged(float x, float y, float z) { | ||
| 107 | constexpr float FULL_FPS = 60; | ||
| 108 | float coef = GetGyroscopeRawToDpsCoefficient(); | ||
| 109 | float stretch = | ||
| 110 | FULL_FPS / Common::Profiling::GetTimingResultsAggregator()->GetAggregatedResults().fps; | ||
| 111 | std::lock_guard<std::mutex> lock(gyro_mutex); | ||
| 112 | gyro_x = x * coef * stretch; | ||
| 113 | gyro_y = y * coef * stretch; | ||
| 114 | gyro_z = z * coef * stretch; | ||
| 115 | } | ||
| 116 | |||
| 92 | void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { | 117 | void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { |
| 93 | Layout::FramebufferLayout layout; | 118 | Layout::FramebufferLayout layout; |
| 94 | switch (Settings::values.layout_option) { | 119 | switch (Settings::values.layout_option) { |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 835c4d500..1ba64c92b 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <mutex> | ||
| 7 | #include <tuple> | 8 | #include <tuple> |
| 8 | #include <utility> | 9 | #include <utility> |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| @@ -93,6 +94,27 @@ public: | |||
| 93 | void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); | 94 | void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); |
| 94 | 95 | ||
| 95 | /** | 96 | /** |
| 97 | * Signal accelerometer state has changed. | ||
| 98 | * @param x X-axis accelerometer value | ||
| 99 | * @param y Y-axis accelerometer value | ||
| 100 | * @param z Z-axis accelerometer value | ||
| 101 | * @note all values are in unit of g (gravitational acceleration). | ||
| 102 | * e.g. x = 1.0 means 9.8m/s^2 in x direction. | ||
| 103 | * @see GetAccelerometerState for axis explanation. | ||
| 104 | */ | ||
| 105 | void AccelerometerChanged(float x, float y, float z); | ||
| 106 | |||
| 107 | /** | ||
| 108 | * Signal gyroscope state has changed. | ||
| 109 | * @param x X-axis accelerometer value | ||
| 110 | * @param y Y-axis accelerometer value | ||
| 111 | * @param z Z-axis accelerometer value | ||
| 112 | * @note all values are in deg/sec. | ||
| 113 | * @see GetGyroscopeState for axis explanation. | ||
| 114 | */ | ||
| 115 | void GyroscopeChanged(float x, float y, float z); | ||
| 116 | |||
| 117 | /** | ||
| 96 | * Gets the current pad state (which buttons are pressed). | 118 | * Gets the current pad state (which buttons are pressed). |
| 97 | * @note This should be called by the core emu thread to get a state set by the window thread. | 119 | * @note This should be called by the core emu thread to get a state set by the window thread. |
| 98 | * @note This doesn't include analog input like circle pad direction | 120 | * @note This doesn't include analog input like circle pad direction |
| @@ -134,12 +156,11 @@ public: | |||
| 134 | * 1 unit of return value = 1/512 g (measured by hw test), | 156 | * 1 unit of return value = 1/512 g (measured by hw test), |
| 135 | * where g is the gravitational acceleration (9.8 m/sec2). | 157 | * where g is the gravitational acceleration (9.8 m/sec2). |
| 136 | * @note This should be called by the core emu thread to get a state set by the window thread. | 158 | * @note This should be called by the core emu thread to get a state set by the window thread. |
| 137 | * @todo Implement accelerometer input in front-end. | ||
| 138 | * @return std::tuple of (x, y, z) | 159 | * @return std::tuple of (x, y, z) |
| 139 | */ | 160 | */ |
| 140 | std::tuple<s16, s16, s16> GetAccelerometerState() const { | 161 | std::tuple<s16, s16, s16> GetAccelerometerState() { |
| 141 | // stubbed | 162 | std::lock_guard<std::mutex> lock(accel_mutex); |
| 142 | return std::make_tuple(0, -512, 0); | 163 | return std::make_tuple(accel_x, accel_y, accel_z); |
| 143 | } | 164 | } |
| 144 | 165 | ||
| 145 | /** | 166 | /** |
| @@ -153,12 +174,11 @@ public: | |||
| 153 | * 1 unit of return value = (1/coef) deg/sec, | 174 | * 1 unit of return value = (1/coef) deg/sec, |
| 154 | * where coef is the return value of GetGyroscopeRawToDpsCoefficient(). | 175 | * where coef is the return value of GetGyroscopeRawToDpsCoefficient(). |
| 155 | * @note This should be called by the core emu thread to get a state set by the window thread. | 176 | * @note This should be called by the core emu thread to get a state set by the window thread. |
| 156 | * @todo Implement gyroscope input in front-end. | ||
| 157 | * @return std::tuple of (x, y, z) | 177 | * @return std::tuple of (x, y, z) |
| 158 | */ | 178 | */ |
| 159 | std::tuple<s16, s16, s16> GetGyroscopeState() const { | 179 | std::tuple<s16, s16, s16> GetGyroscopeState() { |
| 160 | // stubbed | 180 | std::lock_guard<std::mutex> lock(gyro_mutex); |
| 161 | return std::make_tuple(0, 0, 0); | 181 | return std::make_tuple(gyro_x, gyro_y, gyro_z); |
| 162 | } | 182 | } |
| 163 | 183 | ||
| 164 | /** | 184 | /** |
| @@ -216,6 +236,12 @@ protected: | |||
| 216 | circle_pad_x = 0; | 236 | circle_pad_x = 0; |
| 217 | circle_pad_y = 0; | 237 | circle_pad_y = 0; |
| 218 | touch_pressed = false; | 238 | touch_pressed = false; |
| 239 | accel_x = 0; | ||
| 240 | accel_y = -512; | ||
| 241 | accel_z = 0; | ||
| 242 | gyro_x = 0; | ||
| 243 | gyro_y = 0; | ||
| 244 | gyro_z = 0; | ||
| 219 | } | 245 | } |
| 220 | virtual ~EmuWindow() {} | 246 | virtual ~EmuWindow() {} |
| 221 | 247 | ||
| @@ -281,6 +307,16 @@ private: | |||
| 281 | s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156) | 307 | s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156) |
| 282 | s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156) | 308 | s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156) |
| 283 | 309 | ||
| 310 | std::mutex accel_mutex; | ||
| 311 | s16 accel_x; ///< Accelerometer X-axis value in native 3DS units | ||
| 312 | s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units | ||
| 313 | s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units | ||
| 314 | |||
| 315 | std::mutex gyro_mutex; | ||
| 316 | s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units | ||
| 317 | s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units | ||
| 318 | s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units | ||
| 319 | |||
| 284 | /** | 320 | /** |
| 285 | * Clip the provided coordinates to be inside the touchscreen area. | 321 | * Clip the provided coordinates to be inside the touchscreen area. |
| 286 | */ | 322 | */ |
diff --git a/src/core/frontend/motion_emu.cpp b/src/core/frontend/motion_emu.cpp new file mode 100644 index 000000000..9a5b3185d --- /dev/null +++ b/src/core/frontend/motion_emu.cpp | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/math_util.h" | ||
| 6 | #include "common/quaternion.h" | ||
| 7 | #include "core/frontend/emu_window.h" | ||
| 8 | #include "core/frontend/motion_emu.h" | ||
| 9 | |||
| 10 | namespace Motion { | ||
| 11 | |||
| 12 | static constexpr int update_millisecond = 100; | ||
| 13 | static constexpr auto update_duration = | ||
| 14 | std::chrono::duration_cast<std::chrono::steady_clock::duration>( | ||
| 15 | std::chrono::milliseconds(update_millisecond)); | ||
| 16 | |||
| 17 | MotionEmu::MotionEmu(EmuWindow& emu_window) | ||
| 18 | : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {} | ||
| 19 | |||
| 20 | MotionEmu::~MotionEmu() { | ||
| 21 | if (motion_emu_thread.joinable()) { | ||
| 22 | shutdown_event.Set(); | ||
| 23 | motion_emu_thread.join(); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | void MotionEmu::MotionEmuThread(EmuWindow& emu_window) { | ||
| 28 | auto update_time = std::chrono::steady_clock::now(); | ||
| 29 | Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0); | ||
| 30 | Math::Quaternion<float> old_q; | ||
| 31 | |||
| 32 | while (!shutdown_event.WaitUntil(update_time)) { | ||
| 33 | update_time += update_duration; | ||
| 34 | old_q = q; | ||
| 35 | |||
| 36 | { | ||
| 37 | std::lock_guard<std::mutex> guard(tilt_mutex); | ||
| 38 | |||
| 39 | // Find the quaternion describing current 3DS tilting | ||
| 40 | q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), | ||
| 41 | tilt_angle); | ||
| 42 | } | ||
| 43 | |||
| 44 | auto inv_q = q.Inverse(); | ||
| 45 | |||
| 46 | // Set the gravity vector in world space | ||
| 47 | auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f); | ||
| 48 | |||
| 49 | // Find the angular rate vector in world space | ||
| 50 | auto angular_rate = ((q - old_q) * inv_q).xyz * 2; | ||
| 51 | angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180; | ||
| 52 | |||
| 53 | // Transform the two vectors from world space to 3DS space | ||
| 54 | gravity = QuaternionRotate(inv_q, gravity); | ||
| 55 | angular_rate = QuaternionRotate(inv_q, angular_rate); | ||
| 56 | |||
| 57 | // Update the sensor state | ||
| 58 | emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z); | ||
| 59 | emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | void MotionEmu::BeginTilt(int x, int y) { | ||
| 64 | mouse_origin = Math::MakeVec(x, y); | ||
| 65 | is_tilting = true; | ||
| 66 | } | ||
| 67 | |||
| 68 | void MotionEmu::Tilt(int x, int y) { | ||
| 69 | constexpr float SENSITIVITY = 0.01f; | ||
| 70 | auto mouse_move = Math::MakeVec(x, y) - mouse_origin; | ||
| 71 | if (is_tilting) { | ||
| 72 | std::lock_guard<std::mutex> guard(tilt_mutex); | ||
| 73 | if (mouse_move.x == 0 && mouse_move.y == 0) { | ||
| 74 | tilt_angle = 0; | ||
| 75 | } else { | ||
| 76 | tilt_direction = mouse_move.Cast<float>(); | ||
| 77 | tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f, | ||
| 78 | MathUtil::PI * 0.5f); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | void MotionEmu::EndTilt() { | ||
| 84 | std::lock_guard<std::mutex> guard(tilt_mutex); | ||
| 85 | tilt_angle = 0; | ||
| 86 | is_tilting = false; | ||
| 87 | } | ||
| 88 | |||
| 89 | } // namespace Motion | ||
diff --git a/src/core/frontend/motion_emu.h b/src/core/frontend/motion_emu.h new file mode 100644 index 000000000..99d41a726 --- /dev/null +++ b/src/core/frontend/motion_emu.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | #include "common/thread.h" | ||
| 7 | #include "common/vector_math.h" | ||
| 8 | |||
| 9 | class EmuWindow; | ||
| 10 | |||
| 11 | namespace Motion { | ||
| 12 | |||
| 13 | class MotionEmu final { | ||
| 14 | public: | ||
| 15 | MotionEmu(EmuWindow& emu_window); | ||
| 16 | ~MotionEmu(); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Signals that a motion sensor tilt has begun. | ||
| 20 | * @param x the x-coordinate of the cursor | ||
| 21 | * @param y the y-coordinate of the cursor | ||
| 22 | */ | ||
| 23 | void BeginTilt(int x, int y); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Signals that a motion sensor tilt is occurring. | ||
| 27 | * @param x the x-coordinate of the cursor | ||
| 28 | * @param y the y-coordinate of the cursor | ||
| 29 | */ | ||
| 30 | void Tilt(int x, int y); | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Signals that a motion sensor tilt has ended. | ||
| 34 | */ | ||
| 35 | void EndTilt(); | ||
| 36 | |||
| 37 | private: | ||
| 38 | Math::Vec2<int> mouse_origin; | ||
| 39 | |||
| 40 | std::mutex tilt_mutex; | ||
| 41 | Math::Vec2<float> tilt_direction; | ||
| 42 | float tilt_angle = 0; | ||
| 43 | |||
| 44 | bool is_tilting = false; | ||
| 45 | |||
| 46 | Common::Event shutdown_event; | ||
| 47 | std::thread motion_emu_thread; | ||
| 48 | |||
| 49 | void MotionEmuThread(EmuWindow& emu_window); | ||
| 50 | }; | ||
| 51 | |||
| 52 | } // namespace Motion | ||
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index d88e25073..5cf45ada5 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -57,7 +57,6 @@ const u32 SIGTERM = 15; | |||
| 57 | const u32 MSG_WAITALL = 8; | 57 | const u32 MSG_WAITALL = 8; |
| 58 | #endif | 58 | #endif |
| 59 | 59 | ||
| 60 | const u32 R0_REGISTER = 0; | ||
| 61 | const u32 R15_REGISTER = 15; | 60 | const u32 R15_REGISTER = 15; |
| 62 | const u32 CPSR_REGISTER = 25; | 61 | const u32 CPSR_REGISTER = 25; |
| 63 | const u32 FPSCR_REGISTER = 58; | 62 | const u32 FPSCR_REGISTER = 58; |
| @@ -816,10 +815,6 @@ static void RemoveBreakpoint() { | |||
| 816 | auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); | 815 | auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); |
| 817 | PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset)); | 816 | PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset)); |
| 818 | 817 | ||
| 819 | start_offset = addr_pos + 1; | ||
| 820 | u32 len = | ||
| 821 | HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset)); | ||
| 822 | |||
| 823 | if (type == BreakpointType::Access) { | 818 | if (type == BreakpointType::Access) { |
| 824 | // Access is made up of Read and Write types, so add both breakpoints | 819 | // Access is made up of Read and Write types, so add both breakpoints |
| 825 | type = BreakpointType::Read; | 820 | type = BreakpointType::Read; |
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index a515f53f9..23f9df0d6 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp | |||
| @@ -25,12 +25,12 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { | |||
| 25 | return evt; | 25 | return evt; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | bool Event::ShouldWait() { | 28 | bool Event::ShouldWait(Thread* thread) const { |
| 29 | return !signaled; | 29 | return !signaled; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | void Event::Acquire() { | 32 | void Event::Acquire(Thread* thread) { |
| 33 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 33 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 34 | 34 | ||
| 35 | if (reset_type == ResetType::OneShot) | 35 | if (reset_type == ResetType::OneShot) |
| 36 | signaled = false; | 36 | signaled = false; |
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 28d955b1e..3e3673508 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h | |||
| @@ -35,8 +35,8 @@ public: | |||
| 35 | bool signaled; ///< Whether the event has already been signaled | 35 | bool signaled; ///< Whether the event has already been signaled |
| 36 | std::string name; ///< Name of event (optional) | 36 | std::string name; ///< Name of event (optional) |
| 37 | 37 | ||
| 38 | bool ShouldWait() override; | 38 | bool ShouldWait(Thread* thread) const override; |
| 39 | void Acquire() override; | 39 | void Acquire(Thread* thread) override; |
| 40 | 40 | ||
| 41 | void WakeupAllWaitingThreads() override; | 41 | void WakeupAllWaitingThreads() override; |
| 42 | 42 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1db8e102f..f599916f0 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <boost/range/algorithm_ext/erase.hpp> | ||
| 7 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 9 | #include "core/hle/config_mem.h" | 8 | #include "core/hle/config_mem.h" |
| @@ -28,32 +27,39 @@ void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { | |||
| 28 | 27 | ||
| 29 | void WaitObject::RemoveWaitingThread(Thread* thread) { | 28 | void WaitObject::RemoveWaitingThread(Thread* thread) { |
| 30 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | 29 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); |
| 30 | // If a thread passed multiple handles to the same object, | ||
| 31 | // the kernel might attempt to remove the thread from the object's | ||
| 32 | // waiting threads list multiple times. | ||
| 31 | if (itr != waiting_threads.end()) | 33 | if (itr != waiting_threads.end()) |
| 32 | waiting_threads.erase(itr); | 34 | waiting_threads.erase(itr); |
| 33 | } | 35 | } |
| 34 | 36 | ||
| 35 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | 37 | SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { |
| 36 | // Remove the threads that are ready or already running from our waitlist | ||
| 37 | boost::range::remove_erase_if(waiting_threads, [](const SharedPtr<Thread>& thread) { | ||
| 38 | return thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_READY || | ||
| 39 | thread->status == THREADSTATUS_DEAD; | ||
| 40 | }); | ||
| 41 | |||
| 42 | // TODO(Subv): This call should be performed inside the loop below to check if an object can be | ||
| 43 | // acquired by a particular thread. This is useful for things like recursive locking of Mutexes. | ||
| 44 | if (ShouldWait()) | ||
| 45 | return nullptr; | ||
| 46 | |||
| 47 | Thread* candidate = nullptr; | 38 | Thread* candidate = nullptr; |
| 48 | s32 candidate_priority = THREADPRIO_LOWEST + 1; | 39 | s32 candidate_priority = THREADPRIO_LOWEST + 1; |
| 49 | 40 | ||
| 50 | for (const auto& thread : waiting_threads) { | 41 | for (const auto& thread : waiting_threads) { |
| 42 | // The list of waiting threads must not contain threads that are not waiting to be awakened. | ||
| 43 | ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | ||
| 44 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL, | ||
| 45 | "Inconsistent thread statuses in waiting_threads"); | ||
| 46 | |||
| 51 | if (thread->current_priority >= candidate_priority) | 47 | if (thread->current_priority >= candidate_priority) |
| 52 | continue; | 48 | continue; |
| 53 | 49 | ||
| 54 | bool ready_to_run = | 50 | if (ShouldWait(thread.get())) |
| 55 | std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), | 51 | continue; |
| 56 | [](const SharedPtr<WaitObject>& object) { return object->ShouldWait(); }); | 52 | |
| 53 | // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or | ||
| 54 | // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready. | ||
| 55 | bool ready_to_run = true; | ||
| 56 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { | ||
| 57 | ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), | ||
| 58 | [&thread](const SharedPtr<WaitObject>& object) { | ||
| 59 | return object->ShouldWait(thread.get()); | ||
| 60 | }); | ||
| 61 | } | ||
| 62 | |||
| 57 | if (ready_to_run) { | 63 | if (ready_to_run) { |
| 58 | candidate = thread.get(); | 64 | candidate = thread.get(); |
| 59 | candidate_priority = thread->current_priority; | 65 | candidate_priority = thread->current_priority; |
| @@ -66,7 +72,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | |||
| 66 | void WaitObject::WakeupAllWaitingThreads() { | 72 | void WaitObject::WakeupAllWaitingThreads() { |
| 67 | while (auto thread = GetHighestPriorityReadyThread()) { | 73 | while (auto thread = GetHighestPriorityReadyThread()) { |
| 68 | if (!thread->IsSleepingOnWaitAll()) { | 74 | if (!thread->IsSleepingOnWaitAll()) { |
| 69 | Acquire(); | 75 | Acquire(thread.get()); |
| 70 | // Set the output index of the WaitSynchronizationN call to the index of this object. | 76 | // Set the output index of the WaitSynchronizationN call to the index of this object. |
| 71 | if (thread->wait_set_output) { | 77 | if (thread->wait_set_output) { |
| 72 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); | 78 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); |
| @@ -74,18 +80,17 @@ void WaitObject::WakeupAllWaitingThreads() { | |||
| 74 | } | 80 | } |
| 75 | } else { | 81 | } else { |
| 76 | for (auto& object : thread->wait_objects) { | 82 | for (auto& object : thread->wait_objects) { |
| 77 | object->Acquire(); | 83 | object->Acquire(thread.get()); |
| 78 | object->RemoveWaitingThread(thread.get()); | ||
| 79 | } | 84 | } |
| 80 | // Note: This case doesn't update the output index of WaitSynchronizationN. | 85 | // Note: This case doesn't update the output index of WaitSynchronizationN. |
| 81 | // Clear the thread's waitlist | ||
| 82 | thread->wait_objects.clear(); | ||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 88 | for (auto& object : thread->wait_objects) | ||
| 89 | object->RemoveWaitingThread(thread.get()); | ||
| 90 | thread->wait_objects.clear(); | ||
| 91 | |||
| 85 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | 92 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); |
| 86 | thread->ResumeFromWait(); | 93 | thread->ResumeFromWait(); |
| 87 | // Note: Removing the thread from the object's waitlist will be | ||
| 88 | // done by GetHighestPriorityReadyThread. | ||
| 89 | } | 94 | } |
| 90 | } | 95 | } |
| 91 | 96 | ||
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 1e68f9cab..bb8b99bb5 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -132,25 +132,26 @@ using SharedPtr = boost::intrusive_ptr<T>; | |||
| 132 | class WaitObject : public Object { | 132 | class WaitObject : public Object { |
| 133 | public: | 133 | public: |
| 134 | /** | 134 | /** |
| 135 | * Check if the current thread should wait until the object is available | 135 | * Check if the specified thread should wait until the object is available |
| 136 | * @param thread The thread about which we're deciding. | ||
| 136 | * @return True if the current thread should wait due to this object being unavailable | 137 | * @return True if the current thread should wait due to this object being unavailable |
| 137 | */ | 138 | */ |
| 138 | virtual bool ShouldWait() = 0; | 139 | virtual bool ShouldWait(Thread* thread) const = 0; |
| 139 | 140 | ||
| 140 | /// Acquire/lock the object if it is available | 141 | /// Acquire/lock the object for the specified thread if it is available |
| 141 | virtual void Acquire() = 0; | 142 | virtual void Acquire(Thread* thread) = 0; |
| 142 | 143 | ||
| 143 | /** | 144 | /** |
| 144 | * Add a thread to wait on this object | 145 | * Add a thread to wait on this object |
| 145 | * @param thread Pointer to thread to add | 146 | * @param thread Pointer to thread to add |
| 146 | */ | 147 | */ |
| 147 | void AddWaitingThread(SharedPtr<Thread> thread); | 148 | virtual void AddWaitingThread(SharedPtr<Thread> thread); |
| 148 | 149 | ||
| 149 | /** | 150 | /** |
| 150 | * Removes a thread from waiting on this object (e.g. if it was resumed already) | 151 | * Removes a thread from waiting on this object (e.g. if it was resumed already) |
| 151 | * @param thread Pointer to thread to remove | 152 | * @param thread Pointer to thread to remove |
| 152 | */ | 153 | */ |
| 153 | void RemoveWaitingThread(Thread* thread); | 154 | virtual void RemoveWaitingThread(Thread* thread); |
| 154 | 155 | ||
| 155 | /** | 156 | /** |
| 156 | * Wake up all threads waiting on this object that can be awoken, in priority order, | 157 | * Wake up all threads waiting on this object that can be awoken, in priority order, |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 736944bae..cef961289 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -6,26 +6,18 @@ | |||
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | #include <boost/range/algorithm_ext/erase.hpp> | 7 | #include <boost/range/algorithm_ext/erase.hpp> |
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "core/core.h" | ||
| 9 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 10 | #include "core/hle/kernel/mutex.h" | 11 | #include "core/hle/kernel/mutex.h" |
| 11 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 12 | 13 | ||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| 14 | 15 | ||
| 15 | /** | ||
| 16 | * Resumes a thread waiting for the specified mutex | ||
| 17 | * @param mutex The mutex that some thread is waiting on | ||
| 18 | */ | ||
| 19 | static void ResumeWaitingThread(Mutex* mutex) { | ||
| 20 | // Reset mutex lock thread handle, nothing is waiting | ||
| 21 | mutex->lock_count = 0; | ||
| 22 | mutex->holding_thread = nullptr; | ||
| 23 | mutex->WakeupAllWaitingThreads(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void ReleaseThreadMutexes(Thread* thread) { | 16 | void ReleaseThreadMutexes(Thread* thread) { |
| 27 | for (auto& mtx : thread->held_mutexes) { | 17 | for (auto& mtx : thread->held_mutexes) { |
| 28 | ResumeWaitingThread(mtx.get()); | 18 | mtx->lock_count = 0; |
| 19 | mtx->holding_thread = nullptr; | ||
| 20 | mtx->WakeupAllWaitingThreads(); | ||
| 29 | } | 21 | } |
| 30 | thread->held_mutexes.clear(); | 22 | thread->held_mutexes.clear(); |
| 31 | } | 23 | } |
| @@ -40,52 +32,74 @@ SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { | |||
| 40 | mutex->name = std::move(name); | 32 | mutex->name = std::move(name); |
| 41 | mutex->holding_thread = nullptr; | 33 | mutex->holding_thread = nullptr; |
| 42 | 34 | ||
| 43 | // Acquire mutex with current thread if initialized as locked... | 35 | // Acquire mutex with current thread if initialized as locked |
| 44 | if (initial_locked) | 36 | if (initial_locked) |
| 45 | mutex->Acquire(); | 37 | mutex->Acquire(GetCurrentThread()); |
| 46 | 38 | ||
| 47 | return mutex; | 39 | return mutex; |
| 48 | } | 40 | } |
| 49 | 41 | ||
| 50 | bool Mutex::ShouldWait() { | 42 | bool Mutex::ShouldWait(Thread* thread) const { |
| 51 | auto thread = GetCurrentThread(); | 43 | return lock_count > 0 && thread != holding_thread; |
| 52 | bool wait = lock_count > 0 && holding_thread != thread; | ||
| 53 | |||
| 54 | // If the holding thread of the mutex is lower priority than this thread, that thread should | ||
| 55 | // temporarily inherit this thread's priority | ||
| 56 | if (wait && thread->current_priority < holding_thread->current_priority) | ||
| 57 | holding_thread->BoostPriority(thread->current_priority); | ||
| 58 | |||
| 59 | return wait; | ||
| 60 | } | ||
| 61 | |||
| 62 | void Mutex::Acquire() { | ||
| 63 | Acquire(GetCurrentThread()); | ||
| 64 | } | 44 | } |
| 65 | 45 | ||
| 66 | void Mutex::Acquire(SharedPtr<Thread> thread) { | 46 | void Mutex::Acquire(Thread* thread) { |
| 67 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 47 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 68 | 48 | ||
| 69 | // Actually "acquire" the mutex only if we don't already have it... | 49 | // Actually "acquire" the mutex only if we don't already have it |
| 70 | if (lock_count == 0) { | 50 | if (lock_count == 0) { |
| 51 | priority = thread->current_priority; | ||
| 71 | thread->held_mutexes.insert(this); | 52 | thread->held_mutexes.insert(this); |
| 72 | holding_thread = std::move(thread); | 53 | holding_thread = thread; |
| 54 | thread->UpdatePriority(); | ||
| 55 | Core::System::GetInstance().PrepareReschedule(); | ||
| 73 | } | 56 | } |
| 74 | 57 | ||
| 75 | lock_count++; | 58 | lock_count++; |
| 76 | } | 59 | } |
| 77 | 60 | ||
| 78 | void Mutex::Release() { | 61 | void Mutex::Release() { |
| 79 | // Only release if the mutex is held... | 62 | // Only release if the mutex is held |
| 80 | if (lock_count > 0) { | 63 | if (lock_count > 0) { |
| 81 | lock_count--; | 64 | lock_count--; |
| 82 | 65 | ||
| 83 | // Yield to the next thread only if we've fully released the mutex... | 66 | // Yield to the next thread only if we've fully released the mutex |
| 84 | if (lock_count == 0) { | 67 | if (lock_count == 0) { |
| 85 | holding_thread->held_mutexes.erase(this); | 68 | holding_thread->held_mutexes.erase(this); |
| 86 | ResumeWaitingThread(this); | 69 | holding_thread->UpdatePriority(); |
| 70 | holding_thread = nullptr; | ||
| 71 | WakeupAllWaitingThreads(); | ||
| 72 | Core::System::GetInstance().PrepareReschedule(); | ||
| 87 | } | 73 | } |
| 88 | } | 74 | } |
| 89 | } | 75 | } |
| 90 | 76 | ||
| 77 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | ||
| 78 | WaitObject::AddWaitingThread(thread); | ||
| 79 | thread->pending_mutexes.insert(this); | ||
| 80 | UpdatePriority(); | ||
| 81 | } | ||
| 82 | |||
| 83 | void Mutex::RemoveWaitingThread(Thread* thread) { | ||
| 84 | WaitObject::RemoveWaitingThread(thread); | ||
| 85 | thread->pending_mutexes.erase(this); | ||
| 86 | UpdatePriority(); | ||
| 87 | } | ||
| 88 | |||
| 89 | void Mutex::UpdatePriority() { | ||
| 90 | if (!holding_thread) | ||
| 91 | return; | ||
| 92 | |||
| 93 | s32 best_priority = THREADPRIO_LOWEST; | ||
| 94 | for (auto& waiter : GetWaitingThreads()) { | ||
| 95 | if (waiter->current_priority < best_priority) | ||
| 96 | best_priority = waiter->current_priority; | ||
| 97 | } | ||
| 98 | |||
| 99 | if (best_priority != priority) { | ||
| 100 | priority = best_priority; | ||
| 101 | holding_thread->UpdatePriority(); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 91 | } // namespace | 105 | } // namespace |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 53c3dc1f1..c57adf400 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -35,17 +35,22 @@ public: | |||
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | int lock_count; ///< Number of times the mutex has been acquired | 37 | int lock_count; ///< Number of times the mutex has been acquired |
| 38 | u32 priority; ///< The priority of the mutex, used for priority inheritance. | ||
| 38 | std::string name; ///< Name of mutex (optional) | 39 | std::string name; ///< Name of mutex (optional) |
| 39 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex | 40 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex |
| 40 | 41 | ||
| 41 | bool ShouldWait() override; | ||
| 42 | void Acquire() override; | ||
| 43 | |||
| 44 | /** | 42 | /** |
| 45 | * Acquires the specified mutex for the specified thread | 43 | * Elevate the mutex priority to the best priority |
| 46 | * @param thread Thread that will acquire the mutex | 44 | * among the priorities of all its waiting threads. |
| 47 | */ | 45 | */ |
| 48 | void Acquire(SharedPtr<Thread> thread); | 46 | void UpdatePriority(); |
| 47 | |||
| 48 | bool ShouldWait(Thread* thread) const override; | ||
| 49 | void Acquire(Thread* thread) override; | ||
| 50 | |||
| 51 | void AddWaitingThread(SharedPtr<Thread> thread) override; | ||
| 52 | void RemoveWaitingThread(Thread* thread) override; | ||
| 53 | |||
| 49 | void Release(); | 54 | void Release(); |
| 50 | 55 | ||
| 51 | private: | 56 | private: |
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index bf7600780..8bda2f75d 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp | |||
| @@ -30,12 +30,13 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou | |||
| 30 | return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); | 30 | return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | bool Semaphore::ShouldWait() { | 33 | bool Semaphore::ShouldWait(Thread* thread) const { |
| 34 | return available_count <= 0; | 34 | return available_count <= 0; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | void Semaphore::Acquire() { | 37 | void Semaphore::Acquire(Thread* thread) { |
| 38 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 38 | if (available_count <= 0) |
| 39 | return; | ||
| 39 | --available_count; | 40 | --available_count; |
| 40 | } | 41 | } |
| 41 | 42 | ||
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index e01908a25..cde94f7cc 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h | |||
| @@ -39,8 +39,8 @@ public: | |||
| 39 | s32 available_count; ///< Number of free slots left in the semaphore | 39 | s32 available_count; ///< Number of free slots left in the semaphore |
| 40 | std::string name; ///< Name of semaphore (optional) | 40 | std::string name; ///< Name of semaphore (optional) |
| 41 | 41 | ||
| 42 | bool ShouldWait() override; | 42 | bool ShouldWait(Thread* thread) const override; |
| 43 | void Acquire() override; | 43 | void Acquire(Thread* thread) override; |
| 44 | 44 | ||
| 45 | /** | 45 | /** |
| 46 | * Releases a certain number of slots from a semaphore. | 46 | * Releases a certain number of slots from a semaphore. |
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 6c19aa7c0..fd3bbbcad 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp | |||
| @@ -14,13 +14,13 @@ namespace Kernel { | |||
| 14 | ServerPort::ServerPort() {} | 14 | ServerPort::ServerPort() {} |
| 15 | ServerPort::~ServerPort() {} | 15 | ServerPort::~ServerPort() {} |
| 16 | 16 | ||
| 17 | bool ServerPort::ShouldWait() { | 17 | bool ServerPort::ShouldWait(Thread* thread) const { |
| 18 | // If there are no pending sessions, we wait until a new one is added. | 18 | // If there are no pending sessions, we wait until a new one is added. |
| 19 | return pending_sessions.size() == 0; | 19 | return pending_sessions.size() == 0; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | void ServerPort::Acquire() { | 22 | void ServerPort::Acquire(Thread* thread) { |
| 23 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 23 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( | 26 | std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( |
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index b0f8df62c..6f8bdb6a9 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h | |||
| @@ -53,8 +53,8 @@ public: | |||
| 53 | /// ServerSessions created from this port inherit a reference to this handler. | 53 | /// ServerSessions created from this port inherit a reference to this handler. |
| 54 | std::shared_ptr<Service::SessionRequestHandler> hle_handler; | 54 | std::shared_ptr<Service::SessionRequestHandler> hle_handler; |
| 55 | 55 | ||
| 56 | bool ShouldWait() override; | 56 | bool ShouldWait(Thread* thread) const override; |
| 57 | void Acquire() override; | 57 | void Acquire(Thread* thread) override; |
| 58 | 58 | ||
| 59 | private: | 59 | private: |
| 60 | ServerPort(); | 60 | ServerPort(); |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 146458c1c..9447ff236 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -29,12 +29,12 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create( | |||
| 29 | return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); | 29 | return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | bool ServerSession::ShouldWait() { | 32 | bool ServerSession::ShouldWait(Thread* thread) const { |
| 33 | return !signaled; | 33 | return !signaled; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | void ServerSession::Acquire() { | 36 | void ServerSession::Acquire(Thread* thread) { |
| 37 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 37 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 38 | signaled = false; | 38 | signaled = false; |
| 39 | } | 39 | } |
| 40 | 40 | ||
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 458284a5d..c088b9a19 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -57,9 +57,9 @@ public: | |||
| 57 | */ | 57 | */ |
| 58 | ResultCode HandleSyncRequest(); | 58 | ResultCode HandleSyncRequest(); |
| 59 | 59 | ||
| 60 | bool ShouldWait() override; | 60 | bool ShouldWait(Thread* thread) const override; |
| 61 | 61 | ||
| 62 | void Acquire() override; | 62 | void Acquire(Thread* thread) override; |
| 63 | 63 | ||
| 64 | std::string name; ///< The name of this session (optional) | 64 | std::string name; ///< The name of this session (optional) |
| 65 | bool signaled; ///< Whether there's new data available to this ServerSession | 65 | bool signaled; ///< Whether there's new data available to this ServerSession |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 5fb95dada..8c6fbcd04 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -27,12 +27,12 @@ namespace Kernel { | |||
| 27 | /// Event type for the thread wake up event | 27 | /// Event type for the thread wake up event |
| 28 | static int ThreadWakeupEventType; | 28 | static int ThreadWakeupEventType; |
| 29 | 29 | ||
| 30 | bool Thread::ShouldWait() { | 30 | bool Thread::ShouldWait(Thread* thread) const { |
| 31 | return status != THREADSTATUS_DEAD; | 31 | return status != THREADSTATUS_DEAD; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | void Thread::Acquire() { | 34 | void Thread::Acquire(Thread* thread) { |
| 35 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 35 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing | 38 | // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing |
| @@ -66,20 +66,6 @@ Thread* GetCurrentThread() { | |||
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | /** | 68 | /** |
| 69 | * Check if a thread is waiting on the specified wait object | ||
| 70 | * @param thread The thread to test | ||
| 71 | * @param wait_object The object to test against | ||
| 72 | * @return True if the thread is waiting, false otherwise | ||
| 73 | */ | ||
| 74 | static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { | ||
| 75 | if (thread->status != THREADSTATUS_WAIT_SYNCH) | ||
| 76 | return false; | ||
| 77 | |||
| 78 | auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); | ||
| 79 | return itr != thread->wait_objects.end(); | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Check if the specified thread is waiting on the specified address to be arbitrated | 69 | * Check if the specified thread is waiting on the specified address to be arbitrated |
| 84 | * @param thread The thread to test | 70 | * @param thread The thread to test |
| 85 | * @param wait_address The address to test against | 71 | * @param wait_address The address to test against |
| @@ -90,9 +76,6 @@ static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { | |||
| 90 | } | 76 | } |
| 91 | 77 | ||
| 92 | void Thread::Stop() { | 78 | void Thread::Stop() { |
| 93 | // Release all the mutexes that this thread holds | ||
| 94 | ReleaseThreadMutexes(this); | ||
| 95 | |||
| 96 | // Cancel any outstanding wakeup events for this thread | 79 | // Cancel any outstanding wakeup events for this thread |
| 97 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | 80 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); |
| 98 | wakeup_callback_handle_table.Close(callback_handle); | 81 | wakeup_callback_handle_table.Close(callback_handle); |
| @@ -114,6 +97,9 @@ void Thread::Stop() { | |||
| 114 | } | 97 | } |
| 115 | wait_objects.clear(); | 98 | wait_objects.clear(); |
| 116 | 99 | ||
| 100 | // Release all the mutexes that this thread holds | ||
| 101 | ReleaseThreadMutexes(this); | ||
| 102 | |||
| 117 | // Mark the TLS slot in the thread's page as free. | 103 | // Mark the TLS slot in the thread's page as free. |
| 118 | u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; | 104 | u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; |
| 119 | u32 tls_slot = | 105 | u32 tls_slot = |
| @@ -155,28 +141,6 @@ void ArbitrateAllThreads(u32 address) { | |||
| 155 | } | 141 | } |
| 156 | } | 142 | } |
| 157 | 143 | ||
| 158 | /// Boost low priority threads (temporarily) that have been starved | ||
| 159 | static void PriorityBoostStarvedThreads() { | ||
| 160 | u64 current_ticks = CoreTiming::GetTicks(); | ||
| 161 | |||
| 162 | for (auto& thread : thread_list) { | ||
| 163 | // TODO(bunnei): Threads that have been waiting to be scheduled for `boost_ticks` (or | ||
| 164 | // longer) will have their priority temporarily adjusted to 1 higher than the highest | ||
| 165 | // priority thread to prevent thread starvation. This general behavior has been verified | ||
| 166 | // on hardware. However, this is almost certainly not perfect, and the real CTR OS scheduler | ||
| 167 | // should probably be reversed to verify this. | ||
| 168 | |||
| 169 | const u64 boost_timeout = 2000000; // Boost threads that have been ready for > this long | ||
| 170 | |||
| 171 | u64 delta = current_ticks - thread->last_running_ticks; | ||
| 172 | |||
| 173 | if (thread->status == THREADSTATUS_READY && delta > boost_timeout) { | ||
| 174 | const s32 priority = std::max(ready_queue.get_first()->current_priority - 1, 0); | ||
| 175 | thread->BoostPriority(priority); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | /** | 144 | /** |
| 181 | * Switches the CPU's active thread context to that of the specified thread | 145 | * Switches the CPU's active thread context to that of the specified thread |
| 182 | * @param new_thread The thread to switch to | 146 | * @param new_thread The thread to switch to |
| @@ -199,8 +163,8 @@ static void SwitchContext(Thread* new_thread) { | |||
| 199 | 163 | ||
| 200 | // Load context of new thread | 164 | // Load context of new thread |
| 201 | if (new_thread) { | 165 | if (new_thread) { |
| 202 | DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, | 166 | ASSERT_MSG(new_thread->status == THREADSTATUS_READY, |
| 203 | "Thread must be ready to become running."); | 167 | "Thread must be ready to become running."); |
| 204 | 168 | ||
| 205 | // Cancel any outstanding wakeup events for this thread | 169 | // Cancel any outstanding wakeup events for this thread |
| 206 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); | 170 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); |
| @@ -210,9 +174,6 @@ static void SwitchContext(Thread* new_thread) { | |||
| 210 | ready_queue.remove(new_thread->current_priority, new_thread); | 174 | ready_queue.remove(new_thread->current_priority, new_thread); |
| 211 | new_thread->status = THREADSTATUS_RUNNING; | 175 | new_thread->status = THREADSTATUS_RUNNING; |
| 212 | 176 | ||
| 213 | // Restores thread to its nominal priority if it has been temporarily changed | ||
| 214 | new_thread->current_priority = new_thread->nominal_priority; | ||
| 215 | |||
| 216 | Core::CPU().LoadContext(new_thread->context); | 177 | Core::CPU().LoadContext(new_thread->context); |
| 217 | Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); | 178 | Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); |
| 218 | } else { | 179 | } else { |
| @@ -248,14 +209,6 @@ void WaitCurrentThread_Sleep() { | |||
| 248 | thread->status = THREADSTATUS_WAIT_SLEEP; | 209 | thread->status = THREADSTATUS_WAIT_SLEEP; |
| 249 | } | 210 | } |
| 250 | 211 | ||
| 251 | void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, | ||
| 252 | bool wait_set_output) { | ||
| 253 | Thread* thread = GetCurrentThread(); | ||
| 254 | thread->wait_set_output = wait_set_output; | ||
| 255 | thread->wait_objects = std::move(wait_objects); | ||
| 256 | thread->status = THREADSTATUS_WAIT_SYNCH; | ||
| 257 | } | ||
| 258 | |||
| 259 | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { | 212 | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { |
| 260 | Thread* thread = GetCurrentThread(); | 213 | Thread* thread = GetCurrentThread(); |
| 261 | thread->wait_address = wait_address; | 214 | thread->wait_address = wait_address; |
| @@ -281,7 +234,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | |||
| 281 | return; | 234 | return; |
| 282 | } | 235 | } |
| 283 | 236 | ||
| 284 | if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) { | 237 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || |
| 238 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { | ||
| 285 | thread->wait_set_output = false; | 239 | thread->wait_set_output = false; |
| 286 | // Remove the thread from each of its waiting objects' waitlists | 240 | // Remove the thread from each of its waiting objects' waitlists |
| 287 | for (auto& object : thread->wait_objects) | 241 | for (auto& object : thread->wait_objects) |
| @@ -305,8 +259,11 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 305 | } | 259 | } |
| 306 | 260 | ||
| 307 | void Thread::ResumeFromWait() { | 261 | void Thread::ResumeFromWait() { |
| 262 | ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); | ||
| 263 | |||
| 308 | switch (status) { | 264 | switch (status) { |
| 309 | case THREADSTATUS_WAIT_SYNCH: | 265 | case THREADSTATUS_WAIT_SYNCH_ALL: |
| 266 | case THREADSTATUS_WAIT_SYNCH_ANY: | ||
| 310 | case THREADSTATUS_WAIT_ARB: | 267 | case THREADSTATUS_WAIT_ARB: |
| 311 | case THREADSTATUS_WAIT_SLEEP: | 268 | case THREADSTATUS_WAIT_SLEEP: |
| 312 | break; | 269 | break; |
| @@ -515,8 +472,21 @@ void Thread::SetPriority(s32 priority) { | |||
| 515 | nominal_priority = current_priority = priority; | 472 | nominal_priority = current_priority = priority; |
| 516 | } | 473 | } |
| 517 | 474 | ||
| 475 | void Thread::UpdatePriority() { | ||
| 476 | s32 best_priority = nominal_priority; | ||
| 477 | for (auto& mutex : held_mutexes) { | ||
| 478 | if (mutex->priority < best_priority) | ||
| 479 | best_priority = mutex->priority; | ||
| 480 | } | ||
| 481 | BoostPriority(best_priority); | ||
| 482 | } | ||
| 483 | |||
| 518 | void Thread::BoostPriority(s32 priority) { | 484 | void Thread::BoostPriority(s32 priority) { |
| 519 | ready_queue.move(this, current_priority, priority); | 485 | // If thread was ready, adjust queues |
| 486 | if (status == THREADSTATUS_READY) | ||
| 487 | ready_queue.move(this, current_priority, priority); | ||
| 488 | else | ||
| 489 | ready_queue.prepare(priority); | ||
| 520 | current_priority = priority; | 490 | current_priority = priority; |
| 521 | } | 491 | } |
| 522 | 492 | ||
| @@ -538,9 +508,11 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) { | |||
| 538 | return thread; | 508 | return thread; |
| 539 | } | 509 | } |
| 540 | 510 | ||
| 541 | void Reschedule() { | 511 | bool HaveReadyThreads() { |
| 542 | PriorityBoostStarvedThreads(); | 512 | return ready_queue.get_first() != nullptr; |
| 513 | } | ||
| 543 | 514 | ||
| 515 | void Reschedule() { | ||
| 544 | Thread* cur = GetCurrentThread(); | 516 | Thread* cur = GetCurrentThread(); |
| 545 | Thread* next = PopNextReadyThread(); | 517 | Thread* next = PopNextReadyThread(); |
| 546 | 518 | ||
| @@ -563,6 +535,12 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { | |||
| 563 | context.cpu_registers[1] = output; | 535 | context.cpu_registers[1] = output; |
| 564 | } | 536 | } |
| 565 | 537 | ||
| 538 | s32 Thread::GetWaitObjectIndex(WaitObject* object) const { | ||
| 539 | ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); | ||
| 540 | auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); | ||
| 541 | return std::distance(match, wait_objects.rend()) - 1; | ||
| 542 | } | ||
| 543 | |||
| 566 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 544 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 567 | 545 | ||
| 568 | void ThreadingInit() { | 546 | void ThreadingInit() { |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index c77ac644d..c557a2279 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -31,13 +31,14 @@ enum ThreadProcessorId : s32 { | |||
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | enum ThreadStatus { | 33 | enum ThreadStatus { |
| 34 | THREADSTATUS_RUNNING, ///< Currently running | 34 | THREADSTATUS_RUNNING, ///< Currently running |
| 35 | THREADSTATUS_READY, ///< Ready to run | 35 | THREADSTATUS_READY, ///< Ready to run |
| 36 | THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter | 36 | THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter |
| 37 | THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC | 37 | THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC |
| 38 | THREADSTATUS_WAIT_SYNCH, ///< Waiting due to a WaitSynchronization SVC | 38 | THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false |
| 39 | THREADSTATUS_DORMANT, ///< Created but not yet made ready | 39 | THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true |
| 40 | THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated | 40 | THREADSTATUS_DORMANT, ///< Created but not yet made ready |
| 41 | THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated | ||
| 41 | }; | 42 | }; |
| 42 | 43 | ||
| 43 | namespace Kernel { | 44 | namespace Kernel { |
| @@ -72,8 +73,8 @@ public: | |||
| 72 | return HANDLE_TYPE; | 73 | return HANDLE_TYPE; |
| 73 | } | 74 | } |
| 74 | 75 | ||
| 75 | bool ShouldWait() override; | 76 | bool ShouldWait(Thread* thread) const override; |
| 76 | void Acquire() override; | 77 | void Acquire(Thread* thread) override; |
| 77 | 78 | ||
| 78 | /** | 79 | /** |
| 79 | * Gets the thread's current priority | 80 | * Gets the thread's current priority |
| @@ -90,6 +91,12 @@ public: | |||
| 90 | void SetPriority(s32 priority); | 91 | void SetPriority(s32 priority); |
| 91 | 92 | ||
| 92 | /** | 93 | /** |
| 94 | * Boost's a thread's priority to the best priority among the thread's held mutexes. | ||
| 95 | * This prevents priority inversion via priority inheritance. | ||
| 96 | */ | ||
| 97 | void UpdatePriority(); | ||
| 98 | |||
| 99 | /** | ||
| 93 | * Temporarily boosts the thread's priority until the next time it is scheduled | 100 | * Temporarily boosts the thread's priority until the next time it is scheduled |
| 94 | * @param priority The new priority | 101 | * @param priority The new priority |
| 95 | */ | 102 | */ |
| @@ -128,13 +135,14 @@ public: | |||
| 128 | 135 | ||
| 129 | /** | 136 | /** |
| 130 | * Retrieves the index that this particular object occupies in the list of objects | 137 | * Retrieves the index that this particular object occupies in the list of objects |
| 131 | * that the thread passed to WaitSynchronizationN. | 138 | * that the thread passed to WaitSynchronizationN, starting the search from the last element. |
| 132 | * It is used to set the output value of WaitSynchronizationN when the thread is awakened. | 139 | * It is used to set the output value of WaitSynchronizationN when the thread is awakened. |
| 140 | * When a thread wakes up due to an object signal, the kernel will use the index of the last | ||
| 141 | * matching object in the wait objects list in case of having multiple instances of the same | ||
| 142 | * object in the list. | ||
| 133 | * @param object Object to query the index of. | 143 | * @param object Object to query the index of. |
| 134 | */ | 144 | */ |
| 135 | s32 GetWaitObjectIndex(const WaitObject* object) const { | 145 | s32 GetWaitObjectIndex(WaitObject* object) const; |
| 136 | return wait_objects_index.at(object->GetObjectId()); | ||
| 137 | } | ||
| 138 | 146 | ||
| 139 | /** | 147 | /** |
| 140 | * Stops a thread, invalidating it from further use | 148 | * Stops a thread, invalidating it from further use |
| @@ -152,10 +160,10 @@ public: | |||
| 152 | /** | 160 | /** |
| 153 | * Returns whether this thread is waiting for all the objects in | 161 | * Returns whether this thread is waiting for all the objects in |
| 154 | * its wait list to become ready, as a result of a WaitSynchronizationN call | 162 | * its wait list to become ready, as a result of a WaitSynchronizationN call |
| 155 | * with wait_all = true, or a ReplyAndReceive call. | 163 | * with wait_all = true. |
| 156 | */ | 164 | */ |
| 157 | bool IsSleepingOnWaitAll() const { | 165 | bool IsSleepingOnWaitAll() const { |
| 158 | return !wait_objects.empty(); | 166 | return status == THREADSTATUS_WAIT_SYNCH_ALL; |
| 159 | } | 167 | } |
| 160 | 168 | ||
| 161 | ARM_Interface::ThreadContext context; | 169 | ARM_Interface::ThreadContext context; |
| @@ -178,15 +186,15 @@ public: | |||
| 178 | /// Mutexes currently held by this thread, which will be released when it exits. | 186 | /// Mutexes currently held by this thread, which will be released when it exits. |
| 179 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; | 187 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; |
| 180 | 188 | ||
| 189 | /// Mutexes that this thread is currently waiting for. | ||
| 190 | boost::container::flat_set<SharedPtr<Mutex>> pending_mutexes; | ||
| 191 | |||
| 181 | SharedPtr<Process> owner_process; ///< Process that owns this thread | 192 | SharedPtr<Process> owner_process; ///< Process that owns this thread |
| 182 | 193 | ||
| 183 | /// Objects that the thread is waiting on. | 194 | /// Objects that the thread is waiting on, in the same order as they were |
| 184 | /// This is only populated when the thread should wait for all the objects to become ready. | 195 | // passed to WaitSynchronization1/N. |
| 185 | std::vector<SharedPtr<WaitObject>> wait_objects; | 196 | std::vector<SharedPtr<WaitObject>> wait_objects; |
| 186 | 197 | ||
| 187 | /// Mapping of Object ids to their position in the last waitlist that this object waited on. | ||
| 188 | boost::container::flat_map<int, s32> wait_objects_index; | ||
| 189 | |||
| 190 | VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address | 198 | VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address |
| 191 | 199 | ||
| 192 | /// True if the WaitSynchronizationN output parameter should be set on thread wakeup. | 200 | /// True if the WaitSynchronizationN output parameter should be set on thread wakeup. |
| @@ -211,6 +219,11 @@ private: | |||
| 211 | SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority); | 219 | SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority); |
| 212 | 220 | ||
| 213 | /** | 221 | /** |
| 222 | * Returns whether there are any threads that are ready to run. | ||
| 223 | */ | ||
| 224 | bool HaveReadyThreads(); | ||
| 225 | |||
| 226 | /** | ||
| 214 | * Reschedules to the next available thread (call after current thread is suspended) | 227 | * Reschedules to the next available thread (call after current thread is suspended) |
| 215 | */ | 228 | */ |
| 216 | void Reschedule(); | 229 | void Reschedule(); |
| @@ -238,15 +251,6 @@ Thread* GetCurrentThread(); | |||
| 238 | void WaitCurrentThread_Sleep(); | 251 | void WaitCurrentThread_Sleep(); |
| 239 | 252 | ||
| 240 | /** | 253 | /** |
| 241 | * Waits the current thread from a WaitSynchronization call | ||
| 242 | * @param wait_objects Kernel objects that we are waiting on | ||
| 243 | * @param wait_set_output If true, set the output parameter on thread wakeup (for | ||
| 244 | * WaitSynchronizationN only) | ||
| 245 | */ | ||
| 246 | void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wait_objects, | ||
| 247 | bool wait_set_output); | ||
| 248 | |||
| 249 | /** | ||
| 250 | * Waits the current thread from an ArbitrateAddress call | 254 | * Waits the current thread from an ArbitrateAddress call |
| 251 | * @param wait_address Arbitration address used to resume from wait | 255 | * @param wait_address Arbitration address used to resume from wait |
| 252 | */ | 256 | */ |
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index ec85b7ec8..60537f355 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp | |||
| @@ -34,12 +34,12 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) { | |||
| 34 | return timer; | 34 | return timer; |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | bool Timer::ShouldWait() { | 37 | bool Timer::ShouldWait(Thread* thread) const { |
| 38 | return !signaled; | 38 | return !signaled; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | void Timer::Acquire() { | 41 | void Timer::Acquire(Thread* thread) { |
| 42 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 42 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 43 | 43 | ||
| 44 | if (reset_type == ResetType::OneShot) | 44 | if (reset_type == ResetType::OneShot) |
| 45 | signaled = false; | 45 | signaled = false; |
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index 2092165d4..c174f5664 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h | |||
| @@ -39,8 +39,8 @@ public: | |||
| 39 | u64 initial_delay; ///< The delay until the timer fires for the first time | 39 | u64 initial_delay; ///< The delay until the timer fires for the first time |
| 40 | u64 interval_delay; ///< The delay until the timer fires after the first time | 40 | u64 interval_delay; ///< The delay until the timer fires after the first time |
| 41 | 41 | ||
| 42 | bool ShouldWait() override; | 42 | bool ShouldWait(Thread* thread) const override; |
| 43 | void Acquire() override; | 43 | void Acquire(Thread* thread) override; |
| 44 | 44 | ||
| 45 | void WakeupAllWaitingThreads() override; | 45 | void WakeupAllWaitingThreads() override; |
| 46 | 46 | ||
diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp index 6ab16ccd5..e0de037f8 100644 --- a/src/core/hle/service/boss/boss.cpp +++ b/src/core/hle/service/boss/boss.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cinttypes> | ||
| 5 | #include "core/hle/service/boss/boss.h" | 6 | #include "core/hle/service/boss/boss.h" |
| 6 | #include "core/hle/service/boss/boss_p.h" | 7 | #include "core/hle/service/boss/boss_p.h" |
| 7 | #include "core/hle/service/boss/boss_u.h" | 8 | #include "core/hle/service/boss/boss_u.h" |
| @@ -33,7 +34,8 @@ void InitializeSession(Service::Interface* self) { | |||
| 33 | cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); | 34 | cmd_buff[0] = IPC::MakeHeader(0x1, 0x1, 0); |
| 34 | cmd_buff[1] = RESULT_SUCCESS.raw; | 35 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 35 | 36 | ||
| 36 | LOG_WARNING(Service_BOSS, "(STUBBED) unk_param=0x%016X, translation=0x%08X, unk_param4=0x%08X", | 37 | LOG_WARNING(Service_BOSS, |
| 38 | "(STUBBED) unk_param=0x%016" PRIX64 ", translation=0x%08X, unk_param4=0x%08X", | ||
| 37 | unk_param, translation, unk_param4); | 39 | unk_param, translation, unk_param4); |
| 38 | } | 40 | } |
| 39 | 41 | ||
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 0bf59eb76..59dd6d1cd 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp | |||
| @@ -84,7 +84,6 @@ struct ConsoleCountryInfo { | |||
| 84 | static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes"); | 84 | static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes"); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | static const u64 CFG_SAVE_ID = 0x00010017; | ||
| 88 | static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE; | 87 | static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE; |
| 89 | static const ConsoleModelInfo CONSOLE_MODEL = {NINTENDO_3DS_XL, {0, 0, 0}}; | 88 | static const ConsoleModelInfo CONSOLE_MODEL = {NINTENDO_3DS_XL, {0, 0, 0}}; |
| 90 | static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; | 89 | static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; |
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 4f1dd2fce..c62f8afc6 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp | |||
| @@ -99,7 +99,8 @@ static void StartSampling(Interface* self) { | |||
| 99 | is_sampling = true; | 99 | is_sampling = true; |
| 100 | LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, " | 100 | LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, " |
| 101 | "audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u", | 101 | "audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u", |
| 102 | encoding, sample_rate, audio_buffer_offset, audio_buffer_size, audio_buffer_loop); | 102 | static_cast<u32>(encoding), static_cast<u32>(sample_rate), audio_buffer_offset, |
| 103 | audio_buffer_size, audio_buffer_loop); | ||
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | /** | 106 | /** |
| @@ -114,7 +115,7 @@ static void AdjustSampling(Interface* self) { | |||
| 114 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 115 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 115 | sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF); | 116 | sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF); |
| 116 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | 117 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error |
| 117 | LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", sample_rate); | 118 | LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", static_cast<u32>(sample_rate)); |
| 118 | } | 119 | } |
| 119 | 120 | ||
| 120 | /** | 121 | /** |
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index d9738c6a1..e248285f9 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hle/kernel/event.h" | ||
| 5 | #include "core/hle/service/nfc/nfc.h" | 6 | #include "core/hle/service/nfc/nfc.h" |
| 6 | #include "core/hle/service/nfc/nfc_m.h" | 7 | #include "core/hle/service/nfc/nfc_m.h" |
| 7 | #include "core/hle/service/nfc/nfc_u.h" | 8 | #include "core/hle/service/nfc/nfc_u.h" |
| @@ -9,9 +10,28 @@ | |||
| 9 | namespace Service { | 10 | namespace Service { |
| 10 | namespace NFC { | 11 | namespace NFC { |
| 11 | 12 | ||
| 13 | static Kernel::SharedPtr<Kernel::Event> tag_in_range_event; | ||
| 14 | |||
| 15 | void GetTagInRangeEvent(Interface* self) { | ||
| 16 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 17 | |||
| 18 | cmd_buff[0] = IPC::MakeHeader(0xB, 1, 2); | ||
| 19 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 20 | cmd_buff[2] = IPC::CopyHandleDesc(); | ||
| 21 | cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).MoveFrom(); | ||
| 22 | LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||
| 23 | } | ||
| 24 | |||
| 12 | void Init() { | 25 | void Init() { |
| 13 | AddService(new NFC_M()); | 26 | AddService(new NFC_M()); |
| 14 | AddService(new NFC_U()); | 27 | AddService(new NFC_U()); |
| 28 | |||
| 29 | tag_in_range_event = | ||
| 30 | Kernel::Event::Create(Kernel::ResetType::OneShot, "NFC::tag_in_range_event"); | ||
| 31 | } | ||
| 32 | |||
| 33 | void Shutdown() { | ||
| 34 | tag_in_range_event = nullptr; | ||
| 15 | } | 35 | } |
| 16 | 36 | ||
| 17 | } // namespace NFC | 37 | } // namespace NFC |
diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h index cd65a5fdc..b02354201 100644 --- a/src/core/hle/service/nfc/nfc.h +++ b/src/core/hle/service/nfc/nfc.h | |||
| @@ -5,10 +5,27 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | namespace Service { | 7 | namespace Service { |
| 8 | |||
| 9 | class Interface; | ||
| 10 | |||
| 8 | namespace NFC { | 11 | namespace NFC { |
| 9 | 12 | ||
| 13 | /** | ||
| 14 | * NFC::GetTagInRangeEvent service function | ||
| 15 | * Inputs: | ||
| 16 | * 0 : Header code [0x000B0000] | ||
| 17 | * Outputs: | ||
| 18 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 19 | * 2 : Copy handle descriptor | ||
| 20 | * 3 : Event Handle | ||
| 21 | */ | ||
| 22 | void GetTagInRangeEvent(Interface* self); | ||
| 23 | |||
| 10 | /// Initialize all NFC services. | 24 | /// Initialize all NFC services. |
| 11 | void Init(); | 25 | void Init(); |
| 12 | 26 | ||
| 27 | /// Shutdown all NFC services. | ||
| 28 | void Shutdown(); | ||
| 29 | |||
| 13 | } // namespace NFC | 30 | } // namespace NFC |
| 14 | } // namespace Service | 31 | } // namespace Service |
diff --git a/src/core/hle/service/nfc/nfc_m.cpp b/src/core/hle/service/nfc/nfc_m.cpp index 717335c11..f43b4029a 100644 --- a/src/core/hle/service/nfc/nfc_m.cpp +++ b/src/core/hle/service/nfc/nfc_m.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hle/service/nfc/nfc.h" | ||
| 5 | #include "core/hle/service/nfc/nfc_m.h" | 6 | #include "core/hle/service/nfc/nfc_m.h" |
| 6 | 7 | ||
| 7 | namespace Service { | 8 | namespace Service { |
| @@ -19,6 +20,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 19 | {0x00070000, nullptr, "LoadAmiiboData"}, | 20 | {0x00070000, nullptr, "LoadAmiiboData"}, |
| 20 | {0x00080000, nullptr, "ResetTagScanState"}, | 21 | {0x00080000, nullptr, "ResetTagScanState"}, |
| 21 | {0x00090002, nullptr, "UpdateStoredAmiiboData"}, | 22 | {0x00090002, nullptr, "UpdateStoredAmiiboData"}, |
| 23 | {0x000B0000, GetTagInRangeEvent, "GetTagInRangeEvent"}, | ||
| 22 | {0x000D0000, nullptr, "GetTagState"}, | 24 | {0x000D0000, nullptr, "GetTagState"}, |
| 23 | {0x000F0000, nullptr, "CommunicationGetStatus"}, | 25 | {0x000F0000, nullptr, "CommunicationGetStatus"}, |
| 24 | {0x00100000, nullptr, "GetTagInfo2"}, | 26 | {0x00100000, nullptr, "GetTagInfo2"}, |
diff --git a/src/core/hle/service/nfc/nfc_u.cpp b/src/core/hle/service/nfc/nfc_u.cpp index deffb0b4f..4b5200ae8 100644 --- a/src/core/hle/service/nfc/nfc_u.cpp +++ b/src/core/hle/service/nfc/nfc_u.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/hle/service/nfc/nfc.h" | ||
| 5 | #include "core/hle/service/nfc/nfc_u.h" | 6 | #include "core/hle/service/nfc/nfc_u.h" |
| 6 | 7 | ||
| 7 | namespace Service { | 8 | namespace Service { |
| @@ -18,6 +19,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 18 | {0x00070000, nullptr, "LoadAmiiboData"}, | 19 | {0x00070000, nullptr, "LoadAmiiboData"}, |
| 19 | {0x00080000, nullptr, "ResetTagScanState"}, | 20 | {0x00080000, nullptr, "ResetTagScanState"}, |
| 20 | {0x00090002, nullptr, "UpdateStoredAmiiboData"}, | 21 | {0x00090002, nullptr, "UpdateStoredAmiiboData"}, |
| 22 | {0x000B0000, GetTagInRangeEvent, "GetTagInRangeEvent"}, | ||
| 21 | {0x000D0000, nullptr, "GetTagState"}, | 23 | {0x000D0000, nullptr, "GetTagState"}, |
| 22 | {0x000F0000, nullptr, "CommunicationGetStatus"}, | 24 | {0x000F0000, nullptr, "CommunicationGetStatus"}, |
| 23 | {0x00100000, nullptr, "GetTagInfo2"}, | 25 | {0x00100000, nullptr, "GetTagInfo2"}, |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 7e52a05d9..f3190e0fa 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -178,6 +178,7 @@ void Init() { | |||
| 178 | /// Shutdown ServiceManager | 178 | /// Shutdown ServiceManager |
| 179 | void Shutdown() { | 179 | void Shutdown() { |
| 180 | PTM::Shutdown(); | 180 | PTM::Shutdown(); |
| 181 | NFC::Shutdown(); | ||
| 181 | NIM::Shutdown(); | 182 | NIM::Shutdown(); |
| 182 | NEWS::Shutdown(); | 183 | NEWS::Shutdown(); |
| 183 | NDM::Shutdown(); | 184 | NDM::Shutdown(); |
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index c3918cdd0..dcc5c3c90 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp | |||
| @@ -603,7 +603,6 @@ static void RecvFrom(Interface* self) { | |||
| 603 | u32 socket_handle = cmd_buffer[1]; | 603 | u32 socket_handle = cmd_buffer[1]; |
| 604 | u32 len = cmd_buffer[2]; | 604 | u32 len = cmd_buffer[2]; |
| 605 | u32 flags = cmd_buffer[3]; | 605 | u32 flags = cmd_buffer[3]; |
| 606 | socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]); | ||
| 607 | 606 | ||
| 608 | struct { | 607 | struct { |
| 609 | u32 output_buffer_descriptor; | 608 | u32 output_buffer_descriptor; |
| @@ -693,7 +692,6 @@ static void Poll(Interface* self) { | |||
| 693 | static void GetSockName(Interface* self) { | 692 | static void GetSockName(Interface* self) { |
| 694 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | 693 | u32* cmd_buffer = Kernel::GetCommandBuffer(); |
| 695 | u32 socket_handle = cmd_buffer[1]; | 694 | u32 socket_handle = cmd_buffer[1]; |
| 696 | socklen_t ctr_len = cmd_buffer[2]; | ||
| 697 | 695 | ||
| 698 | // Memory address of the ctr_dest_addr structure | 696 | // Memory address of the ctr_dest_addr structure |
| 699 | VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2]; | 697 | VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2]; |
| @@ -734,7 +732,6 @@ static void Shutdown(Interface* self) { | |||
| 734 | static void GetPeerName(Interface* self) { | 732 | static void GetPeerName(Interface* self) { |
| 735 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | 733 | u32* cmd_buffer = Kernel::GetCommandBuffer(); |
| 736 | u32 socket_handle = cmd_buffer[1]; | 734 | u32 socket_handle = cmd_buffer[1]; |
| 737 | socklen_t len = cmd_buffer[2]; | ||
| 738 | 735 | ||
| 739 | // Memory address of the ctr_dest_addr structure | 736 | // Memory address of the ctr_dest_addr structure |
| 740 | VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2]; | 737 | VAddr ctr_dest_addr_addr = cmd_buffer[0x104 >> 2]; |
| @@ -765,7 +762,6 @@ static void Connect(Interface* self) { | |||
| 765 | // performing nonblocking operations and spinlock until the data is available | 762 | // performing nonblocking operations and spinlock until the data is available |
| 766 | u32* cmd_buffer = Kernel::GetCommandBuffer(); | 763 | u32* cmd_buffer = Kernel::GetCommandBuffer(); |
| 767 | u32 socket_handle = cmd_buffer[1]; | 764 | u32 socket_handle = cmd_buffer[1]; |
| 768 | socklen_t len = cmd_buffer[2]; | ||
| 769 | 765 | ||
| 770 | // Memory address of the ctr_input_addr structure | 766 | // Memory address of the ctr_input_addr structure |
| 771 | VAddr ctr_input_addr_addr = cmd_buffer[6]; | 767 | VAddr ctr_input_addr_addr = cmd_buffer[6]; |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 2ca270de3..2b242ff98 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp | |||
| @@ -248,6 +248,8 @@ static ResultCode SendSyncRequest(Kernel::Handle handle) { | |||
| 248 | 248 | ||
| 249 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); | 249 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); |
| 250 | 250 | ||
| 251 | Core::System::GetInstance().PrepareReschedule(); | ||
| 252 | |||
| 251 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server | 253 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server |
| 252 | // responds and cause a reschedule. | 254 | // responds and cause a reschedule. |
| 253 | return session->SendSyncRequest(); | 255 | return session->SendSyncRequest(); |
| @@ -270,27 +272,27 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) | |||
| 270 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, | 272 | LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, |
| 271 | object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); | 273 | object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); |
| 272 | 274 | ||
| 273 | if (object->ShouldWait()) { | 275 | if (object->ShouldWait(thread)) { |
| 274 | 276 | ||
| 275 | if (nano_seconds == 0) | 277 | if (nano_seconds == 0) |
| 276 | return ERR_SYNC_TIMEOUT; | 278 | return ERR_SYNC_TIMEOUT; |
| 277 | 279 | ||
| 280 | thread->wait_objects = {object}; | ||
| 278 | object->AddWaitingThread(thread); | 281 | object->AddWaitingThread(thread); |
| 279 | // TODO(Subv): Perform things like update the mutex lock owner's priority to | 282 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
| 280 | // prevent priority inversion. Currently this is done in Mutex::ShouldWait, | ||
| 281 | // but it should be moved to a function that is called from here. | ||
| 282 | thread->status = THREADSTATUS_WAIT_SYNCH; | ||
| 283 | 283 | ||
| 284 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 284 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 285 | thread->WakeAfterDelay(nano_seconds); | 285 | thread->WakeAfterDelay(nano_seconds); |
| 286 | 286 | ||
| 287 | Core::System::GetInstance().PrepareReschedule(); | ||
| 288 | |||
| 287 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread | 289 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread |
| 288 | // resumes due to a signal in its wait objects. | 290 | // resumes due to a signal in its wait objects. |
| 289 | // Otherwise we retain the default value of timeout. | 291 | // Otherwise we retain the default value of timeout. |
| 290 | return ERR_SYNC_TIMEOUT; | 292 | return ERR_SYNC_TIMEOUT; |
| 291 | } | 293 | } |
| 292 | 294 | ||
| 293 | object->Acquire(); | 295 | object->Acquire(thread); |
| 294 | 296 | ||
| 295 | return RESULT_SUCCESS; | 297 | return RESULT_SUCCESS; |
| 296 | } | 298 | } |
| @@ -324,19 +326,14 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 324 | objects[i] = object; | 326 | objects[i] = object; |
| 325 | } | 327 | } |
| 326 | 328 | ||
| 327 | // Clear the mapping of wait object indices. | ||
| 328 | // We don't want any lingering state in this map. | ||
| 329 | // It will be repopulated later in the wait_all = false case. | ||
| 330 | thread->wait_objects_index.clear(); | ||
| 331 | |||
| 332 | if (wait_all) { | 329 | if (wait_all) { |
| 333 | bool all_available = | 330 | bool all_available = |
| 334 | std::all_of(objects.begin(), objects.end(), | 331 | std::all_of(objects.begin(), objects.end(), |
| 335 | [](const ObjectPtr& object) { return !object->ShouldWait(); }); | 332 | [thread](const ObjectPtr& object) { return !object->ShouldWait(thread); }); |
| 336 | if (all_available) { | 333 | if (all_available) { |
| 337 | // We can acquire all objects right now, do so. | 334 | // We can acquire all objects right now, do so. |
| 338 | for (auto& object : objects) | 335 | for (auto& object : objects) |
| 339 | object->Acquire(); | 336 | object->Acquire(thread); |
| 340 | // Note: In this case, the `out` parameter is not set, | 337 | // Note: In this case, the `out` parameter is not set, |
| 341 | // and retains whatever value it had before. | 338 | // and retains whatever value it had before. |
| 342 | return RESULT_SUCCESS; | 339 | return RESULT_SUCCESS; |
| @@ -350,22 +347,20 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 350 | return ERR_SYNC_TIMEOUT; | 347 | return ERR_SYNC_TIMEOUT; |
| 351 | 348 | ||
| 352 | // Put the thread to sleep | 349 | // Put the thread to sleep |
| 353 | thread->status = THREADSTATUS_WAIT_SYNCH; | 350 | thread->status = THREADSTATUS_WAIT_SYNCH_ALL; |
| 354 | 351 | ||
| 355 | // Add the thread to each of the objects' waiting threads. | 352 | // Add the thread to each of the objects' waiting threads. |
| 356 | for (auto& object : objects) { | 353 | for (auto& object : objects) { |
| 357 | object->AddWaitingThread(thread); | 354 | object->AddWaitingThread(thread); |
| 358 | // TODO(Subv): Perform things like update the mutex lock owner's priority to | ||
| 359 | // prevent priority inversion. Currently this is done in Mutex::ShouldWait, | ||
| 360 | // but it should be moved to a function that is called from here. | ||
| 361 | } | 355 | } |
| 362 | 356 | ||
| 363 | // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN | ||
| 364 | thread->wait_objects = std::move(objects); | 357 | thread->wait_objects = std::move(objects); |
| 365 | 358 | ||
| 366 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 359 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 367 | thread->WakeAfterDelay(nano_seconds); | 360 | thread->WakeAfterDelay(nano_seconds); |
| 368 | 361 | ||
| 362 | Core::System::GetInstance().PrepareReschedule(); | ||
| 363 | |||
| 369 | // This value gets set to -1 by default in this case, it is not modified after this. | 364 | // This value gets set to -1 by default in this case, it is not modified after this. |
| 370 | *out = -1; | 365 | *out = -1; |
| 371 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to | 366 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to |
| @@ -373,13 +368,14 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 373 | return ERR_SYNC_TIMEOUT; | 368 | return ERR_SYNC_TIMEOUT; |
| 374 | } else { | 369 | } else { |
| 375 | // Find the first object that is acquirable in the provided list of objects | 370 | // Find the first object that is acquirable in the provided list of objects |
| 376 | auto itr = std::find_if(objects.begin(), objects.end(), | 371 | auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { |
| 377 | [](const ObjectPtr& object) { return !object->ShouldWait(); }); | 372 | return !object->ShouldWait(thread); |
| 373 | }); | ||
| 378 | 374 | ||
| 379 | if (itr != objects.end()) { | 375 | if (itr != objects.end()) { |
| 380 | // We found a ready object, acquire it and set the result value | 376 | // We found a ready object, acquire it and set the result value |
| 381 | Kernel::WaitObject* object = itr->get(); | 377 | Kernel::WaitObject* object = itr->get(); |
| 382 | object->Acquire(); | 378 | object->Acquire(thread); |
| 383 | *out = std::distance(objects.begin(), itr); | 379 | *out = std::distance(objects.begin(), itr); |
| 384 | return RESULT_SUCCESS; | 380 | return RESULT_SUCCESS; |
| 385 | } | 381 | } |
| @@ -392,28 +388,24 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 392 | return ERR_SYNC_TIMEOUT; | 388 | return ERR_SYNC_TIMEOUT; |
| 393 | 389 | ||
| 394 | // Put the thread to sleep | 390 | // Put the thread to sleep |
| 395 | thread->status = THREADSTATUS_WAIT_SYNCH; | 391 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
| 396 | |||
| 397 | // Clear the thread's waitlist, we won't use it for wait_all = false | ||
| 398 | thread->wait_objects.clear(); | ||
| 399 | 392 | ||
| 400 | // Add the thread to each of the objects' waiting threads. | 393 | // Add the thread to each of the objects' waiting threads. |
| 401 | for (size_t i = 0; i < objects.size(); ++i) { | 394 | for (size_t i = 0; i < objects.size(); ++i) { |
| 402 | Kernel::WaitObject* object = objects[i].get(); | 395 | Kernel::WaitObject* object = objects[i].get(); |
| 403 | // Set the index of this object in the mapping of Objects -> index for this thread. | ||
| 404 | thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i); | ||
| 405 | object->AddWaitingThread(thread); | 396 | object->AddWaitingThread(thread); |
| 406 | // TODO(Subv): Perform things like update the mutex lock owner's priority to | ||
| 407 | // prevent priority inversion. Currently this is done in Mutex::ShouldWait, | ||
| 408 | // but it should be moved to a function that is called from here. | ||
| 409 | } | 397 | } |
| 410 | 398 | ||
| 399 | thread->wait_objects = std::move(objects); | ||
| 400 | |||
| 411 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is | 401 | // Note: If no handles and no timeout were given, then the thread will deadlock, this is |
| 412 | // consistent with hardware behavior. | 402 | // consistent with hardware behavior. |
| 413 | 403 | ||
| 414 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 404 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 415 | thread->WakeAfterDelay(nano_seconds); | 405 | thread->WakeAfterDelay(nano_seconds); |
| 416 | 406 | ||
| 407 | Core::System::GetInstance().PrepareReschedule(); | ||
| 408 | |||
| 417 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | 409 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a |
| 418 | // signal in one of its wait objects. | 410 | // signal in one of its wait objects. |
| 419 | // Otherwise we retain the default value of timeout, and -1 in the out parameter | 411 | // Otherwise we retain the default value of timeout, and -1 in the out parameter |
| @@ -448,6 +440,9 @@ static ResultCode ArbitrateAddress(Kernel::Handle handle, u32 address, u32 type, | |||
| 448 | auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), address, value, | 440 | auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), address, value, |
| 449 | nanoseconds); | 441 | nanoseconds); |
| 450 | 442 | ||
| 443 | // TODO(Subv): Identify in which specific cases this call should cause a reschedule. | ||
| 444 | Core::System::GetInstance().PrepareReschedule(); | ||
| 445 | |||
| 451 | return res; | 446 | return res; |
| 452 | } | 447 | } |
| 453 | 448 | ||
| @@ -574,6 +569,8 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent | |||
| 574 | 569 | ||
| 575 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); | 570 | CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); |
| 576 | 571 | ||
| 572 | Core::System::GetInstance().PrepareReschedule(); | ||
| 573 | |||
| 577 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " | 574 | LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " |
| 578 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", | 575 | "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", |
| 579 | entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); | 576 | entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); |
| @@ -586,6 +583,7 @@ static void ExitThread() { | |||
| 586 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); | 583 | LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); |
| 587 | 584 | ||
| 588 | Kernel::ExitCurrentThread(); | 585 | Kernel::ExitCurrentThread(); |
| 586 | Core::System::GetInstance().PrepareReschedule(); | ||
| 589 | } | 587 | } |
| 590 | 588 | ||
| 591 | /// Gets the priority for the specified thread | 589 | /// Gets the priority for the specified thread |
| @@ -605,6 +603,13 @@ static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { | |||
| 605 | return ERR_INVALID_HANDLE; | 603 | return ERR_INVALID_HANDLE; |
| 606 | 604 | ||
| 607 | thread->SetPriority(priority); | 605 | thread->SetPriority(priority); |
| 606 | thread->UpdatePriority(); | ||
| 607 | |||
| 608 | // Update the mutexes that this thread is waiting for | ||
| 609 | for (auto& mutex : thread->pending_mutexes) | ||
| 610 | mutex->UpdatePriority(); | ||
| 611 | |||
| 612 | Core::System::GetInstance().PrepareReschedule(); | ||
| 608 | return RESULT_SUCCESS; | 613 | return RESULT_SUCCESS; |
| 609 | } | 614 | } |
| 610 | 615 | ||
| @@ -844,11 +849,18 @@ static ResultCode CancelTimer(Kernel::Handle handle) { | |||
| 844 | static void SleepThread(s64 nanoseconds) { | 849 | static void SleepThread(s64 nanoseconds) { |
| 845 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); | 850 | LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); |
| 846 | 851 | ||
| 852 | // Don't attempt to yield execution if there are no available threads to run, | ||
| 853 | // this way we avoid a useless reschedule to the idle thread. | ||
| 854 | if (nanoseconds == 0 && !Kernel::HaveReadyThreads()) | ||
| 855 | return; | ||
| 856 | |||
| 847 | // Sleep current thread and check for next thread to schedule | 857 | // Sleep current thread and check for next thread to schedule |
| 848 | Kernel::WaitCurrentThread_Sleep(); | 858 | Kernel::WaitCurrentThread_Sleep(); |
| 849 | 859 | ||
| 850 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 860 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 851 | Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds); | 861 | Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds); |
| 862 | |||
| 863 | Core::System::GetInstance().PrepareReschedule(); | ||
| 852 | } | 864 | } |
| 853 | 865 | ||
| 854 | /// This returns the total CPU ticks elapsed since the CPU was powered-on | 866 | /// This returns the total CPU ticks elapsed since the CPU was powered-on |
| @@ -890,7 +902,11 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si | |||
| 890 | return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, | 902 | return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, |
| 891 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); | 903 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); |
| 892 | 904 | ||
| 893 | if (addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) { | 905 | // TODO(Subv): Processes with memory type APPLICATION are not allowed |
| 906 | // to create memory blocks with addr = 0, any attempts to do so | ||
| 907 | // should return error 0xD92007EA. | ||
| 908 | if ((addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) && | ||
| 909 | addr != 0) { | ||
| 894 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, | 910 | return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, |
| 895 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); | 911 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); |
| 896 | } | 912 | } |
| @@ -1184,8 +1200,6 @@ void CallSVC(u32 immediate) { | |||
| 1184 | if (info) { | 1200 | if (info) { |
| 1185 | if (info->func) { | 1201 | if (info->func) { |
| 1186 | info->func(); | 1202 | info->func(); |
| 1187 | // TODO(Subv): Not all service functions should cause a reschedule in all cases. | ||
| 1188 | Core::System::GetInstance().PrepareReschedule(); | ||
| 1189 | } else { | 1203 | } else { |
| 1190 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); | 1204 | LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); |
| 1191 | } | 1205 | } |
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index a204dc336..5df33f6d2 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp | |||
| @@ -288,7 +288,7 @@ ResultStatus AppLoader_NCCH::LoadExeFS() { | |||
| 288 | LOG_DEBUG(Loader, "Thread priority: 0x%X", priority); | 288 | LOG_DEBUG(Loader, "Thread priority: 0x%X", priority); |
| 289 | LOG_DEBUG(Loader, "Resource limit category: %d", resource_limit_category); | 289 | LOG_DEBUG(Loader, "Resource limit category: %d", resource_limit_category); |
| 290 | LOG_DEBUG(Loader, "System Mode: %d", | 290 | LOG_DEBUG(Loader, "System Mode: %d", |
| 291 | exheader_header.arm11_system_local_caps.system_mode); | 291 | static_cast<int>(exheader_header.arm11_system_local_caps.system_mode)); |
| 292 | 292 | ||
| 293 | if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) { | 293 | if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) { |
| 294 | LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted."); | 294 | LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted."); |
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index fe08f5b45..4ef95b5c6 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h | |||
| @@ -181,7 +181,7 @@ public: | |||
| 181 | * Loads the Exheader and returns the system mode for this application. | 181 | * Loads the Exheader and returns the system mode for this application. |
| 182 | * @return Optional with the kernel system mode | 182 | * @return Optional with the kernel system mode |
| 183 | */ | 183 | */ |
| 184 | boost::optional<u32> LoadKernelSystemMode(); | 184 | boost::optional<u32> LoadKernelSystemMode() override; |
| 185 | 185 | ||
| 186 | ResultStatus ReadCode(std::vector<u8>& buffer) override; | 186 | ResultStatus ReadCode(std::vector<u8>& buffer) override; |
| 187 | 187 | ||
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 5d23c52f9..9afaf79ec 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -20,7 +20,6 @@ void Apply() { | |||
| 20 | 20 | ||
| 21 | VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; | 21 | VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; |
| 22 | VideoCore::g_shader_jit_enabled = values.use_shader_jit; | 22 | VideoCore::g_shader_jit_enabled = values.use_shader_jit; |
| 23 | VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; | ||
| 24 | VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit; | 23 | VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit; |
| 25 | 24 | ||
| 26 | if (VideoCore::g_emu_window) { | 25 | if (VideoCore::g_emu_window) { |
diff --git a/src/core/settings.h b/src/core/settings.h index 4e7a4b1be..8dbda653a 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -88,7 +88,7 @@ struct Values { | |||
| 88 | // Renderer | 88 | // Renderer |
| 89 | bool use_hw_renderer; | 89 | bool use_hw_renderer; |
| 90 | bool use_shader_jit; | 90 | bool use_shader_jit; |
| 91 | bool use_scaled_resolution; | 91 | float resolution_factor; |
| 92 | bool use_vsync; | 92 | bool use_vsync; |
| 93 | bool toggle_framelimit; | 93 | bool toggle_framelimit; |
| 94 | 94 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 85aa06cd5..ef3b06a7b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -556,14 +556,21 @@ RasterizerCacheOpenGL::GetFramebufferSurfaces(const Pica::Regs::FramebufferConfi | |||
| 556 | color_params.width = depth_params.width = config.GetWidth(); | 556 | color_params.width = depth_params.width = config.GetWidth(); |
| 557 | color_params.height = depth_params.height = config.GetHeight(); | 557 | color_params.height = depth_params.height = config.GetHeight(); |
| 558 | color_params.is_tiled = depth_params.is_tiled = true; | 558 | color_params.is_tiled = depth_params.is_tiled = true; |
| 559 | if (VideoCore::g_scaled_resolution_enabled) { | ||
| 560 | auto layout = VideoCore::g_emu_window->GetFramebufferLayout(); | ||
| 561 | 559 | ||
| 562 | // Assume same scaling factor for top and bottom screens | 560 | // Set the internal resolution, assume the same scaling factor for top and bottom screens |
| 561 | const Layout::FramebufferLayout& layout = VideoCore::g_emu_window->GetFramebufferLayout(); | ||
| 562 | if (Settings::values.resolution_factor == 0.0f) { | ||
| 563 | // Auto - scale resolution to the window size | ||
| 563 | color_params.res_scale_width = depth_params.res_scale_width = | 564 | color_params.res_scale_width = depth_params.res_scale_width = |
| 564 | (float)layout.top_screen.GetWidth() / VideoCore::kScreenTopWidth; | 565 | (float)layout.top_screen.GetWidth() / VideoCore::kScreenTopWidth; |
| 565 | color_params.res_scale_height = depth_params.res_scale_height = | 566 | color_params.res_scale_height = depth_params.res_scale_height = |
| 566 | (float)layout.top_screen.GetHeight() / VideoCore::kScreenTopHeight; | 567 | (float)layout.top_screen.GetHeight() / VideoCore::kScreenTopHeight; |
| 568 | } else { | ||
| 569 | // Otherwise, scale the resolution by the specified factor | ||
| 570 | color_params.res_scale_width = Settings::values.resolution_factor; | ||
| 571 | depth_params.res_scale_width = Settings::values.resolution_factor; | ||
| 572 | color_params.res_scale_height = Settings::values.resolution_factor; | ||
| 573 | depth_params.res_scale_height = Settings::values.resolution_factor; | ||
| 567 | } | 574 | } |
| 568 | 575 | ||
| 569 | color_params.addr = config.GetColorBufferPhysicalAddress(); | 576 | color_params.addr = config.GetColorBufferPhysicalAddress(); |
diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp index 70db4167e..20fb9754b 100644 --- a/src/video_core/shader/shader_interpreter.cpp +++ b/src/video_core/shader/shader_interpreter.cpp | |||
| @@ -27,8 +27,6 @@ namespace Pica { | |||
| 27 | 27 | ||
| 28 | namespace Shader { | 28 | namespace Shader { |
| 29 | 29 | ||
| 30 | constexpr u32 INVALID_ADDRESS = 0xFFFFFFFF; | ||
| 31 | |||
| 32 | struct CallStackElement { | 30 | struct CallStackElement { |
| 33 | u32 final_address; // Address upon which we jump to return_address | 31 | u32 final_address; // Address upon which we jump to return_address |
| 34 | u32 return_address; // Where to jump when leaving scope | 32 | u32 return_address; // Where to jump when leaving scope |
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 8db882f59..7186a7652 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -19,7 +19,6 @@ std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin | |||
| 19 | 19 | ||
| 20 | std::atomic<bool> g_hw_renderer_enabled; | 20 | std::atomic<bool> g_hw_renderer_enabled; |
| 21 | std::atomic<bool> g_shader_jit_enabled; | 21 | std::atomic<bool> g_shader_jit_enabled; |
| 22 | std::atomic<bool> g_scaled_resolution_enabled; | ||
| 23 | std::atomic<bool> g_vsync_enabled; | 22 | std::atomic<bool> g_vsync_enabled; |
| 24 | std::atomic<bool> g_toggle_framelimit_enabled; | 23 | std::atomic<bool> g_toggle_framelimit_enabled; |
| 25 | 24 | ||
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index c397c1974..4aba19ca0 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h | |||
| @@ -37,7 +37,6 @@ extern EmuWindow* g_emu_window; ///< Emu window | |||
| 37 | // qt ui) | 37 | // qt ui) |
| 38 | extern std::atomic<bool> g_hw_renderer_enabled; | 38 | extern std::atomic<bool> g_hw_renderer_enabled; |
| 39 | extern std::atomic<bool> g_shader_jit_enabled; | 39 | extern std::atomic<bool> g_shader_jit_enabled; |
| 40 | extern std::atomic<bool> g_scaled_resolution_enabled; | ||
| 41 | extern std::atomic<bool> g_toggle_framelimit_enabled; | 40 | extern std::atomic<bool> g_toggle_framelimit_enabled; |
| 42 | 41 | ||
| 43 | /// Start the video core | 42 | /// Start the video core |