summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/config.cpp3
-rw-r--r--src/citra/default_ini.h7
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp22
-rw-r--r--src/citra/emu_window/emu_window_sdl2.h5
-rw-r--r--src/citra_qt/bootmanager.cpp12
-rw-r--r--src/citra_qt/bootmanager.h4
-rw-r--r--src/citra_qt/config.cpp5
-rw-r--r--src/citra_qt/configure_graphics.cpp76
-rw-r--r--src/citra_qt/configure_graphics.ui86
-rw-r--r--src/citra_qt/debugger/callstack.cpp1
-rw-r--r--src/citra_qt/debugger/graphics/graphics_cmdlists.cpp5
-rw-r--r--src/citra_qt/debugger/graphics/graphics_vertex_shader.cpp4
-rw-r--r--src/citra_qt/debugger/wait_tree.cpp9
-rw-r--r--src/citra_qt/util/spinbox.cpp7
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/logging/backend.cpp1
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/math_util.h2
-rw-r--r--src/common/quaternion.h44
-rw-r--r--src/common/thread.h10
-rw-r--r--src/common/vector_math.h19
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/frontend/emu_window.cpp25
-rw-r--r--src/core/frontend/emu_window.h52
-rw-r--r--src/core/frontend/motion_emu.cpp89
-rw-r--r--src/core/frontend/motion_emu.h52
-rw-r--r--src/core/gdbstub/gdbstub.cpp5
-rw-r--r--src/core/hle/kernel/event.cpp6
-rw-r--r--src/core/hle/kernel/event.h4
-rw-r--r--src/core/hle/kernel/kernel.cpp49
-rw-r--r--src/core/hle/kernel/kernel.h13
-rw-r--r--src/core/hle/kernel/mutex.cpp84
-rw-r--r--src/core/hle/kernel/mutex.h17
-rw-r--r--src/core/hle/kernel/semaphore.cpp7
-rw-r--r--src/core/hle/kernel/semaphore.h4
-rw-r--r--src/core/hle/kernel/server_port.cpp6
-rw-r--r--src/core/hle/kernel/server_port.h4
-rw-r--r--src/core/hle/kernel/server_session.cpp6
-rw-r--r--src/core/hle/kernel/server_session.h4
-rw-r--r--src/core/hle/kernel/thread.cpp98
-rw-r--r--src/core/hle/kernel/thread.h62
-rw-r--r--src/core/hle/kernel/timer.cpp6
-rw-r--r--src/core/hle/kernel/timer.h4
-rw-r--r--src/core/hle/service/boss/boss.cpp4
-rw-r--r--src/core/hle/service/cfg/cfg.cpp1
-rw-r--r--src/core/hle/service/mic_u.cpp5
-rw-r--r--src/core/hle/service/nfc/nfc.cpp20
-rw-r--r--src/core/hle/service/nfc/nfc.h17
-rw-r--r--src/core/hle/service/nfc/nfc_m.cpp2
-rw-r--r--src/core/hle/service/nfc/nfc_u.cpp2
-rw-r--r--src/core/hle/service/service.cpp1
-rw-r--r--src/core/hle/service/soc_u.cpp4
-rw-r--r--src/core/hle/svc.cpp80
-rw-r--r--src/core/loader/ncch.cpp2
-rw-r--r--src/core/loader/ncch.h2
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp13
-rw-r--r--src/video_core/shader/shader_interpreter.cpp2
-rw-r--r--src/video_core/video_core.cpp1
-rw-r--r--src/video_core/video_core.h1
61 files changed, 788 insertions, 295 deletions
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)
52use_shader_jit = 52use_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
56use_scaled_resolution = 56# factor for the 3DS resolution
57resolution_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
20void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 20void 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
24void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 25void 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() {
109EmuWindow_SDL2::~EmuWindow_SDL2() { 116EmuWindow_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
114void EmuWindow_SDL2::SwapBuffers() { 122void 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
10struct SDL_Window; 12struct 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
101GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) 101GRenderWindow::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
193void GRenderWindow::closeEvent(QCloseEvent* event) { 193void 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
206void GRenderWindow::mousePressEvent(QMouseEvent* event) { 207void 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
222void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { 226void 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
227void GRenderWindow::ReloadSetKeymaps() { 233void GRenderWindow::ReloadSetKeymaps() {
@@ -279,11 +285,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(
279} 285}
280 286
281void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { 287void 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
286void GRenderWindow::OnEmulationStopping() { 293void 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
15class QKeyEvent; 16class QKeyEvent;
16class QScreen; 17class 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
159protected: 163protected:
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
19ConfigureGraphics::~ConfigureGraphics() {} 19ConfigureGraphics::~ConfigureGraphics() {}
20 20
21enum 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
35float 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
63Resolution 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
21void ConfigureGraphics::setConfiguration() { 90void 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() {
31void ConfigureGraphics::applyConfiguration() { 102void 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
475void GraphicsVertexShaderWidget::OnBreakPointHit(Pica::DebugContext::Event event, void* data) { 472void 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
11namespace MathUtil { 11namespace MathUtil {
12 12
13static constexpr float PI = 3.14159265f;
14
13inline bool IntervalsIntersect(unsigned start0, unsigned length0, unsigned start1, 15inline 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
9namespace Math {
10
11template <typename T>
12class Quaternion {
13public:
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
35template <typename T>
36auto 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
40inline 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
187typedef Vec2<float> Vec2f; 187typedef Vec2<float> Vec2f;
188 188
189template <>
190inline float Vec2<float>::Length() const {
191 return std::sqrt(x * x + y * y);
192}
193
194template <>
195inline float Vec2<float>::Normalize() {
196 float length = Length();
197 *this /= length;
198 return length;
199}
200
189template <typename T> 201template <typename T>
190class Vec3 { 202class Vec3 {
191public: 203public:
@@ -388,6 +400,13 @@ inline Vec3<float> Vec3<float>::Normalized() const {
388 return *this / Length(); 400 return *this / Length();
389} 401}
390 402
403template <>
404inline float Vec3<float>::Normalize() {
405 float length = Length();
406 *this /= length;
407 return length;
408}
409
391typedef Vec3<float> Vec3f; 410typedef Vec3<float> Vec3f;
392 411
393template <typename T> 412template <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
93void 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
106void 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
92void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { 117void 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
10namespace Motion {
11
12static constexpr int update_millisecond = 100;
13static constexpr auto update_duration =
14 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
15 std::chrono::milliseconds(update_millisecond));
16
17MotionEmu::MotionEmu(EmuWindow& emu_window)
18 : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {}
19
20MotionEmu::~MotionEmu() {
21 if (motion_emu_thread.joinable()) {
22 shutdown_event.Set();
23 motion_emu_thread.join();
24 }
25}
26
27void 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
63void MotionEmu::BeginTilt(int x, int y) {
64 mouse_origin = Math::MakeVec(x, y);
65 is_tilting = true;
66}
67
68void 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
83void 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
9class EmuWindow;
10
11namespace Motion {
12
13class MotionEmu final {
14public:
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
37private:
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;
57const u32 MSG_WAITALL = 8; 57const u32 MSG_WAITALL = 8;
58#endif 58#endif
59 59
60const u32 R0_REGISTER = 0;
61const u32 R15_REGISTER = 15; 60const u32 R15_REGISTER = 15;
62const u32 CPSR_REGISTER = 25; 61const u32 CPSR_REGISTER = 25;
63const u32 FPSCR_REGISTER = 58; 62const 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
28bool Event::ShouldWait() { 28bool Event::ShouldWait(Thread* thread) const {
29 return !signaled; 29 return !signaled;
30} 30}
31 31
32void Event::Acquire() { 32void 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
29void WaitObject::RemoveWaitingThread(Thread* thread) { 28void 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
35SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { 37SharedPtr<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() {
66void WaitObject::WakeupAllWaitingThreads() { 72void 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>;
132class WaitObject : public Object { 132class WaitObject : public Object {
133public: 133public:
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
13namespace Kernel { 14namespace 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 */
19static 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
26void ReleaseThreadMutexes(Thread* thread) { 16void 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
50bool Mutex::ShouldWait() { 42bool 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
62void Mutex::Acquire() {
63 Acquire(GetCurrentThread());
64} 44}
65 45
66void Mutex::Acquire(SharedPtr<Thread> thread) { 46void 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
78void Mutex::Release() { 61void 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
77void Mutex::AddWaitingThread(SharedPtr<Thread> thread) {
78 WaitObject::AddWaitingThread(thread);
79 thread->pending_mutexes.insert(this);
80 UpdatePriority();
81}
82
83void Mutex::RemoveWaitingThread(Thread* thread) {
84 WaitObject::RemoveWaitingThread(thread);
85 thread->pending_mutexes.erase(this);
86 UpdatePriority();
87}
88
89void 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
51private: 56private:
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
33bool Semaphore::ShouldWait() { 33bool Semaphore::ShouldWait(Thread* thread) const {
34 return available_count <= 0; 34 return available_count <= 0;
35} 35}
36 36
37void Semaphore::Acquire() { 37void 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 {
14ServerPort::ServerPort() {} 14ServerPort::ServerPort() {}
15ServerPort::~ServerPort() {} 15ServerPort::~ServerPort() {}
16 16
17bool ServerPort::ShouldWait() { 17bool 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
22void ServerPort::Acquire() { 22void ServerPort::Acquire(Thread* thread) {
23 ASSERT_MSG(!ShouldWait(), "object unavailable!"); 23 ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
24} 24}
25 25
26std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( 26std::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
59private: 59private:
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
32bool ServerSession::ShouldWait() { 32bool ServerSession::ShouldWait(Thread* thread) const {
33 return !signaled; 33 return !signaled;
34} 34}
35 35
36void ServerSession::Acquire() { 36void 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
28static int ThreadWakeupEventType; 28static int ThreadWakeupEventType;
29 29
30bool Thread::ShouldWait() { 30bool Thread::ShouldWait(Thread* thread) const {
31 return status != THREADSTATUS_DEAD; 31 return status != THREADSTATUS_DEAD;
32} 32}
33 33
34void Thread::Acquire() { 34void 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 */
74static 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
92void Thread::Stop() { 78void 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
159static 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
251void 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
259void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { 212void 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
307void Thread::ResumeFromWait() { 261void 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
475void 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
518void Thread::BoostPriority(s32 priority) { 484void 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
541void Reschedule() { 511bool HaveReadyThreads() {
542 PriorityBoostStarvedThreads(); 512 return ready_queue.get_first() != nullptr;
513}
543 514
515void 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
538s32 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
568void ThreadingInit() { 546void 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
33enum ThreadStatus { 33enum 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
43namespace Kernel { 44namespace 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:
211SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority); 219SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority);
212 220
213/** 221/**
222 * Returns whether there are any threads that are ready to run.
223 */
224bool 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 */
216void Reschedule(); 229void Reschedule();
@@ -238,15 +251,6 @@ Thread* GetCurrentThread();
238void WaitCurrentThread_Sleep(); 251void 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 */
246void 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
37bool Timer::ShouldWait() { 37bool Timer::ShouldWait(Thread* thread) const {
38 return !signaled; 38 return !signaled;
39} 39}
40 40
41void Timer::Acquire() { 41void 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 {
84static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes"); 84static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes");
85} 85}
86 86
87static const u64 CFG_SAVE_ID = 0x00010017;
88static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE; 87static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
89static const ConsoleModelInfo CONSOLE_MODEL = {NINTENDO_3DS_XL, {0, 0, 0}}; 88static const ConsoleModelInfo CONSOLE_MODEL = {NINTENDO_3DS_XL, {0, 0, 0}};
90static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; 89static 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 @@
9namespace Service { 10namespace Service {
10namespace NFC { 11namespace NFC {
11 12
13static Kernel::SharedPtr<Kernel::Event> tag_in_range_event;
14
15void 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
12void Init() { 25void 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
33void 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
7namespace Service { 7namespace Service {
8
9class Interface;
10
8namespace NFC { 11namespace 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 */
22void GetTagInRangeEvent(Interface* self);
23
10/// Initialize all NFC services. 24/// Initialize all NFC services.
11void Init(); 25void Init();
12 26
27/// Shutdown all NFC services.
28void 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
7namespace Service { 8namespace 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
7namespace Service { 8namespace 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
179void Shutdown() { 179void 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) {
693static void GetSockName(Interface* self) { 692static 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) {
734static void GetPeerName(Interface* self) { 732static 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) {
844static void SleepThread(s64 nanoseconds) { 849static 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
28namespace Shader { 28namespace Shader {
29 29
30constexpr u32 INVALID_ADDRESS = 0xFFFFFFFF;
31
32struct CallStackElement { 30struct 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
20std::atomic<bool> g_hw_renderer_enabled; 20std::atomic<bool> g_hw_renderer_enabled;
21std::atomic<bool> g_shader_jit_enabled; 21std::atomic<bool> g_shader_jit_enabled;
22std::atomic<bool> g_scaled_resolution_enabled;
23std::atomic<bool> g_vsync_enabled; 22std::atomic<bool> g_vsync_enabled;
24std::atomic<bool> g_toggle_framelimit_enabled; 23std::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)
38extern std::atomic<bool> g_hw_renderer_enabled; 38extern std::atomic<bool> g_hw_renderer_enabled;
39extern std::atomic<bool> g_shader_jit_enabled; 39extern std::atomic<bool> g_shader_jit_enabled;
40extern std::atomic<bool> g_scaled_resolution_enabled;
41extern std::atomic<bool> g_toggle_framelimit_enabled; 40extern std::atomic<bool> g_toggle_framelimit_enabled;
42 41
43/// Start the video core 42/// Start the video core