summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--src/audio_core/algorithm/interpolate.cpp54
-rw-r--r--src/audio_core/algorithm/interpolate.h7
-rw-r--r--src/core/CMakeLists.txt12
-rw-r--r--src/core/arm/arm_interface.h32
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp208
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h77
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp (renamed from src/core/arm/dynarmic/arm_dynarmic.cpp)83
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h (renamed from src/core/arm/dynarmic/arm_dynarmic.h)34
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp80
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h152
-rw-r--r--src/core/arm/exclusive_monitor.cpp2
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp8
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h7
-rw-r--r--src/core/core.cpp3
-rw-r--r--src/core/core_manager.cpp3
-rw-r--r--src/core/frontend/emu_window.h3
-rw-r--r--src/core/frontend/framebuffer_layout.h1
-rw-r--r--src/core/frontend/scope_acquire_context.cpp18
-rw-r--r--src/core/frontend/scope_acquire_context.h (renamed from src/core/frontend/scope_acquire_window_context.h)10
-rw-r--r--src/core/frontend/scope_acquire_window_context.cpp18
-rw-r--r--src/core/gdbstub/gdbstub.cpp14
-rw-r--r--src/core/hle/kernel/kernel.cpp4
-rw-r--r--src/core/hle/kernel/physical_core.cpp19
-rw-r--r--src/core/hle/kernel/physical_core.h6
-rw-r--r--src/core/hle/kernel/process.cpp3
-rw-r--r--src/core/hle/kernel/scheduler.cpp21
-rw-r--r--src/core/hle/kernel/scheduler.h3
-rw-r--r--src/core/hle/kernel/svc.cpp329
-rw-r--r--src/core/hle/kernel/svc_wrap.h158
-rw-r--r--src/core/hle/kernel/thread.cpp31
-rw-r--r--src/core/hle/kernel/thread.h22
-rw-r--r--src/core/hle/service/am/am.cpp15
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp6
-rw-r--r--src/core/reporter.cpp2
-rw-r--r--src/core/settings.cpp1
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp1
-rw-r--r--src/input_common/udp/client.cpp17
-rw-r--r--src/input_common/udp/protocol.cpp1
-rw-r--r--src/video_core/gpu_thread.cpp4
-rw-r--r--src/video_core/renderer_base.h10
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp26
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp18
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h25
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_state.h4
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h24
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp272
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h26
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp14
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp15
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp99
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp21
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp32
-rw-r--r--src/video_core/shader/decode/arithmetic.cpp33
-rw-r--r--src/video_core/shader/decode/arithmetic_integer.cpp94
-rw-r--r--src/video_core/shader/node.h2
-rw-r--r--src/video_core/shader/track.cpp20
-rw-r--r--src/yuzu/bootmanager.cpp420
-rw-r--r--src/yuzu/bootmanager.h67
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp3
-rw-r--r--src/yuzu/configuration/configure_graphics.ui10
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/main.cpp49
-rw-r--r--src/yuzu/main.h7
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h14
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp54
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h18
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp9
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h11
-rw-r--r--src/yuzu_cmd/yuzu.cpp25
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp4
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h3
83 files changed, 2167 insertions, 784 deletions
diff --git a/README.md b/README.md
index bdee2e872..e8e7946de 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
1yuzu emulator 1yuzu emulator
2============= 2=============
3[![Travis CI Build Status](https://travis-ci.org/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.org/yuzu-emu/yuzu) 3[![Travis CI Build Status](https://travis-ci.com/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.com/yuzu-emu/yuzu)
4[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/) 4[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/)
5 5
6yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/). 6yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
@@ -21,7 +21,7 @@ For development discussion, please join us on [Discord](https://discord.gg/XQV6d
21 21
22Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted. 22Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted.
23 23
24If you want to contribute please take a look at the [Contributor's Guide](CONTRIBUTING.md) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should as well contact any of the developers on Discord in order to know about the current state of the emulator. 24If you want to contribute please take a look at the [Contributor's Guide](CONTRIBUTING.md) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should also contact any of the developers on Discord in order to know about the current state of the emulator.
25 25
26### Building 26### Building
27 27
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp
index a58f24169..49ab9d3e1 100644
--- a/src/audio_core/algorithm/interpolate.cpp
+++ b/src/audio_core/algorithm/interpolate.cpp
@@ -8,13 +8,14 @@
8#include <climits> 8#include <climits>
9#include <cmath> 9#include <cmath>
10#include <vector> 10#include <vector>
11
11#include "audio_core/algorithm/interpolate.h" 12#include "audio_core/algorithm/interpolate.h"
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14 15
15namespace AudioCore { 16namespace AudioCore {
16 17
17constexpr std::array<s16, 512> curve_lut0 = { 18constexpr std::array<s16, 512> curve_lut0{
18 6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239, 19 6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239,
19 19412, 7093, 22, 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377, 20 19412, 7093, 22, 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377,
20 7472, 41, 5773, 19361, 7600, 48, 5659, 19342, 7728, 55, 5546, 19321, 7857, 21 7472, 41, 5773, 19361, 7600, 48, 5659, 19342, 7728, 55, 5546, 19321, 7857,
@@ -56,7 +57,7 @@ constexpr std::array<s16, 512> curve_lut0 = {
56 19403, 6121, 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424, 57 19403, 6121, 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424,
57 6479, 3, 6722, 19426, 6600}; 58 6479, 3, 6722, 19426, 6600};
58 59
59constexpr std::array<s16, 512> curve_lut1 = { 60constexpr std::array<s16, 512> curve_lut1{
60 -68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450, 61 -68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450,
61 32586, 512, -36, -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454, 62 32586, 512, -36, -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454,
62 1000, -69, -891, 32393, 1174, -80, -990, 32323, 1352, -92, -1084, 32244, 1536, 63 1000, -69, -891, 32393, 1174, -80, -990, 32323, 1352, -92, -1084, 32244, 1536,
@@ -98,7 +99,7 @@ constexpr std::array<s16, 512> curve_lut1 = {
98 32551, -568, -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630, 99 32551, -568, -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630,
99 -200, -5, 69, 32639, -68}; 100 -200, -5, 69, 32639, -68};
100 101
101constexpr std::array<s16, 512> curve_lut2 = { 102constexpr std::array<s16, 512> curve_lut2{
102 3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811, 103 3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811,
103 26253, 3751, -42, 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169, 104 26253, 3751, -42, 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169,
104 4199, -54, 2338, 26130, 4354, -58, 2227, 26085, 4512, -63, 2120, 26035, 4673, 105 4199, -54, 2338, 26130, 4354, -58, 2227, 26085, 4512, -63, 2120, 26035, 4673,
@@ -146,10 +147,10 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
146 147
147 if (ratio <= 0) { 148 if (ratio <= 0) {
148 LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio); 149 LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
149 ratio = 1.0; 150 return input;
150 } 151 }
151 152
152 const int step = static_cast<int>(ratio * 0x8000); 153 const s32 step{static_cast<s32>(ratio * 0x8000)};
153 const std::array<s16, 512>& lut = [step] { 154 const std::array<s16, 512>& lut = [step] {
154 if (step > 0xaaaa) { 155 if (step > 0xaaaa) {
155 return curve_lut0; 156 return curve_lut0;
@@ -160,28 +161,37 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
160 return curve_lut2; 161 return curve_lut2;
161 }(); 162 }();
162 163
163 std::vector<s16> output(static_cast<std::size_t>(input.size() / ratio)); 164 const std::size_t num_frames{input.size() / 2};
164 int in_offset = 0; 165
165 for (std::size_t out_offset = 0; out_offset < output.size(); out_offset += 2) { 166 std::vector<s16> output;
166 const int lut_index = (state.fraction >> 8) * 4; 167 output.reserve(static_cast<std::size_t>(input.size() / ratio + InterpolationState::taps));
167 168
168 const int l = input[(in_offset + 0) * 2 + 0] * lut[lut_index + 0] + 169 for (std::size_t frame{}; frame < num_frames; ++frame) {
169 input[(in_offset + 1) * 2 + 0] * lut[lut_index + 1] + 170 const std::size_t lut_index{(state.fraction >> 8) * InterpolationState::taps};
170 input[(in_offset + 2) * 2 + 0] * lut[lut_index + 2] +
171 input[(in_offset + 3) * 2 + 0] * lut[lut_index + 3];
172 171
173 const int r = input[(in_offset + 0) * 2 + 1] * lut[lut_index + 0] + 172 std::rotate(state.history.begin(), state.history.end() - 1, state.history.end());
174 input[(in_offset + 1) * 2 + 1] * lut[lut_index + 1] + 173 state.history[0][0] = input[frame * 2 + 0];
175 input[(in_offset + 2) * 2 + 1] * lut[lut_index + 2] + 174 state.history[0][1] = input[frame * 2 + 1];
176 input[(in_offset + 3) * 2 + 1] * lut[lut_index + 3];
177 175
178 const int new_offset = state.fraction + step; 176 while (state.position <= 1.0) {
177 const s32 left{state.history[0][0] * lut[lut_index + 0] +
178 state.history[1][0] * lut[lut_index + 1] +
179 state.history[2][0] * lut[lut_index + 2] +
180 state.history[3][0] * lut[lut_index + 3]};
181 const s32 right{state.history[0][1] * lut[lut_index + 0] +
182 state.history[1][1] * lut[lut_index + 1] +
183 state.history[2][1] * lut[lut_index + 2] +
184 state.history[3][1] * lut[lut_index + 3]};
185 const s32 new_offset{state.fraction + step};
179 186
180 in_offset += new_offset >> 15; 187 state.fraction = new_offset & 0x7fff;
181 state.fraction = new_offset & 0x7fff;
182 188
183 output[out_offset + 0] = static_cast<s16>(std::clamp(l >> 15, SHRT_MIN, SHRT_MAX)); 189 output.emplace_back(static_cast<s16>(std::clamp(left >> 15, SHRT_MIN, SHRT_MAX)));
184 output[out_offset + 1] = static_cast<s16>(std::clamp(r >> 15, SHRT_MIN, SHRT_MAX)); 190 output.emplace_back(static_cast<s16>(std::clamp(right >> 15, SHRT_MIN, SHRT_MAX)));
191
192 state.position += ratio;
193 }
194 state.position -= 1.0;
185 } 195 }
186 196
187 return output; 197 return output;
diff --git a/src/audio_core/algorithm/interpolate.h b/src/audio_core/algorithm/interpolate.h
index 1b9831a75..ab1a31754 100644
--- a/src/audio_core/algorithm/interpolate.h
+++ b/src/audio_core/algorithm/interpolate.h
@@ -6,12 +6,17 @@
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9
9#include "common/common_types.h" 10#include "common/common_types.h"
10 11
11namespace AudioCore { 12namespace AudioCore {
12 13
13struct InterpolationState { 14struct InterpolationState {
14 int fraction = 0; 15 static constexpr std::size_t taps{4};
16 static constexpr std::size_t history_size{taps * 2 - 1};
17 std::array<std::array<s16, 2>, history_size> history{};
18 double position{};
19 s32 fraction{};
15}; 20};
16 21
17/// Interpolates input signal to produce output signal. 22/// Interpolates input signal to produce output signal.
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 88c06b2ce..b31a0328c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -131,8 +131,8 @@ add_library(core STATIC
131 frontend/framebuffer_layout.cpp 131 frontend/framebuffer_layout.cpp
132 frontend/framebuffer_layout.h 132 frontend/framebuffer_layout.h
133 frontend/input.h 133 frontend/input.h
134 frontend/scope_acquire_window_context.cpp 134 frontend/scope_acquire_context.cpp
135 frontend/scope_acquire_window_context.h 135 frontend/scope_acquire_context.h
136 gdbstub/gdbstub.cpp 136 gdbstub/gdbstub.cpp
137 gdbstub/gdbstub.h 137 gdbstub/gdbstub.h
138 hardware_interrupt_manager.cpp 138 hardware_interrupt_manager.cpp
@@ -595,8 +595,12 @@ endif()
595 595
596if (ARCHITECTURE_x86_64) 596if (ARCHITECTURE_x86_64)
597 target_sources(core PRIVATE 597 target_sources(core PRIVATE
598 arm/dynarmic/arm_dynarmic.cpp 598 arm/dynarmic/arm_dynarmic_32.cpp
599 arm/dynarmic/arm_dynarmic.h 599 arm/dynarmic/arm_dynarmic_32.h
600 arm/dynarmic/arm_dynarmic_64.cpp
601 arm/dynarmic/arm_dynarmic_64.h
602 arm/dynarmic/arm_dynarmic_cp15.cpp
603 arm/dynarmic/arm_dynarmic_cp15.h
600 ) 604 )
601 target_link_libraries(core PRIVATE dynarmic) 605 target_link_libraries(core PRIVATE dynarmic)
602endif() 606endif()
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 47b964eb7..57eae839e 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -25,7 +25,20 @@ public:
25 explicit ARM_Interface(System& system_) : system{system_} {} 25 explicit ARM_Interface(System& system_) : system{system_} {}
26 virtual ~ARM_Interface() = default; 26 virtual ~ARM_Interface() = default;
27 27
28 struct ThreadContext { 28 struct ThreadContext32 {
29 std::array<u32, 16> cpu_registers;
30 u32 cpsr;
31 std::array<u8, 4> padding;
32 std::array<u64, 32> fprs;
33 u32 fpscr;
34 u32 fpexc;
35 u32 tpidr;
36 };
37 // Internally within the kernel, it expects the AArch32 version of the
38 // thread context to be 344 bytes in size.
39 static_assert(sizeof(ThreadContext32) == 0x158);
40
41 struct ThreadContext64 {
29 std::array<u64, 31> cpu_registers; 42 std::array<u64, 31> cpu_registers;
30 u64 sp; 43 u64 sp;
31 u64 pc; 44 u64 pc;
@@ -38,7 +51,7 @@ public:
38 }; 51 };
39 // Internally within the kernel, it expects the AArch64 version of the 52 // Internally within the kernel, it expects the AArch64 version of the
40 // thread context to be 800 bytes in size. 53 // thread context to be 800 bytes in size.
41 static_assert(sizeof(ThreadContext) == 0x320); 54 static_assert(sizeof(ThreadContext64) == 0x320);
42 55
43 /// Runs the CPU until an event happens 56 /// Runs the CPU until an event happens
44 virtual void Run() = 0; 57 virtual void Run() = 0;
@@ -130,17 +143,10 @@ public:
130 */ 143 */
131 virtual void SetTPIDR_EL0(u64 value) = 0; 144 virtual void SetTPIDR_EL0(u64 value) = 0;
132 145
133 /** 146 virtual void SaveContext(ThreadContext32& ctx) = 0;
134 * Saves the current CPU context 147 virtual void SaveContext(ThreadContext64& ctx) = 0;
135 * @param ctx Thread context to save 148 virtual void LoadContext(const ThreadContext32& ctx) = 0;
136 */ 149 virtual void LoadContext(const ThreadContext64& ctx) = 0;
137 virtual void SaveContext(ThreadContext& ctx) = 0;
138
139 /**
140 * Loads a CPU context
141 * @param ctx Thread context to load
142 */
143 virtual void LoadContext(const ThreadContext& ctx) = 0;
144 150
145 /// Clears the exclusive monitor's state. 151 /// Clears the exclusive monitor's state.
146 virtual void ClearExclusiveState() = 0; 152 virtual void ClearExclusiveState() = 0;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
new file mode 100644
index 000000000..187a972ac
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -0,0 +1,208 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cinttypes>
6#include <memory>
7#include <dynarmic/A32/a32.h>
8#include <dynarmic/A32/config.h>
9#include <dynarmic/A32/context.h>
10#include "common/microprofile.h"
11#include "core/arm/dynarmic/arm_dynarmic_32.h"
12#include "core/arm/dynarmic/arm_dynarmic_64.h"
13#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
14#include "core/core.h"
15#include "core/core_manager.h"
16#include "core/core_timing.h"
17#include "core/hle/kernel/svc.h"
18#include "core/memory.h"
19
20namespace Core {
21
22class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
23public:
24 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent) : parent(parent) {}
25
26 u8 MemoryRead8(u32 vaddr) override {
27 return parent.system.Memory().Read8(vaddr);
28 }
29 u16 MemoryRead16(u32 vaddr) override {
30 return parent.system.Memory().Read16(vaddr);
31 }
32 u32 MemoryRead32(u32 vaddr) override {
33 return parent.system.Memory().Read32(vaddr);
34 }
35 u64 MemoryRead64(u32 vaddr) override {
36 return parent.system.Memory().Read64(vaddr);
37 }
38
39 void MemoryWrite8(u32 vaddr, u8 value) override {
40 parent.system.Memory().Write8(vaddr, value);
41 }
42 void MemoryWrite16(u32 vaddr, u16 value) override {
43 parent.system.Memory().Write16(vaddr, value);
44 }
45 void MemoryWrite32(u32 vaddr, u32 value) override {
46 parent.system.Memory().Write32(vaddr, value);
47 }
48 void MemoryWrite64(u32 vaddr, u64 value) override {
49 parent.system.Memory().Write64(vaddr, value);
50 }
51
52 void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
53 UNIMPLEMENTED();
54 }
55
56 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
57 switch (exception) {
58 case Dynarmic::A32::Exception::UndefinedInstruction:
59 case Dynarmic::A32::Exception::UnpredictableInstruction:
60 break;
61 case Dynarmic::A32::Exception::Breakpoint:
62 break;
63 }
64 LOG_CRITICAL(HW_GPU, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
65 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
66 UNIMPLEMENTED();
67 }
68
69 void CallSVC(u32 swi) override {
70 Kernel::CallSVC(parent.system, swi);
71 }
72
73 void AddTicks(u64 ticks) override {
74 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
75 // rough approximation of the amount of executed ticks in the system, it may be thrown off
76 // if not all cores are doing a similar amount of work. Instead of doing this, we should
77 // device a way so that timing is consistent across all cores without increasing the ticks 4
78 // times.
79 u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES;
80 // Always execute at least one tick.
81 amortized_ticks = std::max<u64>(amortized_ticks, 1);
82
83 parent.system.CoreTiming().AddTicks(amortized_ticks);
84 num_interpreted_instructions = 0;
85 }
86 u64 GetTicksRemaining() override {
87 return std::max(parent.system.CoreTiming().GetDowncount(), {});
88 }
89
90 ARM_Dynarmic_32& parent;
91 std::size_t num_interpreted_instructions{};
92 u64 tpidrro_el0{};
93 u64 tpidr_el0{};
94};
95
96std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table,
97 std::size_t address_space_bits) const {
98 Dynarmic::A32::UserConfig config;
99 config.callbacks = cb.get();
100 // TODO(bunnei): Implement page table for 32-bit
101 // config.page_table = &page_table.pointers;
102 config.coprocessors[15] = std::make_shared<DynarmicCP15>((u32*)&CP15_regs[0]);
103 config.define_unpredictable_behaviour = true;
104 return std::make_unique<Dynarmic::A32::Jit>(config);
105}
106
107MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_32, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
108
109void ARM_Dynarmic_32::Run() {
110 MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_32);
111 jit->Run();
112}
113
114void ARM_Dynarmic_32::Step() {
115 cb->InterpreterFallback(jit->Regs()[15], 1);
116}
117
118ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor,
119 std::size_t core_index)
120 : ARM_Interface{system},
121 cb(std::make_unique<DynarmicCallbacks32>(*this)), core_index{core_index},
122 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
123
124ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
125
126void ARM_Dynarmic_32::SetPC(u64 pc) {
127 jit->Regs()[15] = static_cast<u32>(pc);
128}
129
130u64 ARM_Dynarmic_32::GetPC() const {
131 return jit->Regs()[15];
132}
133
134u64 ARM_Dynarmic_32::GetReg(int index) const {
135 return jit->Regs()[index];
136}
137
138void ARM_Dynarmic_32::SetReg(int index, u64 value) {
139 jit->Regs()[index] = static_cast<u32>(value);
140}
141
142u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
143 return {};
144}
145
146void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
147
148u32 ARM_Dynarmic_32::GetPSTATE() const {
149 return jit->Cpsr();
150}
151
152void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
153 jit->SetCpsr(cpsr);
154}
155
156u64 ARM_Dynarmic_32::GetTlsAddress() const {
157 return CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)];
158}
159
160void ARM_Dynarmic_32::SetTlsAddress(VAddr address) {
161 CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)] = static_cast<u32>(address);
162}
163
164u64 ARM_Dynarmic_32::GetTPIDR_EL0() const {
165 return cb->tpidr_el0;
166}
167
168void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
169 cb->tpidr_el0 = value;
170}
171
172void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
173 Dynarmic::A32::Context context;
174 jit->SaveContext(context);
175 ctx.cpu_registers = context.Regs();
176 ctx.cpsr = context.Cpsr();
177}
178
179void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
180 Dynarmic::A32::Context context;
181 context.Regs() = ctx.cpu_registers;
182 context.SetCpsr(ctx.cpsr);
183 jit->LoadContext(context);
184}
185
186void ARM_Dynarmic_32::PrepareReschedule() {
187 jit->HaltExecution();
188}
189
190void ARM_Dynarmic_32::ClearInstructionCache() {
191 jit->ClearCache();
192}
193
194void ARM_Dynarmic_32::ClearExclusiveState() {}
195
196void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
197 std::size_t new_address_space_size_in_bits) {
198 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
199 auto iter = jit_cache.find(key);
200 if (iter != jit_cache.end()) {
201 jit = iter->second;
202 return;
203 }
204 jit = MakeJit(page_table, new_address_space_size_in_bits);
205 jit_cache.emplace(key, jit);
206}
207
208} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
new file mode 100644
index 000000000..143e46e4d
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -0,0 +1,77 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <unordered_map>
9
10#include <dynarmic/A32/a32.h>
11#include <dynarmic/A64/a64.h>
12#include <dynarmic/A64/exclusive_monitor.h>
13#include "common/common_types.h"
14#include "common/hash.h"
15#include "core/arm/arm_interface.h"
16#include "core/arm/exclusive_monitor.h"
17
18namespace Memory {
19class Memory;
20}
21
22namespace Core {
23
24class DynarmicCallbacks32;
25class DynarmicExclusiveMonitor;
26class System;
27
28class ARM_Dynarmic_32 final : public ARM_Interface {
29public:
30 ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
31 ~ARM_Dynarmic_32() override;
32
33 void SetPC(u64 pc) override;
34 u64 GetPC() const override;
35 u64 GetReg(int index) const override;
36 void SetReg(int index, u64 value) override;
37 u128 GetVectorReg(int index) const override;
38 void SetVectorReg(int index, u128 value) override;
39 u32 GetPSTATE() const override;
40 void SetPSTATE(u32 pstate) override;
41 void Run() override;
42 void Step() override;
43 VAddr GetTlsAddress() const override;
44 void SetTlsAddress(VAddr address) override;
45 void SetTPIDR_EL0(u64 value) override;
46 u64 GetTPIDR_EL0() const override;
47
48 void SaveContext(ThreadContext32& ctx) override;
49 void SaveContext(ThreadContext64& ctx) override {}
50 void LoadContext(const ThreadContext32& ctx) override;
51 void LoadContext(const ThreadContext64& ctx) override {}
52
53 void PrepareReschedule() override;
54 void ClearExclusiveState() override;
55
56 void ClearInstructionCache() override;
57 void PageTableChanged(Common::PageTable& new_page_table,
58 std::size_t new_address_space_size_in_bits) override;
59
60private:
61 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable& page_table,
62 std::size_t address_space_bits) const;
63
64 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
65 using JitCacheType =
66 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
67
68 friend class DynarmicCallbacks32;
69 std::unique_ptr<DynarmicCallbacks32> cb;
70 JitCacheType jit_cache;
71 std::shared_ptr<Dynarmic::A32::Jit> jit;
72 std::size_t core_index;
73 DynarmicExclusiveMonitor& exclusive_monitor;
74 std::array<u32, 84> CP15_regs{};
75};
76
77} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 29eaf74e5..a53a58ba0 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -8,7 +8,7 @@
8#include <dynarmic/A64/config.h> 8#include <dynarmic/A64/config.h>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/microprofile.h" 10#include "common/microprofile.h"
11#include "core/arm/dynarmic/arm_dynarmic.h" 11#include "core/arm/dynarmic/arm_dynarmic_64.h"
12#include "core/core.h" 12#include "core/core.h"
13#include "core/core_manager.h" 13#include "core/core_manager.h"
14#include "core/core_timing.h" 14#include "core/core_timing.h"
@@ -25,9 +25,9 @@ namespace Core {
25 25
26using Vector = Dynarmic::A64::Vector; 26using Vector = Dynarmic::A64::Vector;
27 27
28class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks { 28class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
29public: 29public:
30 explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {} 30 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent) : parent(parent) {}
31 31
32 u8 MemoryRead8(u64 vaddr) override { 32 u8 MemoryRead8(u64 vaddr) override {
33 return parent.system.Memory().Read8(vaddr); 33 return parent.system.Memory().Read8(vaddr);
@@ -68,7 +68,7 @@ public:
68 LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, 68 LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
69 num_instructions, MemoryReadCode(pc)); 69 num_instructions, MemoryReadCode(pc));
70 70
71 ARM_Interface::ThreadContext ctx; 71 ARM_Interface::ThreadContext64 ctx;
72 parent.SaveContext(ctx); 72 parent.SaveContext(ctx);
73 parent.inner_unicorn.LoadContext(ctx); 73 parent.inner_unicorn.LoadContext(ctx);
74 parent.inner_unicorn.ExecuteInstructions(num_instructions); 74 parent.inner_unicorn.ExecuteInstructions(num_instructions);
@@ -90,7 +90,7 @@ public:
90 parent.jit->HaltExecution(); 90 parent.jit->HaltExecution();
91 parent.SetPC(pc); 91 parent.SetPC(pc);
92 Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread(); 92 Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread();
93 parent.SaveContext(thread->GetContext()); 93 parent.SaveContext(thread->GetContext64());
94 GDBStub::Break(); 94 GDBStub::Break();
95 GDBStub::SendTrap(thread, 5); 95 GDBStub::SendTrap(thread, 5);
96 return; 96 return;
@@ -126,14 +126,14 @@ public:
126 return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); 126 return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks());
127 } 127 }
128 128
129 ARM_Dynarmic& parent; 129 ARM_Dynarmic_64& parent;
130 std::size_t num_interpreted_instructions = 0; 130 std::size_t num_interpreted_instructions = 0;
131 u64 tpidrro_el0 = 0; 131 u64 tpidrro_el0 = 0;
132 u64 tpidr_el0 = 0; 132 u64 tpidr_el0 = 0;
133}; 133};
134 134
135std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table, 135std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& page_table,
136 std::size_t address_space_bits) const { 136 std::size_t address_space_bits) const {
137 Dynarmic::A64::UserConfig config; 137 Dynarmic::A64::UserConfig config;
138 138
139 // Callbacks 139 // Callbacks
@@ -159,79 +159,79 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& pag
159 // Unpredictable instructions 159 // Unpredictable instructions
160 config.define_unpredictable_behaviour = true; 160 config.define_unpredictable_behaviour = true;
161 161
162 return std::make_unique<Dynarmic::A64::Jit>(config); 162 return std::make_shared<Dynarmic::A64::Jit>(config);
163} 163}
164 164
165MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); 165MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_64, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
166 166
167void ARM_Dynarmic::Run() { 167void ARM_Dynarmic_64::Run() {
168 MICROPROFILE_SCOPE(ARM_Jit_Dynarmic); 168 MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_64);
169 169
170 jit->Run(); 170 jit->Run();
171} 171}
172 172
173void ARM_Dynarmic::Step() { 173void ARM_Dynarmic_64::Step() {
174 cb->InterpreterFallback(jit->GetPC(), 1); 174 cb->InterpreterFallback(jit->GetPC(), 1);
175} 175}
176 176
177ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, 177ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor,
178 std::size_t core_index) 178 std::size_t core_index)
179 : ARM_Interface{system}, 179 : ARM_Interface{system},
180 cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system}, 180 cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system},
181 core_index{core_index}, exclusive_monitor{ 181 core_index{core_index}, exclusive_monitor{
182 dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} 182 dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
183 183
184ARM_Dynarmic::~ARM_Dynarmic() = default; 184ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
185 185
186void ARM_Dynarmic::SetPC(u64 pc) { 186void ARM_Dynarmic_64::SetPC(u64 pc) {
187 jit->SetPC(pc); 187 jit->SetPC(pc);
188} 188}
189 189
190u64 ARM_Dynarmic::GetPC() const { 190u64 ARM_Dynarmic_64::GetPC() const {
191 return jit->GetPC(); 191 return jit->GetPC();
192} 192}
193 193
194u64 ARM_Dynarmic::GetReg(int index) const { 194u64 ARM_Dynarmic_64::GetReg(int index) const {
195 return jit->GetRegister(index); 195 return jit->GetRegister(index);
196} 196}
197 197
198void ARM_Dynarmic::SetReg(int index, u64 value) { 198void ARM_Dynarmic_64::SetReg(int index, u64 value) {
199 jit->SetRegister(index, value); 199 jit->SetRegister(index, value);
200} 200}
201 201
202u128 ARM_Dynarmic::GetVectorReg(int index) const { 202u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
203 return jit->GetVector(index); 203 return jit->GetVector(index);
204} 204}
205 205
206void ARM_Dynarmic::SetVectorReg(int index, u128 value) { 206void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
207 jit->SetVector(index, value); 207 jit->SetVector(index, value);
208} 208}
209 209
210u32 ARM_Dynarmic::GetPSTATE() const { 210u32 ARM_Dynarmic_64::GetPSTATE() const {
211 return jit->GetPstate(); 211 return jit->GetPstate();
212} 212}
213 213
214void ARM_Dynarmic::SetPSTATE(u32 pstate) { 214void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
215 jit->SetPstate(pstate); 215 jit->SetPstate(pstate);
216} 216}
217 217
218u64 ARM_Dynarmic::GetTlsAddress() const { 218u64 ARM_Dynarmic_64::GetTlsAddress() const {
219 return cb->tpidrro_el0; 219 return cb->tpidrro_el0;
220} 220}
221 221
222void ARM_Dynarmic::SetTlsAddress(VAddr address) { 222void ARM_Dynarmic_64::SetTlsAddress(VAddr address) {
223 cb->tpidrro_el0 = address; 223 cb->tpidrro_el0 = address;
224} 224}
225 225
226u64 ARM_Dynarmic::GetTPIDR_EL0() const { 226u64 ARM_Dynarmic_64::GetTPIDR_EL0() const {
227 return cb->tpidr_el0; 227 return cb->tpidr_el0;
228} 228}
229 229
230void ARM_Dynarmic::SetTPIDR_EL0(u64 value) { 230void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
231 cb->tpidr_el0 = value; 231 cb->tpidr_el0 = value;
232} 232}
233 233
234void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { 234void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
235 ctx.cpu_registers = jit->GetRegisters(); 235 ctx.cpu_registers = jit->GetRegisters();
236 ctx.sp = jit->GetSP(); 236 ctx.sp = jit->GetSP();
237 ctx.pc = jit->GetPC(); 237 ctx.pc = jit->GetPC();
@@ -242,7 +242,7 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
242 ctx.tpidr = cb->tpidr_el0; 242 ctx.tpidr = cb->tpidr_el0;
243} 243}
244 244
245void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { 245void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
246 jit->SetRegisters(ctx.cpu_registers); 246 jit->SetRegisters(ctx.cpu_registers);
247 jit->SetSP(ctx.sp); 247 jit->SetSP(ctx.sp);
248 jit->SetPC(ctx.pc); 248 jit->SetPC(ctx.pc);
@@ -253,25 +253,32 @@ void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
253 SetTPIDR_EL0(ctx.tpidr); 253 SetTPIDR_EL0(ctx.tpidr);
254} 254}
255 255
256void ARM_Dynarmic::PrepareReschedule() { 256void ARM_Dynarmic_64::PrepareReschedule() {
257 jit->HaltExecution(); 257 jit->HaltExecution();
258} 258}
259 259
260void ARM_Dynarmic::ClearInstructionCache() { 260void ARM_Dynarmic_64::ClearInstructionCache() {
261 jit->ClearCache(); 261 jit->ClearCache();
262} 262}
263 263
264void ARM_Dynarmic::ClearExclusiveState() { 264void ARM_Dynarmic_64::ClearExclusiveState() {
265 jit->ClearExclusiveState(); 265 jit->ClearExclusiveState();
266} 266}
267 267
268void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table, 268void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
269 std::size_t new_address_space_size_in_bits) { 269 std::size_t new_address_space_size_in_bits) {
270 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
271 auto iter = jit_cache.find(key);
272 if (iter != jit_cache.end()) {
273 jit = iter->second;
274 return;
275 }
270 jit = MakeJit(page_table, new_address_space_size_in_bits); 276 jit = MakeJit(page_table, new_address_space_size_in_bits);
277 jit_cache.emplace(key, jit);
271} 278}
272 279
273DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count) 280DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count)
274 : monitor(core_count), memory{memory_} {} 281 : monitor(core_count), memory{memory} {}
275 282
276DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; 283DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
277 284
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 9cd475cfb..e71240a96 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -5,9 +5,12 @@
5#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <unordered_map>
9
8#include <dynarmic/A64/a64.h> 10#include <dynarmic/A64/a64.h>
9#include <dynarmic/A64/exclusive_monitor.h> 11#include <dynarmic/A64/exclusive_monitor.h>
10#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/hash.h"
11#include "core/arm/arm_interface.h" 14#include "core/arm/arm_interface.h"
12#include "core/arm/exclusive_monitor.h" 15#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h" 16#include "core/arm/unicorn/arm_unicorn.h"
@@ -18,14 +21,14 @@ class Memory;
18 21
19namespace Core { 22namespace Core {
20 23
21class ARM_Dynarmic_Callbacks; 24class DynarmicCallbacks64;
22class DynarmicExclusiveMonitor; 25class DynarmicExclusiveMonitor;
23class System; 26class System;
24 27
25class ARM_Dynarmic final : public ARM_Interface { 28class ARM_Dynarmic_64 final : public ARM_Interface {
26public: 29public:
27 ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); 30 ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
28 ~ARM_Dynarmic() override; 31 ~ARM_Dynarmic_64() override;
29 32
30 void SetPC(u64 pc) override; 33 void SetPC(u64 pc) override;
31 u64 GetPC() const override; 34 u64 GetPC() const override;
@@ -42,8 +45,10 @@ public:
42 void SetTPIDR_EL0(u64 value) override; 45 void SetTPIDR_EL0(u64 value) override;
43 u64 GetTPIDR_EL0() const override; 46 u64 GetTPIDR_EL0() const override;
44 47
45 void SaveContext(ThreadContext& ctx) override; 48 void SaveContext(ThreadContext32& ctx) override {}
46 void LoadContext(const ThreadContext& ctx) override; 49 void SaveContext(ThreadContext64& ctx) override;
50 void LoadContext(const ThreadContext32& ctx) override {}
51 void LoadContext(const ThreadContext64& ctx) override;
47 52
48 void PrepareReschedule() override; 53 void PrepareReschedule() override;
49 void ClearExclusiveState() override; 54 void ClearExclusiveState() override;
@@ -53,12 +58,17 @@ public:
53 std::size_t new_address_space_size_in_bits) override; 58 std::size_t new_address_space_size_in_bits) override;
54 59
55private: 60private:
56 std::unique_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table, 61 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable& page_table,
57 std::size_t address_space_bits) const; 62 std::size_t address_space_bits) const;
58 63
59 friend class ARM_Dynarmic_Callbacks; 64 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
60 std::unique_ptr<ARM_Dynarmic_Callbacks> cb; 65 using JitCacheType =
61 std::unique_ptr<Dynarmic::A64::Jit> jit; 66 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
67
68 friend class DynarmicCallbacks64;
69 std::unique_ptr<DynarmicCallbacks64> cb;
70 JitCacheType jit_cache;
71 std::shared_ptr<Dynarmic::A64::Jit> jit;
62 ARM_Unicorn inner_unicorn; 72 ARM_Unicorn inner_unicorn;
63 73
64 std::size_t core_index; 74 std::size_t core_index;
@@ -67,7 +77,7 @@ private:
67 77
68class DynarmicExclusiveMonitor final : public ExclusiveMonitor { 78class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
69public: 79public:
70 explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count); 80 explicit DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count);
71 ~DynarmicExclusiveMonitor() override; 81 ~DynarmicExclusiveMonitor() override;
72 82
73 void SetExclusive(std::size_t core_index, VAddr addr) override; 83 void SetExclusive(std::size_t core_index, VAddr addr) override;
@@ -80,7 +90,7 @@ public:
80 bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; 90 bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override;
81 91
82private: 92private:
83 friend class ARM_Dynarmic; 93 friend class ARM_Dynarmic_64;
84 Dynarmic::A64::ExclusiveMonitor monitor; 94 Dynarmic::A64::ExclusiveMonitor monitor;
85 Memory::Memory& memory; 95 Memory::Memory& memory;
86}; 96};
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
new file mode 100644
index 000000000..3fdcdebde
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -0,0 +1,80 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
6
7using Callback = Dynarmic::A32::Coprocessor::Callback;
8using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
9using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
10
11std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigned opc1,
12 CoprocReg CRd, CoprocReg CRn,
13 CoprocReg CRm, unsigned opc2) {
14 return {};
15}
16
17CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
18 CoprocReg CRm, unsigned opc2) {
19 // TODO(merry): Privileged CP15 registers
20
21 if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C5 && opc2 == 4) {
22 // This is a dummy write, we ignore the value written here.
23 return &CP15[static_cast<std::size_t>(CP15Register::CP15_FLUSH_PREFETCH_BUFFER)];
24 }
25
26 if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C10) {
27 switch (opc2) {
28 case 4:
29 // This is a dummy write, we ignore the value written here.
30 return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_SYNC_BARRIER)];
31 case 5:
32 // This is a dummy write, we ignore the value written here.
33 return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_MEMORY_BARRIER)];
34 default:
35 return {};
36 }
37 }
38
39 if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 2) {
40 return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)];
41 }
42
43 return {};
44}
45
46CallbackOrAccessTwoWords DynarmicCP15::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) {
47 return {};
48}
49
50CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn,
51 CoprocReg CRm, unsigned opc2) {
52 // TODO(merry): Privileged CP15 registers
53
54 if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0) {
55 switch (opc2) {
56 case 2:
57 return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)];
58 case 3:
59 return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)];
60 default:
61 return {};
62 }
63 }
64
65 return {};
66}
67
68CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
69 return {};
70}
71
72std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd,
73 std::optional<u8> option) {
74 return {};
75}
76
77std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
78 std::optional<u8> option) {
79 return {};
80}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
new file mode 100644
index 000000000..07bcde5f9
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -0,0 +1,152 @@
1// Copyright 2017 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 <memory>
8#include <optional>
9
10#include <dynarmic/A32/coprocessor.h>
11#include "common/common_types.h"
12
13enum class CP15Register {
14 // c0 - Information registers
15 CP15_MAIN_ID,
16 CP15_CACHE_TYPE,
17 CP15_TCM_STATUS,
18 CP15_TLB_TYPE,
19 CP15_CPU_ID,
20 CP15_PROCESSOR_FEATURE_0,
21 CP15_PROCESSOR_FEATURE_1,
22 CP15_DEBUG_FEATURE_0,
23 CP15_AUXILIARY_FEATURE_0,
24 CP15_MEMORY_MODEL_FEATURE_0,
25 CP15_MEMORY_MODEL_FEATURE_1,
26 CP15_MEMORY_MODEL_FEATURE_2,
27 CP15_MEMORY_MODEL_FEATURE_3,
28 CP15_ISA_FEATURE_0,
29 CP15_ISA_FEATURE_1,
30 CP15_ISA_FEATURE_2,
31 CP15_ISA_FEATURE_3,
32 CP15_ISA_FEATURE_4,
33
34 // c1 - Control registers
35 CP15_CONTROL,
36 CP15_AUXILIARY_CONTROL,
37 CP15_COPROCESSOR_ACCESS_CONTROL,
38
39 // c2 - Translation table registers
40 CP15_TRANSLATION_BASE_TABLE_0,
41 CP15_TRANSLATION_BASE_TABLE_1,
42 CP15_TRANSLATION_BASE_CONTROL,
43 CP15_DOMAIN_ACCESS_CONTROL,
44 CP15_RESERVED,
45
46 // c5 - Fault status registers
47 CP15_FAULT_STATUS,
48 CP15_INSTR_FAULT_STATUS,
49 CP15_COMBINED_DATA_FSR = CP15_FAULT_STATUS,
50 CP15_INST_FSR,
51
52 // c6 - Fault Address registers
53 CP15_FAULT_ADDRESS,
54 CP15_COMBINED_DATA_FAR = CP15_FAULT_ADDRESS,
55 CP15_WFAR,
56 CP15_IFAR,
57
58 // c7 - Cache operation registers
59 CP15_WAIT_FOR_INTERRUPT,
60 CP15_PHYS_ADDRESS,
61 CP15_INVALIDATE_INSTR_CACHE,
62 CP15_INVALIDATE_INSTR_CACHE_USING_MVA,
63 CP15_INVALIDATE_INSTR_CACHE_USING_INDEX,
64 CP15_FLUSH_PREFETCH_BUFFER,
65 CP15_FLUSH_BRANCH_TARGET_CACHE,
66 CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY,
67 CP15_INVALIDATE_DATA_CACHE,
68 CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
69 CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
70 CP15_INVALIDATE_DATA_AND_INSTR_CACHE,
71 CP15_CLEAN_DATA_CACHE,
72 CP15_CLEAN_DATA_CACHE_LINE_USING_MVA,
73 CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX,
74 CP15_DATA_SYNC_BARRIER,
75 CP15_DATA_MEMORY_BARRIER,
76 CP15_CLEAN_AND_INVALIDATE_DATA_CACHE,
77 CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
78 CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
79
80 // c8 - TLB operations
81 CP15_INVALIDATE_ITLB,
82 CP15_INVALIDATE_ITLB_SINGLE_ENTRY,
83 CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH,
84 CP15_INVALIDATE_ITLB_ENTRY_ON_MVA,
85 CP15_INVALIDATE_DTLB,
86 CP15_INVALIDATE_DTLB_SINGLE_ENTRY,
87 CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH,
88 CP15_INVALIDATE_DTLB_ENTRY_ON_MVA,
89 CP15_INVALIDATE_UTLB,
90 CP15_INVALIDATE_UTLB_SINGLE_ENTRY,
91 CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH,
92 CP15_INVALIDATE_UTLB_ENTRY_ON_MVA,
93
94 // c9 - Data cache lockdown register
95 CP15_DATA_CACHE_LOCKDOWN,
96
97 // c10 - TLB/Memory map registers
98 CP15_TLB_LOCKDOWN,
99 CP15_PRIMARY_REGION_REMAP,
100 CP15_NORMAL_REGION_REMAP,
101
102 // c13 - Thread related registers
103 CP15_PID,
104 CP15_CONTEXT_ID,
105 CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write
106 CP15_THREAD_URO, // Thread ID register - User Read Only (Privileged R/W)
107 CP15_THREAD_PRW, // Thread ID register - Privileged R/W only.
108
109 // c15 - Performance and TLB lockdown registers
110 CP15_PERFORMANCE_MONITOR_CONTROL,
111 CP15_CYCLE_COUNTER,
112 CP15_COUNT_0,
113 CP15_COUNT_1,
114 CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY,
115 CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY,
116 CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS,
117 CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS,
118 CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE,
119 CP15_TLB_DEBUG_CONTROL,
120
121 // Skyeye defined
122 CP15_TLB_FAULT_ADDR,
123 CP15_TLB_FAULT_STATUS,
124
125 // Not an actual register.
126 // All registers should be defined above this.
127 CP15_REGISTER_COUNT,
128};
129
130class DynarmicCP15 final : public Dynarmic::A32::Coprocessor {
131public:
132 using CoprocReg = Dynarmic::A32::CoprocReg;
133
134 explicit DynarmicCP15(u32* cp15) : CP15(cp15){};
135
136 std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd,
137 CoprocReg CRn, CoprocReg CRm,
138 unsigned opc2) override;
139 CallbackOrAccessOneWord CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
140 CoprocReg CRm, unsigned opc2) override;
141 CallbackOrAccessTwoWords CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
142 CallbackOrAccessOneWord CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm,
143 unsigned opc2) override;
144 CallbackOrAccessTwoWords CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
145 std::optional<Callback> CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd,
146 std::optional<u8> option) override;
147 std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
148 std::optional<u8> option) override;
149
150private:
151 u32* CP15{};
152};
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
index 94570e520..b32401e0b 100644
--- a/src/core/arm/exclusive_monitor.cpp
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -3,7 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#ifdef ARCHITECTURE_x86_64 5#ifdef ARCHITECTURE_x86_64
6#include "core/arm/dynarmic/arm_dynarmic.h" 6#include "core/arm/dynarmic/arm_dynarmic_64.h"
7#endif 7#endif
8#include "core/arm/exclusive_monitor.h" 8#include "core/arm/exclusive_monitor.h"
9#include "core/memory.h" 9#include "core/memory.h"
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index f99ad5802..8a9800a96 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -53,7 +53,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
53 void* user_data) { 53 void* user_data) {
54 auto* const system = static_cast<System*>(user_data); 54 auto* const system = static_cast<System*>(user_data);
55 55
56 ARM_Interface::ThreadContext ctx{}; 56 ARM_Interface::ThreadContext64 ctx{};
57 system->CurrentArmInterface().SaveContext(ctx); 57 system->CurrentArmInterface().SaveContext(ctx);
58 ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr, 58 ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
59 ctx.pc, ctx.cpu_registers[30]); 59 ctx.pc, ctx.cpu_registers[30]);
@@ -179,7 +179,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
179 } 179 }
180 180
181 Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread(); 181 Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
182 SaveContext(thread->GetContext()); 182 SaveContext(thread->GetContext64());
183 if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { 183 if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
184 last_bkpt_hit = false; 184 last_bkpt_hit = false;
185 GDBStub::Break(); 185 GDBStub::Break();
@@ -188,7 +188,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
188 } 188 }
189} 189}
190 190
191void ARM_Unicorn::SaveContext(ThreadContext& ctx) { 191void ARM_Unicorn::SaveContext(ThreadContext64& ctx) {
192 int uregs[32]; 192 int uregs[32];
193 void* tregs[32]; 193 void* tregs[32];
194 194
@@ -215,7 +215,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) {
215 CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32)); 215 CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
216} 216}
217 217
218void ARM_Unicorn::LoadContext(const ThreadContext& ctx) { 218void ARM_Unicorn::LoadContext(const ThreadContext64& ctx) {
219 int uregs[32]; 219 int uregs[32];
220 void* tregs[32]; 220 void* tregs[32];
221 221
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index 3c5b155f9..f30d13cb6 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -30,8 +30,6 @@ public:
30 void SetTlsAddress(VAddr address) override; 30 void SetTlsAddress(VAddr address) override;
31 void SetTPIDR_EL0(u64 value) override; 31 void SetTPIDR_EL0(u64 value) override;
32 u64 GetTPIDR_EL0() const override; 32 u64 GetTPIDR_EL0() const override;
33 void SaveContext(ThreadContext& ctx) override;
34 void LoadContext(const ThreadContext& ctx) override;
35 void PrepareReschedule() override; 33 void PrepareReschedule() override;
36 void ClearExclusiveState() override; 34 void ClearExclusiveState() override;
37 void ExecuteInstructions(std::size_t num_instructions); 35 void ExecuteInstructions(std::size_t num_instructions);
@@ -41,6 +39,11 @@ public:
41 void PageTableChanged(Common::PageTable&, std::size_t) override {} 39 void PageTableChanged(Common::PageTable&, std::size_t) override {}
42 void RecordBreak(GDBStub::BreakpointAddress bkpt); 40 void RecordBreak(GDBStub::BreakpointAddress bkpt);
43 41
42 void SaveContext(ThreadContext32& ctx) override {}
43 void SaveContext(ThreadContext64& ctx) override;
44 void LoadContext(const ThreadContext32& ctx) override {}
45 void LoadContext(const ThreadContext64& ctx) override;
46
44private: 47private:
45 static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data); 48 static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
46 49
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 86e314c94..a82faf127 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -24,6 +24,7 @@
24#include "core/file_sys/sdmc_factory.h" 24#include "core/file_sys/sdmc_factory.h"
25#include "core/file_sys/vfs_concat.h" 25#include "core/file_sys/vfs_concat.h"
26#include "core/file_sys/vfs_real.h" 26#include "core/file_sys/vfs_real.h"
27#include "core/frontend/scope_acquire_context.h"
27#include "core/gdbstub/gdbstub.h" 28#include "core/gdbstub/gdbstub.h"
28#include "core/hardware_interrupt_manager.h" 29#include "core/hardware_interrupt_manager.h"
29#include "core/hle/kernel/client_port.h" 30#include "core/hle/kernel/client_port.h"
@@ -184,6 +185,8 @@ struct System::Impl {
184 185
185 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, 186 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
186 const std::string& filepath) { 187 const std::string& filepath) {
188 Core::Frontend::ScopeAcquireContext acquire_context{emu_window};
189
187 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 190 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
188 if (!app_loader) { 191 if (!app_loader) {
189 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 192 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp
index 8eacf92dd..b6b797c80 100644
--- a/src/core/core_manager.cpp
+++ b/src/core/core_manager.cpp
@@ -6,9 +6,6 @@
6#include <mutex> 6#include <mutex>
7 7
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#ifdef ARCHITECTURE_x86_64
10#include "core/arm/dynarmic/arm_dynarmic.h"
11#endif
12#include "core/arm/exclusive_monitor.h" 9#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h" 10#include "core/arm/unicorn/arm_unicorn.h"
14#include "core/core.h" 11#include "core/core.h"
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 3376eedc5..5eb87fb63 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -26,9 +26,6 @@ public:
26 26
27 /// Releases (dunno if this is the "right" word) the context from the caller thread 27 /// Releases (dunno if this is the "right" word) the context from the caller thread
28 virtual void DoneCurrent() = 0; 28 virtual void DoneCurrent() = 0;
29
30 /// Swap buffers to display the next frame
31 virtual void SwapBuffers() = 0;
32}; 29};
33 30
34/** 31/**
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 1d39c1faf..e9d0a40d3 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -29,6 +29,7 @@ enum class AspectRatio {
29struct FramebufferLayout { 29struct FramebufferLayout {
30 u32 width{ScreenUndocked::Width}; 30 u32 width{ScreenUndocked::Width};
31 u32 height{ScreenUndocked::Height}; 31 u32 height{ScreenUndocked::Height};
32 bool is_srgb{};
32 33
33 Common::Rectangle<u32> screen; 34 Common::Rectangle<u32> screen;
34 35
diff --git a/src/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp
new file mode 100644
index 000000000..878c3157c
--- /dev/null
+++ b/src/core/frontend/scope_acquire_context.cpp
@@ -0,0 +1,18 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/emu_window.h"
6#include "core/frontend/scope_acquire_context.h"
7
8namespace Core::Frontend {
9
10ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context)
11 : context{context} {
12 context.MakeCurrent();
13}
14ScopeAcquireContext::~ScopeAcquireContext() {
15 context.DoneCurrent();
16}
17
18} // namespace Core::Frontend
diff --git a/src/core/frontend/scope_acquire_window_context.h b/src/core/frontend/scope_acquire_context.h
index 2d9f6e825..7a65c0623 100644
--- a/src/core/frontend/scope_acquire_window_context.h
+++ b/src/core/frontend/scope_acquire_context.h
@@ -8,16 +8,16 @@
8 8
9namespace Core::Frontend { 9namespace Core::Frontend {
10 10
11class EmuWindow; 11class GraphicsContext;
12 12
13/// Helper class to acquire/release window context within a given scope 13/// Helper class to acquire/release window context within a given scope
14class ScopeAcquireWindowContext : NonCopyable { 14class ScopeAcquireContext : NonCopyable {
15public: 15public:
16 explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window); 16 explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context);
17 ~ScopeAcquireWindowContext(); 17 ~ScopeAcquireContext();
18 18
19private: 19private:
20 Core::Frontend::EmuWindow& emu_window; 20 Core::Frontend::GraphicsContext& context;
21}; 21};
22 22
23} // namespace Core::Frontend 23} // namespace Core::Frontend
diff --git a/src/core/frontend/scope_acquire_window_context.cpp b/src/core/frontend/scope_acquire_window_context.cpp
deleted file mode 100644
index 3663dad17..000000000
--- a/src/core/frontend/scope_acquire_window_context.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/emu_window.h"
6#include "core/frontend/scope_acquire_window_context.h"
7
8namespace Core::Frontend {
9
10ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_)
11 : emu_window{emu_window_} {
12 emu_window.MakeCurrent();
13}
14ScopeAcquireWindowContext::~ScopeAcquireWindowContext() {
15 emu_window.DoneCurrent();
16}
17
18} // namespace Core::Frontend
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 67e95999d..e8d8871a7 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -217,7 +217,7 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
217 return 0; 217 return 0;
218 } 218 }
219 219
220 const auto& thread_context = thread->GetContext(); 220 const auto& thread_context = thread->GetContext64();
221 221
222 if (id < SP_REGISTER) { 222 if (id < SP_REGISTER) {
223 return thread_context.cpu_registers[id]; 223 return thread_context.cpu_registers[id];
@@ -239,7 +239,7 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
239 return; 239 return;
240 } 240 }
241 241
242 auto& thread_context = thread->GetContext(); 242 auto& thread_context = thread->GetContext64();
243 243
244 if (id < SP_REGISTER) { 244 if (id < SP_REGISTER) {
245 thread_context.cpu_registers[id] = val; 245 thread_context.cpu_registers[id] = val;
@@ -259,7 +259,7 @@ static u128 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) {
259 return u128{0}; 259 return u128{0};
260 } 260 }
261 261
262 auto& thread_context = thread->GetContext(); 262 auto& thread_context = thread->GetContext64();
263 263
264 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { 264 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
265 return thread_context.vector_registers[id - UC_ARM64_REG_Q0]; 265 return thread_context.vector_registers[id - UC_ARM64_REG_Q0];
@@ -275,7 +275,7 @@ static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr)
275 return; 275 return;
276 } 276 }
277 277
278 auto& thread_context = thread->GetContext(); 278 auto& thread_context = thread->GetContext64();
279 279
280 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { 280 if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) {
281 thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val; 281 thread_context.vector_registers[id - UC_ARM64_REG_Q0] = val;
@@ -916,7 +916,7 @@ static void WriteRegister() {
916 // Update ARM context, skipping scheduler - no running threads at this point 916 // Update ARM context, skipping scheduler - no running threads at this point
917 Core::System::GetInstance() 917 Core::System::GetInstance()
918 .ArmInterface(current_core) 918 .ArmInterface(current_core)
919 .LoadContext(current_thread->GetContext()); 919 .LoadContext(current_thread->GetContext64());
920 920
921 SendReply("OK"); 921 SendReply("OK");
922} 922}
@@ -947,7 +947,7 @@ static void WriteRegisters() {
947 // Update ARM context, skipping scheduler - no running threads at this point 947 // Update ARM context, skipping scheduler - no running threads at this point
948 Core::System::GetInstance() 948 Core::System::GetInstance()
949 .ArmInterface(current_core) 949 .ArmInterface(current_core)
950 .LoadContext(current_thread->GetContext()); 950 .LoadContext(current_thread->GetContext64());
951 951
952 SendReply("OK"); 952 SendReply("OK");
953} 953}
@@ -1019,7 +1019,7 @@ static void Step() {
1019 // Update ARM context, skipping scheduler - no running threads at this point 1019 // Update ARM context, skipping scheduler - no running threads at this point
1020 Core::System::GetInstance() 1020 Core::System::GetInstance()
1021 .ArmInterface(current_core) 1021 .ArmInterface(current_core)
1022 .LoadContext(current_thread->GetContext()); 1022 .LoadContext(current_thread->GetContext64());
1023 } 1023 }
1024 step_loop = true; 1024 step_loop = true;
1025 halt_loop = true; 1025 halt_loop = true;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 9232f4d7e..e47f1deed 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -186,6 +186,10 @@ struct KernelCore::Impl {
186 return; 186 return;
187 } 187 }
188 188
189 for (auto& core : cores) {
190 core.SetIs64Bit(process->Is64BitProcess());
191 }
192
189 system.Memory().SetCurrentPageTable(*process); 193 system.Memory().SetCurrentPageTable(*process);
190 } 194 }
191 195
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 9303dd273..aa2787467 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -5,7 +5,8 @@
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/arm/arm_interface.h" 6#include "core/arm/arm_interface.h"
7#ifdef ARCHITECTURE_x86_64 7#ifdef ARCHITECTURE_x86_64
8#include "core/arm/dynarmic/arm_dynarmic.h" 8#include "core/arm/dynarmic/arm_dynarmic_32.h"
9#include "core/arm/dynarmic/arm_dynarmic_64.h"
9#endif 10#endif
10#include "core/arm/exclusive_monitor.h" 11#include "core/arm/exclusive_monitor.h"
11#include "core/arm/unicorn/arm_unicorn.h" 12#include "core/arm/unicorn/arm_unicorn.h"
@@ -20,13 +21,17 @@ PhysicalCore::PhysicalCore(Core::System& system, std::size_t id,
20 Core::ExclusiveMonitor& exclusive_monitor) 21 Core::ExclusiveMonitor& exclusive_monitor)
21 : core_index{id} { 22 : core_index{id} {
22#ifdef ARCHITECTURE_x86_64 23#ifdef ARCHITECTURE_x86_64
23 arm_interface = std::make_unique<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index); 24 arm_interface_32 =
25 std::make_unique<Core::ARM_Dynarmic_32>(system, exclusive_monitor, core_index);
26 arm_interface_64 =
27 std::make_unique<Core::ARM_Dynarmic_64>(system, exclusive_monitor, core_index);
28
24#else 29#else
25 arm_interface = std::make_shared<Core::ARM_Unicorn>(system); 30 arm_interface = std::make_shared<Core::ARM_Unicorn>(system);
26 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 31 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
27#endif 32#endif
28 33
29 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index); 34 scheduler = std::make_unique<Kernel::Scheduler>(system, core_index);
30} 35}
31 36
32PhysicalCore::~PhysicalCore() = default; 37PhysicalCore::~PhysicalCore() = default;
@@ -48,4 +53,12 @@ void PhysicalCore::Shutdown() {
48 scheduler->Shutdown(); 53 scheduler->Shutdown();
49} 54}
50 55
56void PhysicalCore::SetIs64Bit(bool is_64_bit) {
57 if (is_64_bit) {
58 arm_interface = arm_interface_64.get();
59 } else {
60 arm_interface = arm_interface_32.get();
61 }
62}
63
51} // namespace Kernel 64} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 4c32c0f1b..3269166be 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -68,10 +68,14 @@ public:
68 return *scheduler; 68 return *scheduler;
69 } 69 }
70 70
71 void SetIs64Bit(bool is_64_bit);
72
71private: 73private:
72 std::size_t core_index; 74 std::size_t core_index;
73 std::unique_ptr<Core::ARM_Interface> arm_interface; 75 std::unique_ptr<Core::ARM_Interface> arm_interface_32;
76 std::unique_ptr<Core::ARM_Interface> arm_interface_64;
74 std::unique_ptr<Kernel::Scheduler> scheduler; 77 std::unique_ptr<Kernel::Scheduler> scheduler;
78 Core::ARM_Interface* arm_interface{};
75}; 79};
76 80
77} // namespace Kernel 81} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 2fcb7326c..edc414d69 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -42,7 +42,8 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
42 42
43 // Register 1 must be a handle to the main thread 43 // Register 1 must be a handle to the main thread
44 const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); 44 const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
45 thread->GetContext().cpu_registers[1] = thread_handle; 45 thread->GetContext32().cpu_registers[1] = thread_handle;
46 thread->GetContext64().cpu_registers[1] = thread_handle;
46 47
47 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires 48 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
48 thread->ResumeFromWait(); 49 thread->ResumeFromWait();
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index c65f82fb7..1140c72a3 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -383,8 +383,8 @@ void GlobalScheduler::Unlock() {
383 // TODO(Blinkhawk): Setup the interrupts and change context on current core. 383 // TODO(Blinkhawk): Setup the interrupts and change context on current core.
384} 384}
385 385
386Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id) 386Scheduler::Scheduler(Core::System& system, std::size_t core_id)
387 : system(system), cpu_core(cpu_core), core_id(core_id) {} 387 : system{system}, core_id{core_id} {}
388 388
389Scheduler::~Scheduler() = default; 389Scheduler::~Scheduler() = default;
390 390
@@ -422,9 +422,10 @@ void Scheduler::UnloadThread() {
422 422
423 // Save context for previous thread 423 // Save context for previous thread
424 if (previous_thread) { 424 if (previous_thread) {
425 cpu_core.SaveContext(previous_thread->GetContext()); 425 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32());
426 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64());
426 // Save the TPIDR_EL0 system register in case it was modified. 427 // Save the TPIDR_EL0 system register in case it was modified.
427 previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); 428 previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
428 429
429 if (previous_thread->GetStatus() == ThreadStatus::Running) { 430 if (previous_thread->GetStatus() == ThreadStatus::Running) {
430 // This is only the case when a reschedule is triggered without the current thread 431 // This is only the case when a reschedule is triggered without the current thread
@@ -451,9 +452,10 @@ void Scheduler::SwitchContext() {
451 452
452 // Save context for previous thread 453 // Save context for previous thread
453 if (previous_thread) { 454 if (previous_thread) {
454 cpu_core.SaveContext(previous_thread->GetContext()); 455 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32());
456 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64());
455 // Save the TPIDR_EL0 system register in case it was modified. 457 // Save the TPIDR_EL0 system register in case it was modified.
456 previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); 458 previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
457 459
458 if (previous_thread->GetStatus() == ThreadStatus::Running) { 460 if (previous_thread->GetStatus() == ThreadStatus::Running) {
459 // This is only the case when a reschedule is triggered without the current thread 461 // This is only the case when a reschedule is triggered without the current thread
@@ -481,9 +483,10 @@ void Scheduler::SwitchContext() {
481 system.Kernel().MakeCurrentProcess(thread_owner_process); 483 system.Kernel().MakeCurrentProcess(thread_owner_process);
482 } 484 }
483 485
484 cpu_core.LoadContext(new_thread->GetContext()); 486 system.ArmInterface(core_id).LoadContext(new_thread->GetContext32());
485 cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); 487 system.ArmInterface(core_id).LoadContext(new_thread->GetContext64());
486 cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); 488 system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress());
489 system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
487 } else { 490 } else {
488 current_thread = nullptr; 491 current_thread = nullptr;
489 // Note: We do not reset the current process and current page table when idling because 492 // Note: We do not reset the current process and current page table when idling because
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 1c93a838c..07df33f9c 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -181,7 +181,7 @@ private:
181 181
182class Scheduler final { 182class Scheduler final {
183public: 183public:
184 explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id); 184 explicit Scheduler(Core::System& system, std::size_t core_id);
185 ~Scheduler(); 185 ~Scheduler();
186 186
187 /// Returns whether there are any threads that are ready to run. 187 /// Returns whether there are any threads that are ready to run.
@@ -235,7 +235,6 @@ private:
235 std::shared_ptr<Thread> selected_thread = nullptr; 235 std::shared_ptr<Thread> selected_thread = nullptr;
236 236
237 Core::System& system; 237 Core::System& system;
238 Core::ARM_Interface& cpu_core;
239 u64 last_context_switch_time = 0; 238 u64 last_context_switch_time = 0;
240 u64 idle_selection_count = 0; 239 u64 idle_selection_count = 0;
241 const std::size_t core_id; 240 const std::size_t core_id;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index fd91779a3..4ffc113c2 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -187,6 +187,13 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_s
187 return RESULT_SUCCESS; 187 return RESULT_SUCCESS;
188} 188}
189 189
190static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
191 VAddr temp_heap_addr{};
192 const ResultCode result{SetHeapSize(system, &temp_heap_addr, heap_size)};
193 *heap_addr = static_cast<u32>(temp_heap_addr);
194 return result;
195}
196
190static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) { 197static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) {
191 LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); 198 LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
192 199
@@ -371,6 +378,12 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
371 return RESULT_SUCCESS; 378 return RESULT_SUCCESS;
372} 379}
373 380
381static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
382 u32 port_name_address) {
383
384 return ConnectToNamedPort(system, out_handle, port_name_address);
385}
386
374/// Makes a blocking IPC call to an OS service. 387/// Makes a blocking IPC call to an OS service.
375static ResultCode SendSyncRequest(Core::System& system, Handle handle) { 388static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
376 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 389 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
@@ -390,6 +403,10 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
390 return session->SendSyncRequest(SharedFrom(thread), system.Memory()); 403 return session->SendSyncRequest(SharedFrom(thread), system.Memory());
391} 404}
392 405
406static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
407 return SendSyncRequest(system, handle);
408}
409
393/// Get the ID for the specified thread. 410/// Get the ID for the specified thread.
394static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) { 411static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) {
395 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); 412 LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
@@ -405,6 +422,17 @@ static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle threa
405 return RESULT_SUCCESS; 422 return RESULT_SUCCESS;
406} 423}
407 424
425static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high,
426 Handle thread_handle) {
427 u64 thread_id{};
428 const ResultCode result{GetThreadId(system, &thread_id, thread_handle)};
429
430 *thread_id_low = static_cast<u32>(thread_id >> 32);
431 *thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max());
432
433 return result;
434}
435
408/// Gets the ID of the specified process or a specified thread's owning process. 436/// Gets the ID of the specified process or a specified thread's owning process.
409static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) { 437static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) {
410 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); 438 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
@@ -479,6 +507,12 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
479 return result; 507 return result;
480} 508}
481 509
510static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
511 s32 handle_count, u32 timeout_high, Handle* index) {
512 const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
513 return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds);
514}
515
482/// Resumes a thread waiting on WaitSynchronization 516/// Resumes a thread waiting on WaitSynchronization
483static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) { 517static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
484 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); 518 LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
@@ -917,6 +951,18 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
917 } 951 }
918} 952}
919 953
954static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
955 u32 info_id, u32 handle, u32 sub_id_high) {
956 const u64 sub_id{static_cast<u64>(sub_id_low | (static_cast<u64>(sub_id_high) << 32))};
957 u64 res_value{};
958
959 const ResultCode result{GetInfo(system, &res_value, info_id, handle, sub_id)};
960 *result_high = static_cast<u32>(res_value >> 32);
961 *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
962
963 return result;
964}
965
920/// Maps memory at a desired address 966/// Maps memory at a desired address
921static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { 967static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
922 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size); 968 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
@@ -1058,7 +1104,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, H
1058 return ERR_BUSY; 1104 return ERR_BUSY;
1059 } 1105 }
1060 1106
1061 Core::ARM_Interface::ThreadContext ctx = thread->GetContext(); 1107 Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64();
1062 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. 1108 // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
1063 ctx.pstate &= 0xFF0FFE20; 1109 ctx.pstate &= 0xFF0FFE20;
1064 1110
@@ -1088,6 +1134,10 @@ static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle
1088 return RESULT_SUCCESS; 1134 return RESULT_SUCCESS;
1089} 1135}
1090 1136
1137static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) {
1138 return GetThreadPriority(system, priority, handle);
1139}
1140
1091/// Sets the priority for the specified thread 1141/// Sets the priority for the specified thread
1092static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) { 1142static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
1093 LOG_TRACE(Kernel_SVC, "called"); 1143 LOG_TRACE(Kernel_SVC, "called");
@@ -1259,6 +1309,11 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
1259 query_address); 1309 query_address);
1260} 1310}
1261 1311
1312static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address,
1313 u32 page_info_address, u32 query_address) {
1314 return QueryMemory(system, memory_info_address, page_info_address, query_address);
1315}
1316
1262static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, 1317static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1263 u64 src_address, u64 size) { 1318 u64 src_address, u64 size) {
1264 LOG_DEBUG(Kernel_SVC, 1319 LOG_DEBUG(Kernel_SVC,
@@ -1675,6 +1730,10 @@ static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_
1675 } 1730 }
1676} 1731}
1677 1732
1733static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) {
1734 SignalProcessWideKey(system, condition_variable_addr, target);
1735}
1736
1678// Wait for an address (via Address Arbiter) 1737// Wait for an address (via Address Arbiter)
1679static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, 1738static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
1680 s64 timeout) { 1739 s64 timeout) {
@@ -1760,6 +1819,10 @@ static ResultCode CloseHandle(Core::System& system, Handle handle) {
1760 return handle_table.Close(handle); 1819 return handle_table.Close(handle);
1761} 1820}
1762 1821
1822static ResultCode CloseHandle32(Core::System& system, Handle handle) {
1823 return CloseHandle(system, handle);
1824}
1825
1763/// Clears the signaled state of an event or process. 1826/// Clears the signaled state of an event or process.
1764static ResultCode ResetSignal(Core::System& system, Handle handle) { 1827static ResultCode ResetSignal(Core::System& system, Handle handle) {
1765 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); 1828 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
@@ -2317,69 +2380,196 @@ struct FunctionDef {
2317}; 2380};
2318} // namespace 2381} // namespace
2319 2382
2320static const FunctionDef SVC_Table[] = { 2383static const FunctionDef SVC_Table_32[] = {
2321 {0x00, nullptr, "Unknown"}, 2384 {0x00, nullptr, "Unknown"},
2322 {0x01, SvcWrap<SetHeapSize>, "SetHeapSize"}, 2385 {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
2323 {0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"}, 2386 {0x02, nullptr, "Unknown"},
2324 {0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"}, 2387 {0x03, nullptr, "SetMemoryAttribute32"},
2325 {0x04, SvcWrap<MapMemory>, "MapMemory"}, 2388 {0x04, nullptr, "MapMemory32"},
2326 {0x05, SvcWrap<UnmapMemory>, "UnmapMemory"}, 2389 {0x05, nullptr, "UnmapMemory32"},
2327 {0x06, SvcWrap<QueryMemory>, "QueryMemory"}, 2390 {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"},
2328 {0x07, SvcWrap<ExitProcess>, "ExitProcess"}, 2391 {0x07, nullptr, "ExitProcess32"},
2329 {0x08, SvcWrap<CreateThread>, "CreateThread"}, 2392 {0x08, nullptr, "CreateThread32"},
2330 {0x09, SvcWrap<StartThread>, "StartThread"}, 2393 {0x09, nullptr, "StartThread32"},
2331 {0x0A, SvcWrap<ExitThread>, "ExitThread"}, 2394 {0x0a, nullptr, "ExitThread32"},
2332 {0x0B, SvcWrap<SleepThread>, "SleepThread"}, 2395 {0x0b, nullptr, "SleepThread32"},
2333 {0x0C, SvcWrap<GetThreadPriority>, "GetThreadPriority"}, 2396 {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"},
2334 {0x0D, SvcWrap<SetThreadPriority>, "SetThreadPriority"}, 2397 {0x0d, nullptr, "SetThreadPriority32"},
2335 {0x0E, SvcWrap<GetThreadCoreMask>, "GetThreadCoreMask"}, 2398 {0x0e, nullptr, "GetThreadCoreMask32"},
2336 {0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"}, 2399 {0x0f, nullptr, "SetThreadCoreMask32"},
2337 {0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"}, 2400 {0x10, nullptr, "GetCurrentProcessorNumber32"},
2338 {0x11, SvcWrap<SignalEvent>, "SignalEvent"}, 2401 {0x11, nullptr, "SignalEvent32"},
2339 {0x12, SvcWrap<ClearEvent>, "ClearEvent"}, 2402 {0x12, nullptr, "ClearEvent32"},
2340 {0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"}, 2403 {0x13, nullptr, "MapSharedMemory32"},
2341 {0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"}, 2404 {0x14, nullptr, "UnmapSharedMemory32"},
2342 {0x15, SvcWrap<CreateTransferMemory>, "CreateTransferMemory"}, 2405 {0x15, nullptr, "CreateTransferMemory32"},
2343 {0x16, SvcWrap<CloseHandle>, "CloseHandle"}, 2406 {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"},
2344 {0x17, SvcWrap<ResetSignal>, "ResetSignal"}, 2407 {0x17, nullptr, "ResetSignal32"},
2345 {0x18, SvcWrap<WaitSynchronization>, "WaitSynchronization"}, 2408 {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"},
2346 {0x19, SvcWrap<CancelSynchronization>, "CancelSynchronization"}, 2409 {0x19, nullptr, "CancelSynchronization32"},
2347 {0x1A, SvcWrap<ArbitrateLock>, "ArbitrateLock"}, 2410 {0x1a, nullptr, "ArbitrateLock32"},
2348 {0x1B, SvcWrap<ArbitrateUnlock>, "ArbitrateUnlock"}, 2411 {0x1b, nullptr, "ArbitrateUnlock32"},
2349 {0x1C, SvcWrap<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"}, 2412 {0x1c, nullptr, "WaitProcessWideKeyAtomic32"},
2350 {0x1D, SvcWrap<SignalProcessWideKey>, "SignalProcessWideKey"}, 2413 {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
2351 {0x1E, SvcWrap<GetSystemTick>, "GetSystemTick"}, 2414 {0x1e, nullptr, "GetSystemTick32"},
2352 {0x1F, SvcWrap<ConnectToNamedPort>, "ConnectToNamedPort"}, 2415 {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
2416 {0x20, nullptr, "Unknown"},
2417 {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
2418 {0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
2419 {0x23, nullptr, "Unknown"},
2420 {0x24, nullptr, "GetProcessId32"},
2421 {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
2422 {0x26, nullptr, "Break32"},
2423 {0x27, nullptr, "OutputDebugString32"},
2424 {0x28, nullptr, "Unknown"},
2425 {0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
2426 {0x2a, nullptr, "Unknown"},
2427 {0x2b, nullptr, "Unknown"},
2428 {0x2c, nullptr, "MapPhysicalMemory32"},
2429 {0x2d, nullptr, "UnmapPhysicalMemory32"},
2430 {0x2e, nullptr, "Unknown"},
2431 {0x2f, nullptr, "Unknown"},
2432 {0x30, nullptr, "Unknown"},
2433 {0x31, nullptr, "Unknown"},
2434 {0x32, nullptr, "SetThreadActivity32"},
2435 {0x33, nullptr, "GetThreadContext32"},
2436 {0x34, nullptr, "WaitForAddress32"},
2437 {0x35, nullptr, "SignalToAddress32"},
2438 {0x36, nullptr, "Unknown"},
2439 {0x37, nullptr, "Unknown"},
2440 {0x38, nullptr, "Unknown"},
2441 {0x39, nullptr, "Unknown"},
2442 {0x3a, nullptr, "Unknown"},
2443 {0x3b, nullptr, "Unknown"},
2444 {0x3c, nullptr, "Unknown"},
2445 {0x3d, nullptr, "Unknown"},
2446 {0x3e, nullptr, "Unknown"},
2447 {0x3f, nullptr, "Unknown"},
2448 {0x40, nullptr, "CreateSession32"},
2449 {0x41, nullptr, "AcceptSession32"},
2450 {0x42, nullptr, "Unknown"},
2451 {0x43, nullptr, "ReplyAndReceive32"},
2452 {0x44, nullptr, "Unknown"},
2453 {0x45, nullptr, "CreateEvent32"},
2454 {0x46, nullptr, "Unknown"},
2455 {0x47, nullptr, "Unknown"},
2456 {0x48, nullptr, "Unknown"},
2457 {0x49, nullptr, "Unknown"},
2458 {0x4a, nullptr, "Unknown"},
2459 {0x4b, nullptr, "Unknown"},
2460 {0x4c, nullptr, "Unknown"},
2461 {0x4d, nullptr, "Unknown"},
2462 {0x4e, nullptr, "Unknown"},
2463 {0x4f, nullptr, "Unknown"},
2464 {0x50, nullptr, "Unknown"},
2465 {0x51, nullptr, "Unknown"},
2466 {0x52, nullptr, "Unknown"},
2467 {0x53, nullptr, "Unknown"},
2468 {0x54, nullptr, "Unknown"},
2469 {0x55, nullptr, "Unknown"},
2470 {0x56, nullptr, "Unknown"},
2471 {0x57, nullptr, "Unknown"},
2472 {0x58, nullptr, "Unknown"},
2473 {0x59, nullptr, "Unknown"},
2474 {0x5a, nullptr, "Unknown"},
2475 {0x5b, nullptr, "Unknown"},
2476 {0x5c, nullptr, "Unknown"},
2477 {0x5d, nullptr, "Unknown"},
2478 {0x5e, nullptr, "Unknown"},
2479 {0x5F, nullptr, "FlushProcessDataCache32"},
2480 {0x60, nullptr, "Unknown"},
2481 {0x61, nullptr, "Unknown"},
2482 {0x62, nullptr, "Unknown"},
2483 {0x63, nullptr, "Unknown"},
2484 {0x64, nullptr, "Unknown"},
2485 {0x65, nullptr, "GetProcessList32"},
2486 {0x66, nullptr, "Unknown"},
2487 {0x67, nullptr, "Unknown"},
2488 {0x68, nullptr, "Unknown"},
2489 {0x69, nullptr, "Unknown"},
2490 {0x6A, nullptr, "Unknown"},
2491 {0x6B, nullptr, "Unknown"},
2492 {0x6C, nullptr, "Unknown"},
2493 {0x6D, nullptr, "Unknown"},
2494 {0x6E, nullptr, "Unknown"},
2495 {0x6f, nullptr, "GetSystemInfo32"},
2496 {0x70, nullptr, "CreatePort32"},
2497 {0x71, nullptr, "ManageNamedPort32"},
2498 {0x72, nullptr, "ConnectToPort32"},
2499 {0x73, nullptr, "SetProcessMemoryPermission32"},
2500 {0x74, nullptr, "Unknown"},
2501 {0x75, nullptr, "Unknown"},
2502 {0x76, nullptr, "Unknown"},
2503 {0x77, nullptr, "MapProcessCodeMemory32"},
2504 {0x78, nullptr, "UnmapProcessCodeMemory32"},
2505 {0x79, nullptr, "Unknown"},
2506 {0x7A, nullptr, "Unknown"},
2507 {0x7B, nullptr, "TerminateProcess32"},
2508};
2509
2510static const FunctionDef SVC_Table_64[] = {
2511 {0x00, nullptr, "Unknown"},
2512 {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
2513 {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
2514 {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
2515 {0x04, SvcWrap64<MapMemory>, "MapMemory"},
2516 {0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
2517 {0x06, SvcWrap64<QueryMemory>, "QueryMemory"},
2518 {0x07, SvcWrap64<ExitProcess>, "ExitProcess"},
2519 {0x08, SvcWrap64<CreateThread>, "CreateThread"},
2520 {0x09, SvcWrap64<StartThread>, "StartThread"},
2521 {0x0A, SvcWrap64<ExitThread>, "ExitThread"},
2522 {0x0B, SvcWrap64<SleepThread>, "SleepThread"},
2523 {0x0C, SvcWrap64<GetThreadPriority>, "GetThreadPriority"},
2524 {0x0D, SvcWrap64<SetThreadPriority>, "SetThreadPriority"},
2525 {0x0E, SvcWrap64<GetThreadCoreMask>, "GetThreadCoreMask"},
2526 {0x0F, SvcWrap64<SetThreadCoreMask>, "SetThreadCoreMask"},
2527 {0x10, SvcWrap64<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
2528 {0x11, SvcWrap64<SignalEvent>, "SignalEvent"},
2529 {0x12, SvcWrap64<ClearEvent>, "ClearEvent"},
2530 {0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"},
2531 {0x14, SvcWrap64<UnmapSharedMemory>, "UnmapSharedMemory"},
2532 {0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"},
2533 {0x16, SvcWrap64<CloseHandle>, "CloseHandle"},
2534 {0x17, SvcWrap64<ResetSignal>, "ResetSignal"},
2535 {0x18, SvcWrap64<WaitSynchronization>, "WaitSynchronization"},
2536 {0x19, SvcWrap64<CancelSynchronization>, "CancelSynchronization"},
2537 {0x1A, SvcWrap64<ArbitrateLock>, "ArbitrateLock"},
2538 {0x1B, SvcWrap64<ArbitrateUnlock>, "ArbitrateUnlock"},
2539 {0x1C, SvcWrap64<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"},
2540 {0x1D, SvcWrap64<SignalProcessWideKey>, "SignalProcessWideKey"},
2541 {0x1E, SvcWrap64<GetSystemTick>, "GetSystemTick"},
2542 {0x1F, SvcWrap64<ConnectToNamedPort>, "ConnectToNamedPort"},
2353 {0x20, nullptr, "SendSyncRequestLight"}, 2543 {0x20, nullptr, "SendSyncRequestLight"},
2354 {0x21, SvcWrap<SendSyncRequest>, "SendSyncRequest"}, 2544 {0x21, SvcWrap64<SendSyncRequest>, "SendSyncRequest"},
2355 {0x22, nullptr, "SendSyncRequestWithUserBuffer"}, 2545 {0x22, nullptr, "SendSyncRequestWithUserBuffer"},
2356 {0x23, nullptr, "SendAsyncRequestWithUserBuffer"}, 2546 {0x23, nullptr, "SendAsyncRequestWithUserBuffer"},
2357 {0x24, SvcWrap<GetProcessId>, "GetProcessId"}, 2547 {0x24, SvcWrap64<GetProcessId>, "GetProcessId"},
2358 {0x25, SvcWrap<GetThreadId>, "GetThreadId"}, 2548 {0x25, SvcWrap64<GetThreadId>, "GetThreadId"},
2359 {0x26, SvcWrap<Break>, "Break"}, 2549 {0x26, SvcWrap64<Break>, "Break"},
2360 {0x27, SvcWrap<OutputDebugString>, "OutputDebugString"}, 2550 {0x27, SvcWrap64<OutputDebugString>, "OutputDebugString"},
2361 {0x28, nullptr, "ReturnFromException"}, 2551 {0x28, nullptr, "ReturnFromException"},
2362 {0x29, SvcWrap<GetInfo>, "GetInfo"}, 2552 {0x29, SvcWrap64<GetInfo>, "GetInfo"},
2363 {0x2A, nullptr, "FlushEntireDataCache"}, 2553 {0x2A, nullptr, "FlushEntireDataCache"},
2364 {0x2B, nullptr, "FlushDataCache"}, 2554 {0x2B, nullptr, "FlushDataCache"},
2365 {0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"}, 2555 {0x2C, SvcWrap64<MapPhysicalMemory>, "MapPhysicalMemory"},
2366 {0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"}, 2556 {0x2D, SvcWrap64<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
2367 {0x2E, nullptr, "GetFutureThreadInfo"}, 2557 {0x2E, nullptr, "GetFutureThreadInfo"},
2368 {0x2F, nullptr, "GetLastThreadInfo"}, 2558 {0x2F, nullptr, "GetLastThreadInfo"},
2369 {0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"}, 2559 {0x30, SvcWrap64<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
2370 {0x31, SvcWrap<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"}, 2560 {0x31, SvcWrap64<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"},
2371 {0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"}, 2561 {0x32, SvcWrap64<SetThreadActivity>, "SetThreadActivity"},
2372 {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"}, 2562 {0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
2373 {0x34, SvcWrap<WaitForAddress>, "WaitForAddress"}, 2563 {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
2374 {0x35, SvcWrap<SignalToAddress>, "SignalToAddress"}, 2564 {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
2375 {0x36, nullptr, "SynchronizePreemptionState"}, 2565 {0x36, nullptr, "SynchronizePreemptionState"},
2376 {0x37, nullptr, "Unknown"}, 2566 {0x37, nullptr, "Unknown"},
2377 {0x38, nullptr, "Unknown"}, 2567 {0x38, nullptr, "Unknown"},
2378 {0x39, nullptr, "Unknown"}, 2568 {0x39, nullptr, "Unknown"},
2379 {0x3A, nullptr, "Unknown"}, 2569 {0x3A, nullptr, "Unknown"},
2380 {0x3B, nullptr, "Unknown"}, 2570 {0x3B, nullptr, "Unknown"},
2381 {0x3C, SvcWrap<KernelDebug>, "KernelDebug"}, 2571 {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
2382 {0x3D, SvcWrap<ChangeKernelTraceState>, "ChangeKernelTraceState"}, 2572 {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
2383 {0x3E, nullptr, "Unknown"}, 2573 {0x3E, nullptr, "Unknown"},
2384 {0x3F, nullptr, "Unknown"}, 2574 {0x3F, nullptr, "Unknown"},
2385 {0x40, nullptr, "CreateSession"}, 2575 {0x40, nullptr, "CreateSession"},
@@ -2387,7 +2577,7 @@ static const FunctionDef SVC_Table[] = {
2387 {0x42, nullptr, "ReplyAndReceiveLight"}, 2577 {0x42, nullptr, "ReplyAndReceiveLight"},
2388 {0x43, nullptr, "ReplyAndReceive"}, 2578 {0x43, nullptr, "ReplyAndReceive"},
2389 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, 2579 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
2390 {0x45, SvcWrap<CreateEvent>, "CreateEvent"}, 2580 {0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
2391 {0x46, nullptr, "Unknown"}, 2581 {0x46, nullptr, "Unknown"},
2392 {0x47, nullptr, "Unknown"}, 2582 {0x47, nullptr, "Unknown"},
2393 {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, 2583 {0x48, nullptr, "MapPhysicalMemoryUnsafe"},
@@ -2398,9 +2588,9 @@ static const FunctionDef SVC_Table[] = {
2398 {0x4D, nullptr, "SleepSystem"}, 2588 {0x4D, nullptr, "SleepSystem"},
2399 {0x4E, nullptr, "ReadWriteRegister"}, 2589 {0x4E, nullptr, "ReadWriteRegister"},
2400 {0x4F, nullptr, "SetProcessActivity"}, 2590 {0x4F, nullptr, "SetProcessActivity"},
2401 {0x50, SvcWrap<CreateSharedMemory>, "CreateSharedMemory"}, 2591 {0x50, SvcWrap64<CreateSharedMemory>, "CreateSharedMemory"},
2402 {0x51, SvcWrap<MapTransferMemory>, "MapTransferMemory"}, 2592 {0x51, SvcWrap64<MapTransferMemory>, "MapTransferMemory"},
2403 {0x52, SvcWrap<UnmapTransferMemory>, "UnmapTransferMemory"}, 2593 {0x52, SvcWrap64<UnmapTransferMemory>, "UnmapTransferMemory"},
2404 {0x53, nullptr, "CreateInterruptEvent"}, 2594 {0x53, nullptr, "CreateInterruptEvent"},
2405 {0x54, nullptr, "QueryPhysicalAddress"}, 2595 {0x54, nullptr, "QueryPhysicalAddress"},
2406 {0x55, nullptr, "QueryIoMapping"}, 2596 {0x55, nullptr, "QueryIoMapping"},
@@ -2419,8 +2609,8 @@ static const FunctionDef SVC_Table[] = {
2419 {0x62, nullptr, "TerminateDebugProcess"}, 2609 {0x62, nullptr, "TerminateDebugProcess"},
2420 {0x63, nullptr, "GetDebugEvent"}, 2610 {0x63, nullptr, "GetDebugEvent"},
2421 {0x64, nullptr, "ContinueDebugEvent"}, 2611 {0x64, nullptr, "ContinueDebugEvent"},
2422 {0x65, SvcWrap<GetProcessList>, "GetProcessList"}, 2612 {0x65, SvcWrap64<GetProcessList>, "GetProcessList"},
2423 {0x66, SvcWrap<GetThreadList>, "GetThreadList"}, 2613 {0x66, SvcWrap64<GetThreadList>, "GetThreadList"},
2424 {0x67, nullptr, "GetDebugThreadContext"}, 2614 {0x67, nullptr, "GetDebugThreadContext"},
2425 {0x68, nullptr, "SetDebugThreadContext"}, 2615 {0x68, nullptr, "SetDebugThreadContext"},
2426 {0x69, nullptr, "QueryDebugProcessMemory"}, 2616 {0x69, nullptr, "QueryDebugProcessMemory"},
@@ -2436,24 +2626,32 @@ static const FunctionDef SVC_Table[] = {
2436 {0x73, nullptr, "SetProcessMemoryPermission"}, 2626 {0x73, nullptr, "SetProcessMemoryPermission"},
2437 {0x74, nullptr, "MapProcessMemory"}, 2627 {0x74, nullptr, "MapProcessMemory"},
2438 {0x75, nullptr, "UnmapProcessMemory"}, 2628 {0x75, nullptr, "UnmapProcessMemory"},
2439 {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, 2629 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
2440 {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"}, 2630 {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
2441 {0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, 2631 {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
2442 {0x79, nullptr, "CreateProcess"}, 2632 {0x79, nullptr, "CreateProcess"},
2443 {0x7A, nullptr, "StartProcess"}, 2633 {0x7A, nullptr, "StartProcess"},
2444 {0x7B, nullptr, "TerminateProcess"}, 2634 {0x7B, nullptr, "TerminateProcess"},
2445 {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"}, 2635 {0x7C, SvcWrap64<GetProcessInfo>, "GetProcessInfo"},
2446 {0x7D, SvcWrap<CreateResourceLimit>, "CreateResourceLimit"}, 2636 {0x7D, SvcWrap64<CreateResourceLimit>, "CreateResourceLimit"},
2447 {0x7E, SvcWrap<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"}, 2637 {0x7E, SvcWrap64<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"},
2448 {0x7F, nullptr, "CallSecureMonitor"}, 2638 {0x7F, nullptr, "CallSecureMonitor"},
2449}; 2639};
2450 2640
2451static const FunctionDef* GetSVCInfo(u32 func_num) { 2641static const FunctionDef* GetSVCInfo32(u32 func_num) {
2452 if (func_num >= std::size(SVC_Table)) { 2642 if (func_num >= std::size(SVC_Table_32)) {
2643 LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
2644 return nullptr;
2645 }
2646 return &SVC_Table_32[func_num];
2647}
2648
2649static const FunctionDef* GetSVCInfo64(u32 func_num) {
2650 if (func_num >= std::size(SVC_Table_64)) {
2453 LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num); 2651 LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
2454 return nullptr; 2652 return nullptr;
2455 } 2653 }
2456 return &SVC_Table[func_num]; 2654 return &SVC_Table_64[func_num];
2457} 2655}
2458 2656
2459MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); 2657MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
@@ -2464,7 +2662,8 @@ void CallSVC(Core::System& system, u32 immediate) {
2464 // Lock the global kernel mutex when we enter the kernel HLE. 2662 // Lock the global kernel mutex when we enter the kernel HLE.
2465 std::lock_guard lock{HLE::g_hle_lock}; 2663 std::lock_guard lock{HLE::g_hle_lock};
2466 2664
2467 const FunctionDef* info = GetSVCInfo(immediate); 2665 const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
2666 : GetSVCInfo32(immediate);
2468 if (info) { 2667 if (info) {
2469 if (info->func) { 2668 if (info->func) {
2470 info->func(system); 2669 info->func(system);
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 29a2cfa9d..7d735e3fa 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -15,6 +15,10 @@ static inline u64 Param(const Core::System& system, int n) {
15 return system.CurrentArmInterface().GetReg(n); 15 return system.CurrentArmInterface().GetReg(n);
16} 16}
17 17
18static inline u32 Param32(const Core::System& system, int n) {
19 return static_cast<u32>(system.CurrentArmInterface().GetReg(n));
20}
21
18/** 22/**
19 * HLE a function return from the current ARM userland process 23 * HLE a function return from the current ARM userland process
20 * @param system System context 24 * @param system System context
@@ -24,40 +28,44 @@ static inline void FuncReturn(Core::System& system, u64 result) {
24 system.CurrentArmInterface().SetReg(0, result); 28 system.CurrentArmInterface().SetReg(0, result);
25} 29}
26 30
31static inline void FuncReturn32(Core::System& system, u32 result) {
32 system.CurrentArmInterface().SetReg(0, (u64)result);
33}
34
27//////////////////////////////////////////////////////////////////////////////////////////////////// 35////////////////////////////////////////////////////////////////////////////////////////////////////
28// Function wrappers that return type ResultCode 36// Function wrappers that return type ResultCode
29 37
30template <ResultCode func(Core::System&, u64)> 38template <ResultCode func(Core::System&, u64)>
31void SvcWrap(Core::System& system) { 39void SvcWrap64(Core::System& system) {
32 FuncReturn(system, func(system, Param(system, 0)).raw); 40 FuncReturn(system, func(system, Param(system, 0)).raw);
33} 41}
34 42
35template <ResultCode func(Core::System&, u64, u64)> 43template <ResultCode func(Core::System&, u64, u64)>
36void SvcWrap(Core::System& system) { 44void SvcWrap64(Core::System& system) {
37 FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw); 45 FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
38} 46}
39 47
40template <ResultCode func(Core::System&, u32)> 48template <ResultCode func(Core::System&, u32)>
41void SvcWrap(Core::System& system) { 49void SvcWrap64(Core::System& system) {
42 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); 50 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
43} 51}
44 52
45template <ResultCode func(Core::System&, u32, u32)> 53template <ResultCode func(Core::System&, u32, u32)>
46void SvcWrap(Core::System& system) { 54void SvcWrap64(Core::System& system) {
47 FuncReturn( 55 FuncReturn(
48 system, 56 system,
49 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); 57 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
50} 58}
51 59
52template <ResultCode func(Core::System&, u32, u64, u64, u64)> 60template <ResultCode func(Core::System&, u32, u64, u64, u64)>
53void SvcWrap(Core::System& system) { 61void SvcWrap64(Core::System& system) {
54 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), 62 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
55 Param(system, 2), Param(system, 3)) 63 Param(system, 2), Param(system, 3))
56 .raw); 64 .raw);
57} 65}
58 66
59template <ResultCode func(Core::System&, u32*)> 67template <ResultCode func(Core::System&, u32*)>
60void SvcWrap(Core::System& system) { 68void SvcWrap64(Core::System& system) {
61 u32 param = 0; 69 u32 param = 0;
62 const u32 retval = func(system, &param).raw; 70 const u32 retval = func(system, &param).raw;
63 system.CurrentArmInterface().SetReg(1, param); 71 system.CurrentArmInterface().SetReg(1, param);
@@ -65,7 +73,7 @@ void SvcWrap(Core::System& system) {
65} 73}
66 74
67template <ResultCode func(Core::System&, u32*, u32)> 75template <ResultCode func(Core::System&, u32*, u32)>
68void SvcWrap(Core::System& system) { 76void SvcWrap64(Core::System& system) {
69 u32 param_1 = 0; 77 u32 param_1 = 0;
70 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw; 78 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
71 system.CurrentArmInterface().SetReg(1, param_1); 79 system.CurrentArmInterface().SetReg(1, param_1);
@@ -73,7 +81,7 @@ void SvcWrap(Core::System& system) {
73} 81}
74 82
75template <ResultCode func(Core::System&, u32*, u32*)> 83template <ResultCode func(Core::System&, u32*, u32*)>
76void SvcWrap(Core::System& system) { 84void SvcWrap64(Core::System& system) {
77 u32 param_1 = 0; 85 u32 param_1 = 0;
78 u32 param_2 = 0; 86 u32 param_2 = 0;
79 const u32 retval = func(system, &param_1, &param_2).raw; 87 const u32 retval = func(system, &param_1, &param_2).raw;
@@ -86,7 +94,7 @@ void SvcWrap(Core::System& system) {
86} 94}
87 95
88template <ResultCode func(Core::System&, u32*, u64)> 96template <ResultCode func(Core::System&, u32*, u64)>
89void SvcWrap(Core::System& system) { 97void SvcWrap64(Core::System& system) {
90 u32 param_1 = 0; 98 u32 param_1 = 0;
91 const u32 retval = func(system, &param_1, Param(system, 1)).raw; 99 const u32 retval = func(system, &param_1, Param(system, 1)).raw;
92 system.CurrentArmInterface().SetReg(1, param_1); 100 system.CurrentArmInterface().SetReg(1, param_1);
@@ -94,7 +102,7 @@ void SvcWrap(Core::System& system) {
94} 102}
95 103
96template <ResultCode func(Core::System&, u32*, u64, u32)> 104template <ResultCode func(Core::System&, u32*, u64, u32)>
97void SvcWrap(Core::System& system) { 105void SvcWrap64(Core::System& system) {
98 u32 param_1 = 0; 106 u32 param_1 = 0;
99 const u32 retval = 107 const u32 retval =
100 func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw; 108 func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
@@ -104,7 +112,7 @@ void SvcWrap(Core::System& system) {
104} 112}
105 113
106template <ResultCode func(Core::System&, u64*, u32)> 114template <ResultCode func(Core::System&, u64*, u32)>
107void SvcWrap(Core::System& system) { 115void SvcWrap64(Core::System& system) {
108 u64 param_1 = 0; 116 u64 param_1 = 0;
109 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw; 117 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
110 118
@@ -113,12 +121,12 @@ void SvcWrap(Core::System& system) {
113} 121}
114 122
115template <ResultCode func(Core::System&, u64, u32)> 123template <ResultCode func(Core::System&, u64, u32)>
116void SvcWrap(Core::System& system) { 124void SvcWrap64(Core::System& system) {
117 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw); 125 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
118} 126}
119 127
120template <ResultCode func(Core::System&, u64*, u64)> 128template <ResultCode func(Core::System&, u64*, u64)>
121void SvcWrap(Core::System& system) { 129void SvcWrap64(Core::System& system) {
122 u64 param_1 = 0; 130 u64 param_1 = 0;
123 const u32 retval = func(system, &param_1, Param(system, 1)).raw; 131 const u32 retval = func(system, &param_1, Param(system, 1)).raw;
124 132
@@ -127,7 +135,7 @@ void SvcWrap(Core::System& system) {
127} 135}
128 136
129template <ResultCode func(Core::System&, u64*, u32, u32)> 137template <ResultCode func(Core::System&, u64*, u32, u32)>
130void SvcWrap(Core::System& system) { 138void SvcWrap64(Core::System& system) {
131 u64 param_1 = 0; 139 u64 param_1 = 0;
132 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)), 140 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
133 static_cast<u32>(Param(system, 2))) 141 static_cast<u32>(Param(system, 2)))
@@ -138,19 +146,19 @@ void SvcWrap(Core::System& system) {
138} 146}
139 147
140template <ResultCode func(Core::System&, u32, u64)> 148template <ResultCode func(Core::System&, u32, u64)>
141void SvcWrap(Core::System& system) { 149void SvcWrap64(Core::System& system) {
142 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw); 150 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
143} 151}
144 152
145template <ResultCode func(Core::System&, u32, u32, u64)> 153template <ResultCode func(Core::System&, u32, u32, u64)>
146void SvcWrap(Core::System& system) { 154void SvcWrap64(Core::System& system) {
147 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), 155 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
148 static_cast<u32>(Param(system, 1)), Param(system, 2)) 156 static_cast<u32>(Param(system, 1)), Param(system, 2))
149 .raw); 157 .raw);
150} 158}
151 159
152template <ResultCode func(Core::System&, u32, u32*, u64*)> 160template <ResultCode func(Core::System&, u32, u32*, u64*)>
153void SvcWrap(Core::System& system) { 161void SvcWrap64(Core::System& system) {
154 u32 param_1 = 0; 162 u32 param_1 = 0;
155 u64 param_2 = 0; 163 u64 param_2 = 0;
156 const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2); 164 const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
@@ -161,54 +169,54 @@ void SvcWrap(Core::System& system) {
161} 169}
162 170
163template <ResultCode func(Core::System&, u64, u64, u32, u32)> 171template <ResultCode func(Core::System&, u64, u64, u32, u32)>
164void SvcWrap(Core::System& system) { 172void SvcWrap64(Core::System& system) {
165 FuncReturn(system, func(system, Param(system, 0), Param(system, 1), 173 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
166 static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) 174 static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
167 .raw); 175 .raw);
168} 176}
169 177
170template <ResultCode func(Core::System&, u64, u64, u32, u64)> 178template <ResultCode func(Core::System&, u64, u64, u32, u64)>
171void SvcWrap(Core::System& system) { 179void SvcWrap64(Core::System& system) {
172 FuncReturn(system, func(system, Param(system, 0), Param(system, 1), 180 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
173 static_cast<u32>(Param(system, 2)), Param(system, 3)) 181 static_cast<u32>(Param(system, 2)), Param(system, 3))
174 .raw); 182 .raw);
175} 183}
176 184
177template <ResultCode func(Core::System&, u32, u64, u32)> 185template <ResultCode func(Core::System&, u32, u64, u32)>
178void SvcWrap(Core::System& system) { 186void SvcWrap64(Core::System& system) {
179 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), 187 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
180 static_cast<u32>(Param(system, 2))) 188 static_cast<u32>(Param(system, 2)))
181 .raw); 189 .raw);
182} 190}
183 191
184template <ResultCode func(Core::System&, u64, u64, u64)> 192template <ResultCode func(Core::System&, u64, u64, u64)>
185void SvcWrap(Core::System& system) { 193void SvcWrap64(Core::System& system) {
186 FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw); 194 FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
187} 195}
188 196
189template <ResultCode func(Core::System&, u64, u64, u32)> 197template <ResultCode func(Core::System&, u64, u64, u32)>
190void SvcWrap(Core::System& system) { 198void SvcWrap64(Core::System& system) {
191 FuncReturn( 199 FuncReturn(
192 system, 200 system,
193 func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw); 201 func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
194} 202}
195 203
196template <ResultCode func(Core::System&, u32, u64, u64, u32)> 204template <ResultCode func(Core::System&, u32, u64, u64, u32)>
197void SvcWrap(Core::System& system) { 205void SvcWrap64(Core::System& system) {
198 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), 206 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
199 Param(system, 2), static_cast<u32>(Param(system, 3))) 207 Param(system, 2), static_cast<u32>(Param(system, 3)))
200 .raw); 208 .raw);
201} 209}
202 210
203template <ResultCode func(Core::System&, u32, u64, u64)> 211template <ResultCode func(Core::System&, u32, u64, u64)>
204void SvcWrap(Core::System& system) { 212void SvcWrap64(Core::System& system) {
205 FuncReturn( 213 FuncReturn(
206 system, 214 system,
207 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw); 215 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
208} 216}
209 217
210template <ResultCode func(Core::System&, u32*, u64, u64, s64)> 218template <ResultCode func(Core::System&, u32*, u64, u64, s64)>
211void SvcWrap(Core::System& system) { 219void SvcWrap64(Core::System& system) {
212 u32 param_1 = 0; 220 u32 param_1 = 0;
213 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)), 221 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
214 static_cast<s64>(Param(system, 3))) 222 static_cast<s64>(Param(system, 3)))
@@ -219,14 +227,14 @@ void SvcWrap(Core::System& system) {
219} 227}
220 228
221template <ResultCode func(Core::System&, u64, u64, u32, s64)> 229template <ResultCode func(Core::System&, u64, u64, u32, s64)>
222void SvcWrap(Core::System& system) { 230void SvcWrap64(Core::System& system) {
223 FuncReturn(system, func(system, Param(system, 0), Param(system, 1), 231 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
224 static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) 232 static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
225 .raw); 233 .raw);
226} 234}
227 235
228template <ResultCode func(Core::System&, u64*, u64, u64, u64)> 236template <ResultCode func(Core::System&, u64*, u64, u64, u64)>
229void SvcWrap(Core::System& system) { 237void SvcWrap64(Core::System& system) {
230 u64 param_1 = 0; 238 u64 param_1 = 0;
231 const u32 retval = 239 const u32 retval =
232 func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw; 240 func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw;
@@ -236,7 +244,7 @@ void SvcWrap(Core::System& system) {
236} 244}
237 245
238template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)> 246template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
239void SvcWrap(Core::System& system) { 247void SvcWrap64(Core::System& system) {
240 u32 param_1 = 0; 248 u32 param_1 = 0;
241 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3), 249 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
242 static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5))) 250 static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
@@ -247,7 +255,7 @@ void SvcWrap(Core::System& system) {
247} 255}
248 256
249template <ResultCode func(Core::System&, u32*, u64, u64, u32)> 257template <ResultCode func(Core::System&, u32*, u64, u64, u32)>
250void SvcWrap(Core::System& system) { 258void SvcWrap64(Core::System& system) {
251 u32 param_1 = 0; 259 u32 param_1 = 0;
252 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), 260 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
253 static_cast<u32>(Param(system, 3))) 261 static_cast<u32>(Param(system, 3)))
@@ -258,7 +266,7 @@ void SvcWrap(Core::System& system) {
258} 266}
259 267
260template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> 268template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
261void SvcWrap(Core::System& system) { 269void SvcWrap64(Core::System& system) {
262 u32 param_1 = 0; 270 u32 param_1 = 0;
263 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)), 271 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
264 static_cast<u32>(Param(system, 3))) 272 static_cast<u32>(Param(system, 3)))
@@ -269,14 +277,14 @@ void SvcWrap(Core::System& system) {
269} 277}
270 278
271template <ResultCode func(Core::System&, u64, u32, s32, s64)> 279template <ResultCode func(Core::System&, u64, u32, s32, s64)>
272void SvcWrap(Core::System& system) { 280void SvcWrap64(Core::System& system) {
273 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), 281 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
274 static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) 282 static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
275 .raw); 283 .raw);
276} 284}
277 285
278template <ResultCode func(Core::System&, u64, u32, s32, s32)> 286template <ResultCode func(Core::System&, u64, u32, s32, s32)>
279void SvcWrap(Core::System& system) { 287void SvcWrap64(Core::System& system) {
280 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), 288 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
281 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) 289 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
282 .raw); 290 .raw);
@@ -286,7 +294,7 @@ void SvcWrap(Core::System& system) {
286// Function wrappers that return type u32 294// Function wrappers that return type u32
287 295
288template <u32 func(Core::System&)> 296template <u32 func(Core::System&)>
289void SvcWrap(Core::System& system) { 297void SvcWrap64(Core::System& system) {
290 FuncReturn(system, func(system)); 298 FuncReturn(system, func(system));
291} 299}
292 300
@@ -294,7 +302,7 @@ void SvcWrap(Core::System& system) {
294// Function wrappers that return type u64 302// Function wrappers that return type u64
295 303
296template <u64 func(Core::System&)> 304template <u64 func(Core::System&)>
297void SvcWrap(Core::System& system) { 305void SvcWrap64(Core::System& system) {
298 FuncReturn(system, func(system)); 306 FuncReturn(system, func(system));
299} 307}
300 308
@@ -302,44 +310,110 @@ void SvcWrap(Core::System& system) {
302/// Function wrappers that return type void 310/// Function wrappers that return type void
303 311
304template <void func(Core::System&)> 312template <void func(Core::System&)>
305void SvcWrap(Core::System& system) { 313void SvcWrap64(Core::System& system) {
306 func(system); 314 func(system);
307} 315}
308 316
309template <void func(Core::System&, u32)> 317template <void func(Core::System&, u32)>
310void SvcWrap(Core::System& system) { 318void SvcWrap64(Core::System& system) {
311 func(system, static_cast<u32>(Param(system, 0))); 319 func(system, static_cast<u32>(Param(system, 0)));
312} 320}
313 321
314template <void func(Core::System&, u32, u64, u64, u64)> 322template <void func(Core::System&, u32, u64, u64, u64)>
315void SvcWrap(Core::System& system) { 323void SvcWrap64(Core::System& system) {
316 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2), 324 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2),
317 Param(system, 3)); 325 Param(system, 3));
318} 326}
319 327
320template <void func(Core::System&, s64)> 328template <void func(Core::System&, s64)>
321void SvcWrap(Core::System& system) { 329void SvcWrap64(Core::System& system) {
322 func(system, static_cast<s64>(Param(system, 0))); 330 func(system, static_cast<s64>(Param(system, 0)));
323} 331}
324 332
325template <void func(Core::System&, u64, s32)> 333template <void func(Core::System&, u64, s32)>
326void SvcWrap(Core::System& system) { 334void SvcWrap64(Core::System& system) {
327 func(system, Param(system, 0), static_cast<s32>(Param(system, 1))); 335 func(system, Param(system, 0), static_cast<s32>(Param(system, 1)));
328} 336}
329 337
330template <void func(Core::System&, u64, u64)> 338template <void func(Core::System&, u64, u64)>
331void SvcWrap(Core::System& system) { 339void SvcWrap64(Core::System& system) {
332 func(system, Param(system, 0), Param(system, 1)); 340 func(system, Param(system, 0), Param(system, 1));
333} 341}
334 342
335template <void func(Core::System&, u64, u64, u64)> 343template <void func(Core::System&, u64, u64, u64)>
336void SvcWrap(Core::System& system) { 344void SvcWrap64(Core::System& system) {
337 func(system, Param(system, 0), Param(system, 1), Param(system, 2)); 345 func(system, Param(system, 0), Param(system, 1), Param(system, 2));
338} 346}
339 347
340template <void func(Core::System&, u32, u64, u64)> 348template <void func(Core::System&, u32, u64, u64)>
341void SvcWrap(Core::System& system) { 349void SvcWrap64(Core::System& system) {
342 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)); 350 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
343} 351}
344 352
353// Used by QueryMemory32
354template <ResultCode func(Core::System&, u32, u32, u32)>
355void SvcWrap32(Core::System& system) {
356 FuncReturn32(system,
357 func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw);
358}
359
360// Used by GetInfo32
361template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
362void SvcWrap32(Core::System& system) {
363 u32 param_1 = 0;
364 u32 param_2 = 0;
365
366 const u32 retval = func(system, &param_1, &param_2, Param32(system, 0), Param32(system, 1),
367 Param32(system, 2), Param32(system, 3))
368 .raw;
369
370 system.CurrentArmInterface().SetReg(1, param_1);
371 system.CurrentArmInterface().SetReg(2, param_2);
372 FuncReturn(system, retval);
373}
374
375// Used by GetThreadPriority32, ConnectToNamedPort32
376template <ResultCode func(Core::System&, u32*, u32)>
377void SvcWrap32(Core::System& system) {
378 u32 param_1 = 0;
379 const u32 retval = func(system, &param_1, Param32(system, 1)).raw;
380 system.CurrentArmInterface().SetReg(1, param_1);
381 FuncReturn(system, retval);
382}
383
384// Used by GetThreadId32
385template <ResultCode func(Core::System&, u32*, u32*, u32)>
386void SvcWrap32(Core::System& system) {
387 u32 param_1 = 0;
388 u32 param_2 = 0;
389
390 const u32 retval = func(system, &param_1, &param_2, Param32(system, 1)).raw;
391 system.CurrentArmInterface().SetReg(1, param_1);
392 system.CurrentArmInterface().SetReg(2, param_2);
393 FuncReturn(system, retval);
394}
395
396// Used by SignalProcessWideKey32
397template <void func(Core::System&, u32, s32)>
398void SvcWrap32(Core::System& system) {
399 func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)));
400}
401
402// Used by SendSyncRequest32
403template <ResultCode func(Core::System&, u32)>
404void SvcWrap32(Core::System& system) {
405 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
406}
407
408// Used by WaitSynchronization32
409template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)>
410void SvcWrap32(Core::System& system) {
411 u32 param_1 = 0;
412 const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),
413 Param32(system, 3), &param_1)
414 .raw;
415 system.CurrentArmInterface().SetReg(1, param_1);
416 FuncReturn(system, retval);
417}
418
345} // namespace Kernel 419} // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index bf850e0b2..83e956036 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -133,15 +133,16 @@ void Thread::CancelWait() {
133 ResumeFromWait(); 133 ResumeFromWait();
134} 134}
135 135
136/** 136static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top,
137 * Resets a thread context, making it ready to be scheduled and run by the CPU 137 u32 entry_point, u32 arg) {
138 * @param context Thread context to reset 138 context = {};
139 * @param stack_top Address of the top of the stack 139 context.cpu_registers[0] = arg;
140 * @param entry_point Address of entry point for execution 140 context.cpu_registers[15] = entry_point;
141 * @param arg User argument for thread 141 context.cpu_registers[13] = stack_top;
142 */ 142}
143static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top, 143
144 VAddr entry_point, u64 arg) { 144static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top,
145 VAddr entry_point, u64 arg) {
145 context = {}; 146 context = {};
146 context.cpu_registers[0] = arg; 147 context.cpu_registers[0] = arg;
147 context.pc = entry_point; 148 context.pc = entry_point;
@@ -198,9 +199,9 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
198 199
199 thread->owner_process->RegisterThread(thread.get()); 200 thread->owner_process->RegisterThread(thread.get());
200 201
201 // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used 202 ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
202 // to initialize the context 203 static_cast<u32>(entry_point), static_cast<u32>(arg));
203 ResetThreadContext(thread->context, stack_top, entry_point, arg); 204 ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
204 205
205 return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); 206 return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
206} 207}
@@ -213,11 +214,13 @@ void Thread::SetPriority(u32 priority) {
213} 214}
214 215
215void Thread::SetWaitSynchronizationResult(ResultCode result) { 216void Thread::SetWaitSynchronizationResult(ResultCode result) {
216 context.cpu_registers[0] = result.raw; 217 context_32.cpu_registers[0] = result.raw;
218 context_64.cpu_registers[0] = result.raw;
217} 219}
218 220
219void Thread::SetWaitSynchronizationOutput(s32 output) { 221void Thread::SetWaitSynchronizationOutput(s32 output) {
220 context.cpu_registers[1] = output; 222 context_32.cpu_registers[1] = output;
223 context_64.cpu_registers[1] = output;
221} 224}
222 225
223s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { 226s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 129e7858a..23fdef8a4 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -102,7 +102,8 @@ public:
102 102
103 using MutexWaitingThreads = std::vector<std::shared_ptr<Thread>>; 103 using MutexWaitingThreads = std::vector<std::shared_ptr<Thread>>;
104 104
105 using ThreadContext = Core::ARM_Interface::ThreadContext; 105 using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
106 using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
106 107
107 using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; 108 using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>;
108 109
@@ -273,12 +274,20 @@ public:
273 return status == ThreadStatus::WaitSynch; 274 return status == ThreadStatus::WaitSynch;
274 } 275 }
275 276
276 ThreadContext& GetContext() { 277 ThreadContext32& GetContext32() {
277 return context; 278 return context_32;
278 } 279 }
279 280
280 const ThreadContext& GetContext() const { 281 const ThreadContext32& GetContext32() const {
281 return context; 282 return context_32;
283 }
284
285 ThreadContext64& GetContext64() {
286 return context_64;
287 }
288
289 const ThreadContext64& GetContext64() const {
290 return context_64;
282 } 291 }
283 292
284 ThreadStatus GetStatus() const { 293 ThreadStatus GetStatus() const {
@@ -466,7 +475,8 @@ private:
466 void AdjustSchedulingOnPriority(u32 old_priority); 475 void AdjustSchedulingOnPriority(u32 old_priority);
467 void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); 476 void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
468 477
469 Core::ARM_Interface::ThreadContext context{}; 478 ThreadContext32 context_32{};
479 ThreadContext64 context_64{};
470 480
471 u64 thread_id = 0; 481 u64 thread_id = 0;
472 482
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index cc978713b..d1bf13c89 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -607,7 +607,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
607 {40, nullptr, "GetCradleFwVersion"}, 607 {40, nullptr, "GetCradleFwVersion"},
608 {50, nullptr, "IsVrModeEnabled"}, 608 {50, nullptr, "IsVrModeEnabled"},
609 {51, nullptr, "SetVrModeEnabled"}, 609 {51, nullptr, "SetVrModeEnabled"},
610 {52, nullptr, "SwitchLcdBacklight"}, 610 {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
611 {53, nullptr, "BeginVrModeEx"}, 611 {53, nullptr, "BeginVrModeEx"},
612 {54, nullptr, "EndVrModeEx"}, 612 {54, nullptr, "EndVrModeEx"},
613 {55, nullptr, "IsInControllerFirmwareUpdateSection"}, 613 {55, nullptr, "IsInControllerFirmwareUpdateSection"},
@@ -636,7 +636,6 @@ void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
636 636
637 IPC::ResponseBuilder rb{ctx, 3}; 637 IPC::ResponseBuilder rb{ctx, 3};
638 rb.Push(RESULT_SUCCESS); 638 rb.Push(RESULT_SUCCESS);
639
640 rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode 639 rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
641} 640}
642 641
@@ -660,6 +659,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
660 rb.PushEnum<AppletMessageQueue::AppletMessage>(message); 659 rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
661 return; 660 return;
662 } 661 }
662
663 rb.Push(RESULT_SUCCESS); 663 rb.Push(RESULT_SUCCESS);
664 rb.PushEnum<AppletMessageQueue::AppletMessage>(message); 664 rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
665} 665}
@@ -672,6 +672,17 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
672 rb.Push(static_cast<u8>(FocusState::InFocus)); 672 rb.Push(static_cast<u8>(FocusState::InFocus));
673} 673}
674 674
675void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx) {
676 IPC::RequestParser rp{ctx};
677 const auto is_lcd_backlight_off_enabled = rp.Pop<bool>();
678
679 LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}",
680 is_lcd_backlight_off_enabled);
681
682 IPC::ResponseBuilder rb{ctx, 2};
683 rb.Push(RESULT_SUCCESS);
684}
685
675void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { 686void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
676 LOG_DEBUG(Service_AM, "called"); 687 LOG_DEBUG(Service_AM, "called");
677 688
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 0b9a4332d..0843de781 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -182,6 +182,7 @@ private:
182 void GetOperationMode(Kernel::HLERequestContext& ctx); 182 void GetOperationMode(Kernel::HLERequestContext& ctx);
183 void GetPerformanceMode(Kernel::HLERequestContext& ctx); 183 void GetPerformanceMode(Kernel::HLERequestContext& ctx);
184 void GetBootMode(Kernel::HLERequestContext& ctx); 184 void GetBootMode(Kernel::HLERequestContext& ctx);
185 void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx);
185 void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); 186 void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
186 void SetCpuBoostMode(Kernel::HLERequestContext& ctx); 187 void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
187 188
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index d19c3623c..53559e8b1 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -129,12 +129,6 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
129 } 129 }
130 metadata.Print(); 130 metadata.Print();
131 131
132 const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
133 if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
134 arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
135 return {ResultStatus::Error32BitISA, {}};
136 }
137
138 if (process.LoadFromMetadata(metadata).IsError()) { 132 if (process.LoadFromMetadata(metadata).IsError()) {
139 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; 133 return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
140 } 134 }
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index f95eee3b1..85ac81ef7 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -111,7 +111,7 @@ json GetProcessorStateDataAuto(Core::System& system) {
111 const auto& vm_manager{process->VMManager()}; 111 const auto& vm_manager{process->VMManager()};
112 auto& arm{system.CurrentArmInterface()}; 112 auto& arm{system.CurrentArmInterface()};
113 113
114 Core::ARM_Interface::ThreadContext context{}; 114 Core::ARM_Interface::ThreadContext64 context{};
115 arm.SaveContext(context); 115 arm.SaveContext(context);
116 116
117 return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", 117 return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d1fc94060..7c0303684 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -94,6 +94,7 @@ void LogSettings() {
94 LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); 94 LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
95 LogSetting("Renderer_UseAsynchronousGpuEmulation", 95 LogSetting("Renderer_UseAsynchronousGpuEmulation",
96 Settings::values.use_asynchronous_gpu_emulation); 96 Settings::values.use_asynchronous_gpu_emulation);
97 LogSetting("Renderer_UseVsync", Settings::values.use_vsync);
97 LogSetting("Audio_OutputEngine", Settings::values.sink_id); 98 LogSetting("Audio_OutputEngine", Settings::values.sink_id);
98 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 99 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
99 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); 100 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
diff --git a/src/core/settings.h b/src/core/settings.h
index f837d3fbc..15b691342 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -435,6 +435,7 @@ struct Values {
435 bool use_disk_shader_cache; 435 bool use_disk_shader_cache;
436 bool use_accurate_gpu_emulation; 436 bool use_accurate_gpu_emulation;
437 bool use_asynchronous_gpu_emulation; 437 bool use_asynchronous_gpu_emulation;
438 bool use_vsync;
438 bool force_30fps_mode; 439 bool force_30fps_mode;
439 440
440 float bg_red; 441 float bg_red;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 0e72d31cd..0f3685d1c 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -188,6 +188,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
188 Settings::values.use_accurate_gpu_emulation); 188 Settings::values.use_accurate_gpu_emulation);
189 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation", 189 AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
190 Settings::values.use_asynchronous_gpu_emulation); 190 Settings::values.use_asynchronous_gpu_emulation);
191 AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync);
191 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); 192 AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
192} 193}
193 194
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 2228571a6..e82ae7ef1 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -32,8 +32,16 @@ public:
32 SocketCallback callback) 32 SocketCallback callback)
33 : callback(std::move(callback)), timer(io_service), 33 : callback(std::move(callback)), timer(io_service),
34 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id), 34 socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id),
35 pad_index(pad_index), 35 pad_index(pad_index) {
36 send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {} 36 boost::system::error_code ec{};
37 auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
38 if (ec.failed()) {
39 LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host);
40 ipv4 = boost::asio::ip::address_v4{};
41 }
42
43 send_endpoint = {udp::endpoint(ipv4, port)};
44 }
37 45
38 void Stop() { 46 void Stop() {
39 io_service.stop(); 47 io_service.stop();
@@ -85,17 +93,18 @@ private:
85 } 93 }
86 94
87 void HandleSend(const boost::system::error_code& error) { 95 void HandleSend(const boost::system::error_code& error) {
96 boost::system::error_code _ignored{};
88 // Send a request for getting port info for the pad 97 // Send a request for getting port info for the pad
89 Request::PortInfo port_info{1, {pad_index, 0, 0, 0}}; 98 Request::PortInfo port_info{1, {pad_index, 0, 0, 0}};
90 const auto port_message = Request::Create(port_info, client_id); 99 const auto port_message = Request::Create(port_info, client_id);
91 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); 100 std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
92 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint); 101 socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
93 102
94 // Send a request for getting pad data for the pad 103 // Send a request for getting pad data for the pad
95 Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS}; 104 Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS};
96 const auto pad_message = Request::Create(pad_data, client_id); 105 const auto pad_message = Request::Create(pad_data, client_id);
97 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE); 106 std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
98 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint); 107 socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
99 StartSend(timer.expiry()); 108 StartSend(timer.expiry());
100 } 109 }
101 110
diff --git a/src/input_common/udp/protocol.cpp b/src/input_common/udp/protocol.cpp
index a982ac49d..5e50bd612 100644
--- a/src/input_common/udp/protocol.cpp
+++ b/src/input_common/udp/protocol.cpp
@@ -31,7 +31,6 @@ namespace Response {
31 */ 31 */
32std::optional<Type> Validate(u8* data, std::size_t size) { 32std::optional<Type> Validate(u8* data, std::size_t size) {
33 if (size < sizeof(Header)) { 33 if (size < sizeof(Header)) {
34 LOG_DEBUG(Input, "Invalid UDP packet received");
35 return std::nullopt; 34 return std::nullopt;
36 } 35 }
37 Header header{}; 36 Header header{};
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 2cdf1aa7f..b1088af3d 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -5,7 +5,7 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/scope_acquire_window_context.h" 8#include "core/frontend/scope_acquire_context.h"
9#include "video_core/dma_pusher.h" 9#include "video_core/dma_pusher.h"
10#include "video_core/gpu.h" 10#include "video_core/gpu.h"
11#include "video_core/gpu_thread.h" 11#include "video_core/gpu_thread.h"
@@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
27 return; 27 return;
28 } 28 }
29 29
30 Core::Frontend::ScopeAcquireWindowContext acquire_context{renderer.GetRenderWindow()}; 30 Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()};
31 31
32 CommandDataContainer next; 32 CommandDataContainer next;
33 while (state.is_running) { 33 while (state.is_running) {
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index af1bebc4f..5ec99a126 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -35,15 +35,19 @@ public:
35 explicit RendererBase(Core::Frontend::EmuWindow& window); 35 explicit RendererBase(Core::Frontend::EmuWindow& window);
36 virtual ~RendererBase(); 36 virtual ~RendererBase();
37 37
38 /// Swap buffers (render frame)
39 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
40
41 /// Initialize the renderer 38 /// Initialize the renderer
42 virtual bool Init() = 0; 39 virtual bool Init() = 0;
43 40
44 /// Shutdown the renderer 41 /// Shutdown the renderer
45 virtual void ShutDown() = 0; 42 virtual void ShutDown() = 0;
46 43
44 /// Finalize rendering the guest frame and draw into the presentation texture
45 virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
46
47 /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
48 /// specific implementation)
49 virtual void TryPresent(int timeout_ms) = 0;
50
47 // Getter/setter functions: 51 // Getter/setter functions:
48 // ------------------------ 52 // ------------------------
49 53
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e1965fb21..3fcd319fd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -36,6 +36,7 @@ namespace OpenGL {
36 36
37using Maxwell = Tegra::Engines::Maxwell3D::Regs; 37using Maxwell = Tegra::Engines::Maxwell3D::Regs;
38 38
39using Tegra::Engines::ShaderType;
39using VideoCore::Surface::PixelFormat; 40using VideoCore::Surface::PixelFormat;
40using VideoCore::Surface::SurfaceTarget; 41using VideoCore::Surface::SurfaceTarget;
41using VideoCore::Surface::SurfaceType; 42using VideoCore::Surface::SurfaceType;
@@ -56,8 +57,7 @@ namespace {
56 57
57template <typename Engine, typename Entry> 58template <typename Engine, typename Entry>
58Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, 59Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry,
59 Tegra::Engines::ShaderType shader_type, 60 ShaderType shader_type, std::size_t index = 0) {
60 std::size_t index = 0) {
61 if (entry.IsBindless()) { 61 if (entry.IsBindless()) {
62 const Tegra::Texture::TextureHandle tex_handle = 62 const Tegra::Texture::TextureHandle tex_handle =
63 engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset()); 63 engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset());
@@ -910,15 +910,10 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader&
910 const auto& maxwell3d = system.GPU().Maxwell3D(); 910 const auto& maxwell3d = system.GPU().Maxwell3D();
911 u32 binding = device.GetBaseBindings(stage_index).sampler; 911 u32 binding = device.GetBaseBindings(stage_index).sampler;
912 for (const auto& entry : shader->GetShaderEntries().samplers) { 912 for (const auto& entry : shader->GetShaderEntries().samplers) {
913 const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); 913 const auto shader_type = static_cast<ShaderType>(stage_index);
914 if (!entry.IsIndexed()) { 914 for (std::size_t i = 0; i < entry.Size(); ++i) {
915 const auto texture = GetTextureInfo(maxwell3d, entry, shader_type); 915 const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i);
916 SetupTexture(binding++, texture, entry); 916 SetupTexture(binding++, texture, entry);
917 } else {
918 for (std::size_t i = 0; i < entry.Size(); ++i) {
919 const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i);
920 SetupTexture(binding++, texture, entry);
921 }
922 } 917 }
923 } 918 }
924} 919}
@@ -928,16 +923,9 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
928 const auto& compute = system.GPU().KeplerCompute(); 923 const auto& compute = system.GPU().KeplerCompute();
929 u32 binding = 0; 924 u32 binding = 0;
930 for (const auto& entry : kernel->GetShaderEntries().samplers) { 925 for (const auto& entry : kernel->GetShaderEntries().samplers) {
931 if (!entry.IsIndexed()) { 926 for (std::size_t i = 0; i < entry.Size(); ++i) {
932 const auto texture = 927 const auto texture = GetTextureInfo(compute, entry, ShaderType::Compute, i);
933 GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute);
934 SetupTexture(binding++, texture, entry); 928 SetupTexture(binding++, texture, entry);
935 } else {
936 for (std::size_t i = 0; i < entry.Size(); ++i) {
937 const auto texture =
938 GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute, i);
939 SetupTexture(binding++, texture, entry);
940 }
941 } 929 }
942 } 930 }
943} 931}
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index f0ddfb276..c0aee770f 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -15,6 +15,24 @@ MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_R
15 15
16namespace OpenGL { 16namespace OpenGL {
17 17
18void OGLRenderbuffer::Create() {
19 if (handle != 0)
20 return;
21
22 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
23 glGenRenderbuffers(1, &handle);
24}
25
26void OGLRenderbuffer::Release() {
27 if (handle == 0)
28 return;
29
30 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
31 glDeleteRenderbuffers(1, &handle);
32 OpenGLState::GetCurState().ResetRenderbuffer(handle).Apply();
33 handle = 0;
34}
35
18void OGLTexture::Create(GLenum target) { 36void OGLTexture::Create(GLenum target) {
19 if (handle != 0) 37 if (handle != 0)
20 return; 38 return;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 514d1d165..995a4e45e 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -11,6 +11,31 @@
11 11
12namespace OpenGL { 12namespace OpenGL {
13 13
14class OGLRenderbuffer : private NonCopyable {
15public:
16 OGLRenderbuffer() = default;
17
18 OGLRenderbuffer(OGLRenderbuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
19
20 ~OGLRenderbuffer() {
21 Release();
22 }
23
24 OGLRenderbuffer& operator=(OGLRenderbuffer&& o) noexcept {
25 Release();
26 handle = std::exchange(o.handle, 0);
27 return *this;
28 }
29
30 /// Creates a new internal OpenGL resource and stores the handle
31 void Create();
32
33 /// Deletes the internal OpenGL resource
34 void Release();
35
36 GLuint handle = 0;
37};
38
14class OGLTexture : private NonCopyable { 39class OGLTexture : private NonCopyable {
15public: 40public:
16 OGLTexture() = default; 41 OGLTexture() = default;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index ab1f7983c..7d3bc1a1f 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -423,6 +423,13 @@ void OpenGLState::ApplyClipControl() {
423 } 423 }
424} 424}
425 425
426void OpenGLState::ApplyRenderBuffer() {
427 if (cur_state.renderbuffer != renderbuffer) {
428 cur_state.renderbuffer = renderbuffer;
429 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
430 }
431}
432
426void OpenGLState::ApplyTextures() { 433void OpenGLState::ApplyTextures() {
427 const std::size_t size = std::size(textures); 434 const std::size_t size = std::size(textures);
428 for (std::size_t i = 0; i < size; ++i) { 435 for (std::size_t i = 0; i < size; ++i) {
@@ -478,6 +485,7 @@ void OpenGLState::Apply() {
478 ApplyPolygonOffset(); 485 ApplyPolygonOffset();
479 ApplyAlphaTest(); 486 ApplyAlphaTest();
480 ApplyClipControl(); 487 ApplyClipControl();
488 ApplyRenderBuffer();
481} 489}
482 490
483void OpenGLState::EmulateViewportWithScissor() { 491void OpenGLState::EmulateViewportWithScissor() {
@@ -551,4 +559,11 @@ OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
551 return *this; 559 return *this;
552} 560}
553 561
562OpenGLState& OpenGLState::ResetRenderbuffer(GLuint handle) {
563 if (renderbuffer == handle) {
564 renderbuffer = 0;
565 }
566 return *this;
567}
568
554} // namespace OpenGL 569} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 4953eeda2..bce662f2c 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -158,6 +158,8 @@ public:
158 GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE; 158 GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE;
159 } clip_control; 159 } clip_control;
160 160
161 GLuint renderbuffer{}; // GL_RENDERBUFFER_BINDING
162
161 OpenGLState(); 163 OpenGLState();
162 164
163 /// Get the currently active OpenGL state 165 /// Get the currently active OpenGL state
@@ -196,6 +198,7 @@ public:
196 void ApplyPolygonOffset(); 198 void ApplyPolygonOffset();
197 void ApplyAlphaTest(); 199 void ApplyAlphaTest();
198 void ApplyClipControl(); 200 void ApplyClipControl();
201 void ApplyRenderBuffer();
199 202
200 /// Resets any references to the given resource 203 /// Resets any references to the given resource
201 OpenGLState& UnbindTexture(GLuint handle); 204 OpenGLState& UnbindTexture(GLuint handle);
@@ -204,6 +207,7 @@ public:
204 OpenGLState& ResetPipeline(GLuint handle); 207 OpenGLState& ResetPipeline(GLuint handle);
205 OpenGLState& ResetVertexArray(GLuint handle); 208 OpenGLState& ResetVertexArray(GLuint handle);
206 OpenGLState& ResetFramebuffer(GLuint handle); 209 OpenGLState& ResetFramebuffer(GLuint handle);
210 OpenGLState& ResetRenderbuffer(GLuint handle);
207 211
208 /// Viewport does not affects glClearBuffer so emulate viewport using scissor test 212 /// Viewport does not affects glClearBuffer so emulate viewport using scissor test
209 void EmulateViewportWithScissor(); 213 void EmulateViewportWithScissor();
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 7ed505628..d3dea3659 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -92,8 +92,32 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
92 } 92 }
93 case Maxwell::VertexAttribute::Type::UnsignedScaled: 93 case Maxwell::VertexAttribute::Type::UnsignedScaled:
94 switch (attrib.size) { 94 switch (attrib.size) {
95 case Maxwell::VertexAttribute::Size::Size_8:
95 case Maxwell::VertexAttribute::Size::Size_8_8: 96 case Maxwell::VertexAttribute::Size::Size_8_8:
97 case Maxwell::VertexAttribute::Size::Size_8_8_8:
98 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
96 return GL_UNSIGNED_BYTE; 99 return GL_UNSIGNED_BYTE;
100 case Maxwell::VertexAttribute::Size::Size_16:
101 case Maxwell::VertexAttribute::Size::Size_16_16:
102 case Maxwell::VertexAttribute::Size::Size_16_16_16:
103 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
104 return GL_UNSIGNED_SHORT;
105 default:
106 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
107 return {};
108 }
109 case Maxwell::VertexAttribute::Type::SignedScaled:
110 switch (attrib.size) {
111 case Maxwell::VertexAttribute::Size::Size_8:
112 case Maxwell::VertexAttribute::Size::Size_8_8:
113 case Maxwell::VertexAttribute::Size::Size_8_8_8:
114 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
115 return GL_BYTE;
116 case Maxwell::VertexAttribute::Size::Size_16:
117 case Maxwell::VertexAttribute::Size::Size_16_16:
118 case Maxwell::VertexAttribute::Size::Size_16_16_16:
119 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
120 return GL_SHORT;
97 default: 121 default:
98 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); 122 LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
99 return {}; 123 return {};
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index bba16afaf..a4340b502 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -9,11 +9,11 @@
9#include <glad/glad.h> 9#include <glad/glad.h>
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/microprofile.h"
12#include "common/telemetry.h" 13#include "common/telemetry.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/core_timing.h" 15#include "core/core_timing.h"
15#include "core/frontend/emu_window.h" 16#include "core/frontend/emu_window.h"
16#include "core/frontend/scope_acquire_window_context.h"
17#include "core/memory.h" 17#include "core/memory.h"
18#include "core/perf_stats.h" 18#include "core/perf_stats.h"
19#include "core/settings.h" 19#include "core/settings.h"
@@ -24,6 +24,144 @@
24 24
25namespace OpenGL { 25namespace OpenGL {
26 26
27// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
28// to wait on available presentation frames.
29constexpr std::size_t SWAP_CHAIN_SIZE = 3;
30
31struct Frame {
32 u32 width{}; /// Width of the frame (to detect resize)
33 u32 height{}; /// Height of the frame
34 bool color_reloaded{}; /// Texture attachment was recreated (ie: resized)
35 OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO
36 OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread
37 OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
38 GLsync render_fence{}; /// Fence created on the render thread
39 GLsync present_fence{}; /// Fence created on the presentation thread
40 bool is_srgb{}; /// Framebuffer is sRGB or RGB
41};
42
43/**
44 * For smooth Vsync rendering, we want to always present the latest frame that the core generates,
45 * but also make sure that rendering happens at the pace that the frontend dictates. This is a
46 * helper class that the renderer uses to sync frames between the render thread and the presentation
47 * thread
48 */
49class FrameMailbox {
50public:
51 std::mutex swap_chain_lock;
52 std::condition_variable present_cv;
53 std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
54 std::queue<Frame*> free_queue;
55 std::deque<Frame*> present_queue;
56 Frame* previous_frame{};
57
58 FrameMailbox() {
59 for (auto& frame : swap_chain) {
60 free_queue.push(&frame);
61 }
62 }
63
64 ~FrameMailbox() {
65 // lock the mutex and clear out the present and free_queues and notify any people who are
66 // blocked to prevent deadlock on shutdown
67 std::scoped_lock lock{swap_chain_lock};
68 std::queue<Frame*>().swap(free_queue);
69 present_queue.clear();
70 present_cv.notify_all();
71 }
72
73 void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
74 frame->present.Release();
75 frame->present.Create();
76 GLint previous_draw_fbo{};
77 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
78 glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
79 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
80 frame->color.handle);
81 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
82 LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
83 }
84 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
85 frame->color_reloaded = false;
86 }
87
88 void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
89 OpenGLState prev_state = OpenGLState::GetCurState();
90 OpenGLState state = OpenGLState::GetCurState();
91
92 // Recreate the color texture attachment
93 frame->color.Release();
94 frame->color.Create();
95 state.renderbuffer = frame->color.handle;
96 state.Apply();
97 glRenderbufferStorage(GL_RENDERBUFFER, frame->is_srgb ? GL_SRGB8 : GL_RGB8, width, height);
98
99 // Recreate the FBO for the render target
100 frame->render.Release();
101 frame->render.Create();
102 state.draw.read_framebuffer = frame->render.handle;
103 state.draw.draw_framebuffer = frame->render.handle;
104 state.Apply();
105 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
106 frame->color.handle);
107 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
108 LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
109 }
110 prev_state.Apply();
111 frame->width = width;
112 frame->height = height;
113 frame->color_reloaded = true;
114 }
115
116 Frame* GetRenderFrame() {
117 std::unique_lock lock{swap_chain_lock};
118
119 // If theres no free frames, we will reuse the oldest render frame
120 if (free_queue.empty()) {
121 auto frame = present_queue.back();
122 present_queue.pop_back();
123 return frame;
124 }
125
126 Frame* frame = free_queue.front();
127 free_queue.pop();
128 return frame;
129 }
130
131 void ReleaseRenderFrame(Frame* frame) {
132 std::unique_lock lock{swap_chain_lock};
133 present_queue.push_front(frame);
134 present_cv.notify_one();
135 }
136
137 Frame* TryGetPresentFrame(int timeout_ms) {
138 std::unique_lock lock{swap_chain_lock};
139 // wait for new entries in the present_queue
140 present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
141 [&] { return !present_queue.empty(); });
142 if (present_queue.empty()) {
143 // timed out waiting for a frame to draw so return the previous frame
144 return previous_frame;
145 }
146
147 // free the previous frame and add it back to the free queue
148 if (previous_frame) {
149 free_queue.push(previous_frame);
150 }
151
152 // the newest entries are pushed to the front of the queue
153 Frame* frame = present_queue.front();
154 present_queue.pop_front();
155 // remove all old entries from the present queue and move them back to the free_queue
156 for (auto f : present_queue) {
157 free_queue.push(f);
158 }
159 present_queue.clear();
160 previous_frame = frame;
161 return frame;
162 }
163};
164
27namespace { 165namespace {
28 166
29constexpr char vertex_shader[] = R"( 167constexpr char vertex_shader[] = R"(
@@ -158,21 +296,91 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
158} // Anonymous namespace 296} // Anonymous namespace
159 297
160RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) 298RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
161 : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system} {} 299 : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
300 frame_mailbox{std::make_unique<FrameMailbox>()} {}
162 301
163RendererOpenGL::~RendererOpenGL() = default; 302RendererOpenGL::~RendererOpenGL() = default;
164 303
304MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
305MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
306
165void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 307void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
308 render_window.PollEvents();
309
310 if (!framebuffer) {
311 return;
312 }
313
166 // Maintain the rasterizer's state as a priority 314 // Maintain the rasterizer's state as a priority
167 OpenGLState prev_state = OpenGLState::GetCurState(); 315 OpenGLState prev_state = OpenGLState::GetCurState();
168 state.AllDirty(); 316 state.AllDirty();
169 state.Apply(); 317 state.Apply();
170 318
319 PrepareRendertarget(framebuffer);
320 RenderScreenshot();
321
322 Frame* frame;
323 {
324 MICROPROFILE_SCOPE(OpenGL_WaitPresent);
325
326 frame = frame_mailbox->GetRenderFrame();
327
328 // Clean up sync objects before drawing
329
330 // INTEL driver workaround. We can't delete the previous render sync object until we are
331 // sure that the presentation is done
332 if (frame->present_fence) {
333 glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
334 }
335
336 // delete the draw fence if the frame wasn't presented
337 if (frame->render_fence) {
338 glDeleteSync(frame->render_fence);
339 frame->render_fence = 0;
340 }
341
342 // wait for the presentation to be done
343 if (frame->present_fence) {
344 glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
345 glDeleteSync(frame->present_fence);
346 frame->present_fence = 0;
347 }
348 }
349
350 {
351 MICROPROFILE_SCOPE(OpenGL_RenderFrame);
352 const auto& layout = render_window.GetFramebufferLayout();
353
354 // Recreate the frame if the size of the window has changed
355 if (layout.width != frame->width || layout.height != frame->height ||
356 screen_info.display_srgb != frame->is_srgb) {
357 LOG_DEBUG(Render_OpenGL, "Reloading render frame");
358 frame->is_srgb = screen_info.display_srgb;
359 frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
360 }
361 state.draw.draw_framebuffer = frame->render.handle;
362 state.Apply();
363 DrawScreen(layout);
364 // Create a fence for the frontend to wait on and swap this frame to OffTex
365 frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
366 glFlush();
367 frame_mailbox->ReleaseRenderFrame(frame);
368 m_current_frame++;
369 rasterizer->TickFrame();
370 }
371
372 // Restore the rasterizer state
373 prev_state.AllDirty();
374 prev_state.Apply();
375}
376
377void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
171 if (framebuffer) { 378 if (framebuffer) {
172 // If framebuffer is provided, reload it from memory to a texture 379 // If framebuffer is provided, reload it from memory to a texture
173 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || 380 if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
174 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || 381 screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
175 screen_info.texture.pixel_format != framebuffer->pixel_format) { 382 screen_info.texture.pixel_format != framebuffer->pixel_format ||
383 gl_framebuffer_data.empty()) {
176 // Reallocate texture if the framebuffer size has changed. 384 // Reallocate texture if the framebuffer size has changed.
177 // This is expected to not happen very often and hence should not be a 385 // This is expected to not happen very often and hence should not be a
178 // performance problem. 386 // performance problem.
@@ -181,22 +389,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
181 389
182 // Load the framebuffer from memory, draw it to the screen, and swap buffers 390 // Load the framebuffer from memory, draw it to the screen, and swap buffers
183 LoadFBToScreenInfo(*framebuffer); 391 LoadFBToScreenInfo(*framebuffer);
184
185 if (renderer_settings.screenshot_requested)
186 CaptureScreenshot();
187
188 DrawScreen(render_window.GetFramebufferLayout());
189
190 rasterizer->TickFrame();
191
192 render_window.SwapBuffers();
193 } 392 }
194
195 render_window.PollEvents();
196
197 // Restore the rasterizer state
198 prev_state.AllDirty();
199 prev_state.Apply();
200} 393}
201 394
202void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { 395void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
@@ -418,13 +611,48 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
418 DrawScreenTriangles(screen_info, static_cast<float>(screen.left), 611 DrawScreenTriangles(screen_info, static_cast<float>(screen.left),
419 static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()), 612 static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()),
420 static_cast<float>(screen.GetHeight())); 613 static_cast<float>(screen.GetHeight()));
614}
421 615
422 m_current_frame++; 616void RendererOpenGL::TryPresent(int timeout_ms) {
617 const auto& layout = render_window.GetFramebufferLayout();
618 auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
619 if (!frame) {
620 LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
621 return;
622 }
623
624 // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
625 // readback since we won't be doing any blending
626 glClear(GL_COLOR_BUFFER_BIT);
627
628 // Recreate the presentation FBO if the color attachment was changed
629 if (frame->color_reloaded) {
630 LOG_DEBUG(Render_OpenGL, "Reloading present frame");
631 frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
632 }
633 glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
634 // INTEL workaround.
635 // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
636 // it on the emulation thread without too much penalty
637 // glDeleteSync(frame.render_sync);
638 // frame.render_sync = 0;
639
640 glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
641 glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
642 GL_COLOR_BUFFER_BIT, GL_LINEAR);
643
644 // Insert fence for the main thread to block on
645 frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
646 glFlush();
647
648 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
423} 649}
424 650
425void RendererOpenGL::UpdateFramerate() {} 651void RendererOpenGL::RenderScreenshot() {
652 if (!renderer_settings.screenshot_requested) {
653 return;
654 }
426 655
427void RendererOpenGL::CaptureScreenshot() {
428 // Draw the current frame to the screenshot framebuffer 656 // Draw the current frame to the screenshot framebuffer
429 screenshot_framebuffer.Create(); 657 screenshot_framebuffer.Create();
430 GLuint old_read_fb = state.draw.read_framebuffer; 658 GLuint old_read_fb = state.draw.read_framebuffer;
@@ -459,8 +687,6 @@ void RendererOpenGL::CaptureScreenshot() {
459} 687}
460 688
461bool RendererOpenGL::Init() { 689bool RendererOpenGL::Init() {
462 Core::Frontend::ScopeAcquireWindowContext acquire_context{render_window};
463
464 if (GLAD_GL_KHR_debug) { 690 if (GLAD_GL_KHR_debug) {
465 glEnable(GL_DEBUG_OUTPUT); 691 glEnable(GL_DEBUG_OUTPUT);
466 glDebugMessageCallback(DebugHandler, nullptr); 692 glDebugMessageCallback(DebugHandler, nullptr);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index b56328a7f..d45e69cbc 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -44,19 +44,23 @@ struct ScreenInfo {
44 TextureInfo texture; 44 TextureInfo texture;
45}; 45};
46 46
47struct PresentationTexture {
48 u32 width = 0;
49 u32 height = 0;
50 OGLTexture texture;
51};
52
53class FrameMailbox;
54
47class RendererOpenGL final : public VideoCore::RendererBase { 55class RendererOpenGL final : public VideoCore::RendererBase {
48public: 56public:
49 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); 57 explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
50 ~RendererOpenGL() override; 58 ~RendererOpenGL() override;
51 59
52 /// Swap buffers (render frame)
53 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
54
55 /// Initialize the renderer
56 bool Init() override; 60 bool Init() override;
57
58 /// Shutdown the renderer
59 void ShutDown() override; 61 void ShutDown() override;
62 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
63 void TryPresent(int timeout_ms) override;
60 64
61private: 65private:
62 /// Initializes the OpenGL state and creates persistent objects. 66 /// Initializes the OpenGL state and creates persistent objects.
@@ -74,10 +78,7 @@ private:
74 78
75 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h); 79 void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
76 80
77 /// Updates the framerate. 81 void RenderScreenshot();
78 void UpdateFramerate();
79
80 void CaptureScreenshot();
81 82
82 /// Loads framebuffer from emulated memory into the active OpenGL texture. 83 /// Loads framebuffer from emulated memory into the active OpenGL texture.
83 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); 84 void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer);
@@ -87,6 +88,8 @@ private:
87 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, 88 void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
88 const TextureInfo& texture); 89 const TextureInfo& texture);
89 90
91 void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
92
90 Core::Frontend::EmuWindow& emu_window; 93 Core::Frontend::EmuWindow& emu_window;
91 Core::System& system; 94 Core::System& system;
92 95
@@ -107,6 +110,9 @@ private:
107 /// Used for transforming the framebuffer orientation 110 /// Used for transforming the framebuffer orientation
108 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags; 111 Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags;
109 Common::Rectangle<int> framebuffer_crop_rect; 112 Common::Rectangle<int> framebuffer_crop_rect;
113
114 /// Frame presentation mailbox
115 std::unique_ptr<FrameMailbox> frame_mailbox;
110}; 116};
111 117
112} // namespace OpenGL 118} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index ef66dd141..aad0c895b 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -371,8 +371,22 @@ vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttr
371 } 371 }
372 case Maxwell::VertexAttribute::Type::UnsignedScaled: 372 case Maxwell::VertexAttribute::Type::UnsignedScaled:
373 switch (size) { 373 switch (size) {
374 case Maxwell::VertexAttribute::Size::Size_8:
375 return vk::Format::eR8Uscaled;
374 case Maxwell::VertexAttribute::Size::Size_8_8: 376 case Maxwell::VertexAttribute::Size::Size_8_8:
375 return vk::Format::eR8G8Uscaled; 377 return vk::Format::eR8G8Uscaled;
378 case Maxwell::VertexAttribute::Size::Size_8_8_8:
379 return vk::Format::eR8G8B8Uscaled;
380 case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
381 return vk::Format::eR8G8B8A8Uscaled;
382 case Maxwell::VertexAttribute::Size::Size_16:
383 return vk::Format::eR16Uscaled;
384 case Maxwell::VertexAttribute::Size::Size_16_16:
385 return vk::Format::eR16G16Uscaled;
386 case Maxwell::VertexAttribute::Size::Size_16_16_16:
387 return vk::Format::eR16G16B16Uscaled;
388 case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
389 return vk::Format::eR16G16B16A16Uscaled;
376 default: 390 default:
377 break; 391 break;
378 } 392 }
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index d5032b432..ddc62bc97 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -106,8 +106,14 @@ RendererVulkan::~RendererVulkan() {
106} 106}
107 107
108void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { 108void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
109 render_window.PollEvents();
110
111 if (!framebuffer) {
112 return;
113 }
114
109 const auto& layout = render_window.GetFramebufferLayout(); 115 const auto& layout = render_window.GetFramebufferLayout();
110 if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) { 116 if (layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
111 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; 117 const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
112 const bool use_accelerated = 118 const bool use_accelerated =
113 rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 119 rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
@@ -128,13 +134,16 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
128 blit_screen->Recreate(); 134 blit_screen->Recreate();
129 } 135 }
130 136
131 render_window.SwapBuffers();
132 rasterizer->TickFrame(); 137 rasterizer->TickFrame();
133 } 138 }
134 139
135 render_window.PollEvents(); 140 render_window.PollEvents();
136} 141}
137 142
143void RendererVulkan::TryPresent(int /*timeout_ms*/) {
144 // TODO (bunnei): ImplementMe
145}
146
138bool RendererVulkan::Init() { 147bool RendererVulkan::Init() {
139 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; 148 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
140 render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); 149 render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
@@ -262,4 +271,4 @@ void RendererVulkan::Report() const {
262 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); 271 telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
263} 272}
264 273
265} // namespace Vulkan \ No newline at end of file 274} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index a472c5dc9..f513397f0 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -36,14 +36,10 @@ public:
36 explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system); 36 explicit RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system);
37 ~RendererVulkan() override; 37 ~RendererVulkan() override;
38 38
39 /// Swap buffers (render frame)
40 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
41
42 /// Initialize the renderer
43 bool Init() override; 39 bool Init() override;
44
45 /// Shutdown the renderer
46 void ShutDown() override; 40 void ShutDown() override;
41 void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
42 void TryPresent(int timeout_ms) override;
47 43
48private: 44private:
49 std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( 45 std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 9d5b8de7a..60f57d83e 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -73,7 +73,7 @@ UniqueDescriptorUpdateTemplate VKComputePipeline::CreateDescriptorUpdateTemplate
73 std::vector<vk::DescriptorUpdateTemplateEntry> template_entries; 73 std::vector<vk::DescriptorUpdateTemplateEntry> template_entries;
74 u32 binding = 0; 74 u32 binding = 0;
75 u32 offset = 0; 75 u32 offset = 0;
76 FillDescriptorUpdateTemplateEntries(device, entries, binding, offset, template_entries); 76 FillDescriptorUpdateTemplateEntries(entries, binding, offset, template_entries);
77 if (template_entries.empty()) { 77 if (template_entries.empty()) {
78 // If the shader doesn't use descriptor sets, skip template creation. 78 // If the shader doesn't use descriptor sets, skip template creation.
79 return UniqueDescriptorUpdateTemplate{}; 79 return UniqueDescriptorUpdateTemplate{};
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index b155dfb49..6a02403c1 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -97,8 +97,7 @@ UniqueDescriptorUpdateTemplate VKGraphicsPipeline::CreateDescriptorUpdateTemplat
97 u32 offset = 0; 97 u32 offset = 0;
98 for (const auto& stage : program) { 98 for (const auto& stage : program) {
99 if (stage) { 99 if (stage) {
100 FillDescriptorUpdateTemplateEntries(device, stage->entries, binding, offset, 100 FillDescriptorUpdateTemplateEntries(stage->entries, binding, offset, template_entries);
101 template_entries);
102 } 101 }
103 } 102 }
104 if (template_entries.empty()) { 103 if (template_entries.empty()) {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 7ddf7d3ee..696e4b291 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -36,6 +36,13 @@ using Tegra::Engines::ShaderType;
36 36
37namespace { 37namespace {
38 38
39// C++20's using enum
40constexpr auto eUniformBuffer = vk::DescriptorType::eUniformBuffer;
41constexpr auto eStorageBuffer = vk::DescriptorType::eStorageBuffer;
42constexpr auto eUniformTexelBuffer = vk::DescriptorType::eUniformTexelBuffer;
43constexpr auto eCombinedImageSampler = vk::DescriptorType::eCombinedImageSampler;
44constexpr auto eStorageImage = vk::DescriptorType::eStorageImage;
45
39constexpr VideoCommon::Shader::CompilerSettings compiler_settings{ 46constexpr VideoCommon::Shader::CompilerSettings compiler_settings{
40 VideoCommon::Shader::CompileDepth::FullDecompile}; 47 VideoCommon::Shader::CompileDepth::FullDecompile};
41 48
@@ -119,23 +126,32 @@ ShaderType GetShaderType(Maxwell::ShaderProgram program) {
119 } 126 }
120} 127}
121 128
129template <vk::DescriptorType descriptor_type, class Container>
130void AddBindings(std::vector<vk::DescriptorSetLayoutBinding>& bindings, u32& binding,
131 vk::ShaderStageFlags stage_flags, const Container& container) {
132 const u32 num_entries = static_cast<u32>(std::size(container));
133 for (std::size_t i = 0; i < num_entries; ++i) {
134 u32 count = 1;
135 if constexpr (descriptor_type == eCombinedImageSampler) {
136 // Combined image samplers can be arrayed.
137 count = container[i].Size();
138 }
139 bindings.emplace_back(binding++, descriptor_type, count, stage_flags, nullptr);
140 }
141}
142
122u32 FillDescriptorLayout(const ShaderEntries& entries, 143u32 FillDescriptorLayout(const ShaderEntries& entries,
123 std::vector<vk::DescriptorSetLayoutBinding>& bindings, 144 std::vector<vk::DescriptorSetLayoutBinding>& bindings,
124 Maxwell::ShaderProgram program_type, u32 base_binding) { 145 Maxwell::ShaderProgram program_type, u32 base_binding) {
125 const ShaderType stage = GetStageFromProgram(program_type); 146 const ShaderType stage = GetStageFromProgram(program_type);
126 const vk::ShaderStageFlags stage_flags = MaxwellToVK::ShaderStage(stage); 147 const vk::ShaderStageFlags flags = MaxwellToVK::ShaderStage(stage);
127 148
128 u32 binding = base_binding; 149 u32 binding = base_binding;
129 const auto AddBindings = [&](vk::DescriptorType descriptor_type, std::size_t num_entries) { 150 AddBindings<eUniformBuffer>(bindings, binding, flags, entries.const_buffers);
130 for (std::size_t i = 0; i < num_entries; ++i) { 151 AddBindings<eStorageBuffer>(bindings, binding, flags, entries.global_buffers);
131 bindings.emplace_back(binding++, descriptor_type, 1, stage_flags, nullptr); 152 AddBindings<eUniformTexelBuffer>(bindings, binding, flags, entries.texel_buffers);
132 } 153 AddBindings<eCombinedImageSampler>(bindings, binding, flags, entries.samplers);
133 }; 154 AddBindings<eStorageImage>(bindings, binding, flags, entries.images);
134 AddBindings(vk::DescriptorType::eUniformBuffer, entries.const_buffers.size());
135 AddBindings(vk::DescriptorType::eStorageBuffer, entries.global_buffers.size());
136 AddBindings(vk::DescriptorType::eUniformTexelBuffer, entries.texel_buffers.size());
137 AddBindings(vk::DescriptorType::eCombinedImageSampler, entries.samplers.size());
138 AddBindings(vk::DescriptorType::eStorageImage, entries.images.size());
139 return binding; 155 return binding;
140} 156}
141 157
@@ -361,32 +377,45 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
361 return {std::move(program), std::move(bindings)}; 377 return {std::move(program), std::move(bindings)};
362} 378}
363 379
364void FillDescriptorUpdateTemplateEntries( 380template <vk::DescriptorType descriptor_type, class Container>
365 const VKDevice& device, const ShaderEntries& entries, u32& binding, u32& offset, 381void AddEntry(std::vector<vk::DescriptorUpdateTemplateEntry>& template_entries, u32& binding,
366 std::vector<vk::DescriptorUpdateTemplateEntry>& template_entries) { 382 u32& offset, const Container& container) {
367 static constexpr auto entry_size = static_cast<u32>(sizeof(DescriptorUpdateEntry)); 383 static constexpr u32 entry_size = static_cast<u32>(sizeof(DescriptorUpdateEntry));
368 const auto AddEntry = [&](vk::DescriptorType descriptor_type, std::size_t count_) { 384 const u32 count = static_cast<u32>(std::size(container));
369 const u32 count = static_cast<u32>(count_); 385
370 if (descriptor_type == vk::DescriptorType::eUniformTexelBuffer && 386 if constexpr (descriptor_type == eCombinedImageSampler) {
371 device.GetDriverID() == vk::DriverIdKHR::eNvidiaProprietary) { 387 for (u32 i = 0; i < count; ++i) {
372 // Nvidia has a bug where updating multiple uniform texels at once causes the driver to 388 const u32 num_samplers = container[i].Size();
373 // crash. 389 template_entries.emplace_back(binding, 0, num_samplers, descriptor_type, offset,
374 for (u32 i = 0; i < count; ++i) { 390 entry_size);
375 template_entries.emplace_back(binding + i, 0, 1, descriptor_type, 391 ++binding;
376 offset + i * entry_size, entry_size); 392 offset += num_samplers * entry_size;
377 }
378 } else if (count != 0) {
379 template_entries.emplace_back(binding, 0, count, descriptor_type, offset, entry_size);
380 } 393 }
381 offset += count * entry_size; 394 return;
382 binding += count; 395 }
383 };
384 396
385 AddEntry(vk::DescriptorType::eUniformBuffer, entries.const_buffers.size()); 397 if constexpr (descriptor_type == eUniformTexelBuffer) {
386 AddEntry(vk::DescriptorType::eStorageBuffer, entries.global_buffers.size()); 398 // Nvidia has a bug where updating multiple uniform texels at once causes the driver to
387 AddEntry(vk::DescriptorType::eUniformTexelBuffer, entries.texel_buffers.size()); 399 // crash.
388 AddEntry(vk::DescriptorType::eCombinedImageSampler, entries.samplers.size()); 400 for (u32 i = 0; i < count; ++i) {
389 AddEntry(vk::DescriptorType::eStorageImage, entries.images.size()); 401 template_entries.emplace_back(binding + i, 0, 1, descriptor_type,
402 offset + i * entry_size, entry_size);
403 }
404 } else if (count > 0) {
405 template_entries.emplace_back(binding, 0, count, descriptor_type, offset, entry_size);
406 }
407 offset += count * entry_size;
408 binding += count;
409}
410
411void FillDescriptorUpdateTemplateEntries(
412 const ShaderEntries& entries, u32& binding, u32& offset,
413 std::vector<vk::DescriptorUpdateTemplateEntry>& template_entries) {
414 AddEntry<eUniformBuffer>(template_entries, offset, binding, entries.const_buffers);
415 AddEntry<eStorageBuffer>(template_entries, offset, binding, entries.global_buffers);
416 AddEntry<eUniformTexelBuffer>(template_entries, offset, binding, entries.texel_buffers);
417 AddEntry<eCombinedImageSampler>(template_entries, offset, binding, entries.samplers);
418 AddEntry<eStorageImage>(template_entries, offset, binding, entries.images);
390} 419}
391 420
392} // namespace Vulkan 421} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 8678fc9c3..92a670cc7 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -194,7 +194,7 @@ private:
194}; 194};
195 195
196void FillDescriptorUpdateTemplateEntries( 196void FillDescriptorUpdateTemplateEntries(
197 const VKDevice& device, const ShaderEntries& entries, u32& binding, u32& offset, 197 const ShaderEntries& entries, u32& binding, u32& offset,
198 std::vector<vk::DescriptorUpdateTemplateEntry>& template_entries); 198 std::vector<vk::DescriptorUpdateTemplateEntry>& template_entries);
199 199
200} // namespace Vulkan 200} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 3bf86da87..3fe28c204 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -105,17 +105,20 @@ void TransitionImages(const std::vector<ImageView>& views, vk::PipelineStageFlag
105 105
106template <typename Engine, typename Entry> 106template <typename Engine, typename Entry>
107Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, 107Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry,
108 std::size_t stage) { 108 std::size_t stage, std::size_t index = 0) {
109 const auto stage_type = static_cast<Tegra::Engines::ShaderType>(stage); 109 const auto stage_type = static_cast<Tegra::Engines::ShaderType>(stage);
110 if (entry.IsBindless()) { 110 if (entry.IsBindless()) {
111 const Tegra::Texture::TextureHandle tex_handle = 111 const Tegra::Texture::TextureHandle tex_handle =
112 engine.AccessConstBuffer32(stage_type, entry.GetBuffer(), entry.GetOffset()); 112 engine.AccessConstBuffer32(stage_type, entry.GetBuffer(), entry.GetOffset());
113 return engine.GetTextureInfo(tex_handle); 113 return engine.GetTextureInfo(tex_handle);
114 } 114 }
115 const auto& gpu_profile = engine.AccessGuestDriverProfile();
116 const u32 entry_offset = static_cast<u32>(index * gpu_profile.GetTextureHandlerSize());
117 const u32 offset = entry.GetOffset() + entry_offset;
115 if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { 118 if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) {
116 return engine.GetStageTexture(stage_type, entry.GetOffset()); 119 return engine.GetStageTexture(stage_type, offset);
117 } else { 120 } else {
118 return engine.GetTexture(entry.GetOffset()); 121 return engine.GetTexture(offset);
119 } 122 }
120} 123}
121 124
@@ -836,8 +839,10 @@ void RasterizerVulkan::SetupGraphicsTextures(const ShaderEntries& entries, std::
836 MICROPROFILE_SCOPE(Vulkan_Textures); 839 MICROPROFILE_SCOPE(Vulkan_Textures);
837 const auto& gpu = system.GPU().Maxwell3D(); 840 const auto& gpu = system.GPU().Maxwell3D();
838 for (const auto& entry : entries.samplers) { 841 for (const auto& entry : entries.samplers) {
839 const auto texture = GetTextureInfo(gpu, entry, stage); 842 for (std::size_t i = 0; i < entry.Size(); ++i) {
840 SetupTexture(texture, entry); 843 const auto texture = GetTextureInfo(gpu, entry, stage, i);
844 SetupTexture(texture, entry);
845 }
841 } 846 }
842} 847}
843 848
@@ -886,8 +891,10 @@ void RasterizerVulkan::SetupComputeTextures(const ShaderEntries& entries) {
886 MICROPROFILE_SCOPE(Vulkan_Textures); 891 MICROPROFILE_SCOPE(Vulkan_Textures);
887 const auto& gpu = system.GPU().KeplerCompute(); 892 const auto& gpu = system.GPU().KeplerCompute();
888 for (const auto& entry : entries.samplers) { 893 for (const auto& entry : entries.samplers) {
889 const auto texture = GetTextureInfo(gpu, entry, ComputeShaderIndex); 894 for (std::size_t i = 0; i < entry.Size(); ++i) {
890 SetupTexture(texture, entry); 895 const auto texture = GetTextureInfo(gpu, entry, ComputeShaderIndex, i);
896 SetupTexture(texture, entry);
897 }
891 } 898 }
892} 899}
893 900
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index 2da622d15..cfcca5af0 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -69,8 +69,9 @@ struct TexelBuffer {
69 69
70struct SampledImage { 70struct SampledImage {
71 Id image_type{}; 71 Id image_type{};
72 Id sampled_image_type{}; 72 Id sampler_type{};
73 Id sampler{}; 73 Id sampler_pointer_type{};
74 Id variable{};
74}; 75};
75 76
76struct StorageImage { 77struct StorageImage {
@@ -833,16 +834,20 @@ private:
833 constexpr int sampled = 1; 834 constexpr int sampled = 1;
834 constexpr auto format = spv::ImageFormat::Unknown; 835 constexpr auto format = spv::ImageFormat::Unknown;
835 const Id image_type = TypeImage(t_float, dim, depth, arrayed, ms, sampled, format); 836 const Id image_type = TypeImage(t_float, dim, depth, arrayed, ms, sampled, format);
836 const Id sampled_image_type = TypeSampledImage(image_type); 837 const Id sampler_type = TypeSampledImage(image_type);
837 const Id pointer_type = 838 const Id sampler_pointer_type =
838 TypePointer(spv::StorageClass::UniformConstant, sampled_image_type); 839 TypePointer(spv::StorageClass::UniformConstant, sampler_type);
840 const Id type = sampler.IsIndexed()
841 ? TypeArray(sampler_type, Constant(t_uint, sampler.Size()))
842 : sampler_type;
843 const Id pointer_type = TypePointer(spv::StorageClass::UniformConstant, type);
839 const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant); 844 const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant);
840 AddGlobalVariable(Name(id, fmt::format("sampler_{}", sampler.GetIndex()))); 845 AddGlobalVariable(Name(id, fmt::format("sampler_{}", sampler.GetIndex())));
841 Decorate(id, spv::Decoration::Binding, binding++); 846 Decorate(id, spv::Decoration::Binding, binding++);
842 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); 847 Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET);
843 848
844 sampled_images.emplace(sampler.GetIndex(), 849 sampled_images.emplace(sampler.GetIndex(), SampledImage{image_type, sampler_type,
845 SampledImage{image_type, sampled_image_type, id}); 850 sampler_pointer_type, id});
846 } 851 }
847 return binding; 852 return binding;
848 } 853 }
@@ -1525,7 +1530,12 @@ private:
1525 ASSERT(!meta.sampler.IsBuffer()); 1530 ASSERT(!meta.sampler.IsBuffer());
1526 1531
1527 const auto& entry = sampled_images.at(meta.sampler.GetIndex()); 1532 const auto& entry = sampled_images.at(meta.sampler.GetIndex());
1528 return OpLoad(entry.sampled_image_type, entry.sampler); 1533 Id sampler = entry.variable;
1534 if (meta.sampler.IsIndexed()) {
1535 const Id index = AsInt(Visit(meta.index));
1536 sampler = OpAccessChain(entry.sampler_pointer_type, sampler, index);
1537 }
1538 return OpLoad(entry.sampler_type, sampler);
1529 } 1539 }
1530 1540
1531 Id GetTextureImage(Operation operation) { 1541 Id GetTextureImage(Operation operation) {
@@ -2211,16 +2221,14 @@ private:
2211 switch (specialization.attribute_types.at(location)) { 2221 switch (specialization.attribute_types.at(location)) {
2212 case Maxwell::VertexAttribute::Type::SignedNorm: 2222 case Maxwell::VertexAttribute::Type::SignedNorm:
2213 case Maxwell::VertexAttribute::Type::UnsignedNorm: 2223 case Maxwell::VertexAttribute::Type::UnsignedNorm:
2224 case Maxwell::VertexAttribute::Type::UnsignedScaled:
2225 case Maxwell::VertexAttribute::Type::SignedScaled:
2214 case Maxwell::VertexAttribute::Type::Float: 2226 case Maxwell::VertexAttribute::Type::Float:
2215 return {Type::Float, t_in_float, t_in_float4}; 2227 return {Type::Float, t_in_float, t_in_float4};
2216 case Maxwell::VertexAttribute::Type::SignedInt: 2228 case Maxwell::VertexAttribute::Type::SignedInt:
2217 return {Type::Int, t_in_int, t_in_int4}; 2229 return {Type::Int, t_in_int, t_in_int4};
2218 case Maxwell::VertexAttribute::Type::UnsignedInt: 2230 case Maxwell::VertexAttribute::Type::UnsignedInt:
2219 return {Type::Uint, t_in_uint, t_in_uint4}; 2231 return {Type::Uint, t_in_uint, t_in_uint4};
2220 case Maxwell::VertexAttribute::Type::UnsignedScaled:
2221 case Maxwell::VertexAttribute::Type::SignedScaled:
2222 UNIMPLEMENTED();
2223 return {Type::Float, t_in_float, t_in_float4};
2224 default: 2232 default:
2225 UNREACHABLE(); 2233 UNREACHABLE();
2226 return {Type::Float, t_in_float, t_in_float4}; 2234 return {Type::Float, t_in_float, t_in_float4};
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp
index 90240c765..478394682 100644
--- a/src/video_core/shader/decode/arithmetic.cpp
+++ b/src/video_core/shader/decode/arithmetic.cpp
@@ -53,29 +53,24 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
53 53
54 op_b = GetOperandAbsNegFloat(op_b, false, instr.fmul.negate_b); 54 op_b = GetOperandAbsNegFloat(op_b, false, instr.fmul.negate_b);
55 55
56 // TODO(Rodrigo): Should precise be used when there's a postfactor? 56 static constexpr std::array FmulPostFactor = {
57 Node value = Operation(OperationCode::FMul, PRECISE, op_a, op_b); 57 1.000f, // None
58 0.500f, // Divide 2
59 0.250f, // Divide 4
60 0.125f, // Divide 8
61 8.000f, // Mul 8
62 4.000f, // Mul 4
63 2.000f, // Mul 2
64 };
58 65
59 if (instr.fmul.postfactor != 0) { 66 if (instr.fmul.postfactor != 0) {
60 auto postfactor = static_cast<s32>(instr.fmul.postfactor); 67 op_a = Operation(OperationCode::FMul, NO_PRECISE, op_a,
61 68 Immediate(FmulPostFactor[instr.fmul.postfactor]));
62 // Postfactor encoded as 3-bit 1's complement in instruction, interpreted with below
63 // logic.
64 if (postfactor >= 4) {
65 postfactor = 7 - postfactor;
66 } else {
67 postfactor = 0 - postfactor;
68 }
69
70 if (postfactor > 0) {
71 value = Operation(OperationCode::FMul, NO_PRECISE, value,
72 Immediate(static_cast<f32>(1 << postfactor)));
73 } else {
74 value = Operation(OperationCode::FDiv, NO_PRECISE, value,
75 Immediate(static_cast<f32>(1 << -postfactor)));
76 }
77 } 69 }
78 70
71 // TODO(Rodrigo): Should precise be used when there's a postfactor?
72 Node value = Operation(OperationCode::FMul, PRECISE, op_a, op_b);
73
79 value = GetSaturatedFloat(value, instr.alu.saturate_d); 74 value = GetSaturatedFloat(value, instr.alu.saturate_d);
80 75
81 SetInternalFlagsFromFloat(bb, value, instr.generates_cc); 76 SetInternalFlagsFromFloat(bb, value, instr.generates_cc);
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp
index 21366869d..2fe787d6f 100644
--- a/src/video_core/shader/decode/arithmetic_integer.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer.cpp
@@ -293,44 +293,66 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
293 293
294void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Node op_b, Node op_c, 294void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Node op_b, Node op_c,
295 Node imm_lut, bool sets_cc) { 295 Node imm_lut, bool sets_cc) {
296 constexpr u32 lop_iterations = 32; 296 const Node lop3_fast = [&](const Node na, const Node nb, const Node nc, const Node ttbl) {
297 const Node one = Immediate(1); 297 Node value = Immediate(0);
298 const Node two = Immediate(2); 298 const ImmediateNode imm = std::get<ImmediateNode>(*ttbl);
299 299 if (imm.GetValue() & 0x01) {
300 Node value; 300 const Node a = Operation(OperationCode::IBitwiseNot, na);
301 for (u32 i = 0; i < lop_iterations; ++i) { 301 const Node b = Operation(OperationCode::IBitwiseNot, nb);
302 const Node shift_amount = Immediate(i); 302 const Node c = Operation(OperationCode::IBitwiseNot, nc);
303 303 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, b);
304 const Node a = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_c, shift_amount); 304 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
305 const Node pack_0 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, one); 305 value = Operation(OperationCode::IBitwiseOr, value, r);
306
307 const Node b = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_b, shift_amount);
308 const Node c = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, b, one);
309 const Node pack_1 = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, c, one);
310
311 const Node d = Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, op_a, shift_amount);
312 const Node e = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, d, one);
313 const Node pack_2 = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, e, two);
314
315 const Node pack_01 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, pack_0, pack_1);
316 const Node pack_012 = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, pack_01, pack_2);
317
318 const Node shifted_bit =
319 Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, imm_lut, pack_012);
320 const Node bit = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, shifted_bit, one);
321
322 const Node right =
323 Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, bit, shift_amount);
324
325 if (i > 0) {
326 value = Operation(OperationCode::IBitwiseOr, NO_PRECISE, value, right);
327 } else {
328 value = right;
329 } 306 }
330 } 307 if (imm.GetValue() & 0x02) {
308 const Node a = Operation(OperationCode::IBitwiseNot, na);
309 const Node b = Operation(OperationCode::IBitwiseNot, nb);
310 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, b);
311 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
312 value = Operation(OperationCode::IBitwiseOr, value, r);
313 }
314 if (imm.GetValue() & 0x04) {
315 const Node a = Operation(OperationCode::IBitwiseNot, na);
316 const Node c = Operation(OperationCode::IBitwiseNot, nc);
317 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, nb);
318 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
319 value = Operation(OperationCode::IBitwiseOr, value, r);
320 }
321 if (imm.GetValue() & 0x08) {
322 const Node a = Operation(OperationCode::IBitwiseNot, na);
323 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, a, nb);
324 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
325 value = Operation(OperationCode::IBitwiseOr, value, r);
326 }
327 if (imm.GetValue() & 0x10) {
328 const Node b = Operation(OperationCode::IBitwiseNot, nb);
329 const Node c = Operation(OperationCode::IBitwiseNot, nc);
330 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, b);
331 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
332 value = Operation(OperationCode::IBitwiseOr, value, r);
333 }
334 if (imm.GetValue() & 0x20) {
335 const Node b = Operation(OperationCode::IBitwiseNot, nb);
336 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, b);
337 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
338 value = Operation(OperationCode::IBitwiseOr, value, r);
339 }
340 if (imm.GetValue() & 0x40) {
341 const Node c = Operation(OperationCode::IBitwiseNot, nc);
342 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, nb);
343 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, c);
344 value = Operation(OperationCode::IBitwiseOr, value, r);
345 }
346 if (imm.GetValue() & 0x80) {
347 Node r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, na, nb);
348 r = Operation(OperationCode::IBitwiseAnd, NO_PRECISE, r, nc);
349 value = Operation(OperationCode::IBitwiseOr, value, r);
350 }
351 return value;
352 }(op_a, op_b, op_c, imm_lut);
331 353
332 SetInternalFlagsFromInteger(bb, value, sets_cc); 354 SetInternalFlagsFromInteger(bb, lop3_fast, sets_cc);
333 SetRegister(bb, dest, value); 355 SetRegister(bb, dest, lop3_fast);
334} 356}
335 357
336} // namespace VideoCommon::Shader 358} // namespace VideoCommon::Shader
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index a0a7b9111..a1828546e 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -299,7 +299,7 @@ private:
299 u32 index{}; ///< Emulated index given for the this sampler. 299 u32 index{}; ///< Emulated index given for the this sampler.
300 u32 offset{}; ///< Offset in the const buffer from where the sampler is being read. 300 u32 offset{}; ///< Offset in the const buffer from where the sampler is being read.
301 u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers). 301 u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers).
302 u32 size{}; ///< Size of the sampler if indexed. 302 u32 size{1}; ///< Size of the sampler.
303 303
304 Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) 304 Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
305 bool is_array{}; ///< Whether the texture is being sampled as an array texture or not. 305 bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index face8c943..15e22b9fa 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -157,13 +157,21 @@ std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& co
157 if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) { 157 if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) {
158 return {}; 158 return {};
159 } 159 }
160 // Reduce the cursor in one to avoid infinite loops when the instruction sets the same 160 s64 current_cursor = cursor;
161 // register that it uses as operand 161 while (current_cursor > 0) {
162 const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1); 162 // Reduce the cursor in one to avoid infinite loops when the instruction sets the same
163 if (!source) { 163 // register that it uses as operand
164 return {}; 164 const auto [source, new_cursor] = TrackRegister(gpr, code, current_cursor - 1);
165 current_cursor = new_cursor;
166 if (!source) {
167 continue;
168 }
169 const auto [base_address, index, offset] = TrackCbuf(source, code, current_cursor);
170 if (base_address != nullptr) {
171 return {base_address, index, offset};
172 }
165 } 173 }
166 return TrackCbuf(source, code, new_cursor); 174 return {};
167 } 175 }
168 if (const auto operation = std::get_if<OperationNode>(&*tracked)) { 176 if (const auto operation = std::get_if<OperationNode>(&*tracked)) {
169 for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) { 177 for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) {
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 55a37fffa..c3dbb1a88 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -9,6 +9,9 @@
9#include <QKeyEvent> 9#include <QKeyEvent>
10#include <QMessageBox> 10#include <QMessageBox>
11#include <QOffscreenSurface> 11#include <QOffscreenSurface>
12#include <QOpenGLContext>
13#include <QOpenGLFunctions>
14#include <QOpenGLFunctions_4_3_Core>
12#include <QOpenGLWindow> 15#include <QOpenGLWindow>
13#include <QPainter> 16#include <QPainter>
14#include <QScreen> 17#include <QScreen>
@@ -23,9 +26,10 @@
23#include "common/assert.h" 26#include "common/assert.h"
24#include "common/microprofile.h" 27#include "common/microprofile.h"
25#include "common/scm_rev.h" 28#include "common/scm_rev.h"
29#include "common/scope_exit.h"
26#include "core/core.h" 30#include "core/core.h"
27#include "core/frontend/framebuffer_layout.h" 31#include "core/frontend/framebuffer_layout.h"
28#include "core/frontend/scope_acquire_window_context.h" 32#include "core/frontend/scope_acquire_context.h"
29#include "core/settings.h" 33#include "core/settings.h"
30#include "input_common/keyboard.h" 34#include "input_common/keyboard.h"
31#include "input_common/main.h" 35#include "input_common/main.h"
@@ -35,15 +39,27 @@
35#include "yuzu/bootmanager.h" 39#include "yuzu/bootmanager.h"
36#include "yuzu/main.h" 40#include "yuzu/main.h"
37 41
38EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} 42EmuThread::EmuThread(GRenderWindow& window)
43 : shared_context{window.CreateSharedContext()},
44 context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context
45 : window} {}
39 46
40EmuThread::~EmuThread() = default; 47EmuThread::~EmuThread() = default;
41 48
42void EmuThread::run() { 49static GMainWindow* GetMainWindow() {
43 render_window->MakeCurrent(); 50 for (QWidget* w : qApp->topLevelWidgets()) {
51 if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
52 return main;
53 }
54 }
55 return nullptr;
56}
44 57
58void EmuThread::run() {
45 MicroProfileOnThreadCreate("EmuThread"); 59 MicroProfileOnThreadCreate("EmuThread");
46 60
61 Core::Frontend::ScopeAcquireContext acquire_context{context};
62
47 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 63 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
48 64
49 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( 65 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources(
@@ -53,11 +69,6 @@ void EmuThread::run() {
53 69
54 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); 70 emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
55 71
56 if (Settings::values.use_asynchronous_gpu_emulation) {
57 // Release OpenGL context for the GPU thread
58 render_window->DoneCurrent();
59 }
60
61 // Holds whether the cpu was running during the last iteration, 72 // Holds whether the cpu was running during the last iteration,
62 // so that the DebugModeLeft signal can be emitted before the 73 // so that the DebugModeLeft signal can be emitted before the
63 // next execution step 74 // next execution step
@@ -98,190 +109,202 @@ void EmuThread::run() {
98#if MICROPROFILE_ENABLED 109#if MICROPROFILE_ENABLED
99 MicroProfileOnThreadExit(); 110 MicroProfileOnThreadExit();
100#endif 111#endif
101
102 render_window->moveContext();
103} 112}
104 113
105class GGLContext : public Core::Frontend::GraphicsContext { 114class GGLContext : public Core::Frontend::GraphicsContext {
106public: 115public:
107 explicit GGLContext(QOpenGLContext* shared_context) : shared_context{shared_context} { 116 explicit GGLContext(QOpenGLContext* shared_context)
108 context.setFormat(shared_context->format()); 117 : context(new QOpenGLContext(shared_context->parent())),
109 context.setShareContext(shared_context); 118 surface(new QOffscreenSurface(nullptr)) {
110 context.create(); 119
120 // disable vsync for any shared contexts
121 auto format = shared_context->format();
122 format.setSwapInterval(0);
123
124 context->setShareContext(shared_context);
125 context->setFormat(format);
126 context->create();
127 surface->setParent(shared_context->parent());
128 surface->setFormat(format);
129 surface->create();
111 } 130 }
112 131
113 void MakeCurrent() override { 132 void MakeCurrent() override {
114 context.makeCurrent(shared_context->surface()); 133 context->makeCurrent(surface);
115 } 134 }
116 135
117 void DoneCurrent() override { 136 void DoneCurrent() override {
118 context.doneCurrent(); 137 context->doneCurrent();
119 } 138 }
120 139
121 void SwapBuffers() override {}
122
123private: 140private:
124 QOpenGLContext* shared_context; 141 QOpenGLContext* context;
125 QOpenGLContext context; 142 QOffscreenSurface* surface;
126}; 143};
127 144
128class GWidgetInternal : public QWindow { 145class ChildRenderWindow : public QWindow {
129public: 146public:
130 GWidgetInternal(GRenderWindow* parent) : parent(parent) {} 147 ChildRenderWindow(QWindow* parent, QWidget* event_handler)
131 virtual ~GWidgetInternal() = default; 148 : QWindow{parent}, event_handler{event_handler} {}
132 149
133 void resizeEvent(QResizeEvent* ev) override { 150 virtual ~ChildRenderWindow() = default;
134 parent->OnClientAreaResized(ev->size().width(), ev->size().height());
135 parent->OnFramebufferSizeChanged();
136 }
137 151
138 void keyPressEvent(QKeyEvent* event) override { 152 virtual void Present() = 0;
139 InputCommon::GetKeyboard()->PressKey(event->key());
140 }
141 153
142 void keyReleaseEvent(QKeyEvent* event) override { 154protected:
143 InputCommon::GetKeyboard()->ReleaseKey(event->key()); 155 bool event(QEvent* event) override {
156 switch (event->type()) {
157 case QEvent::UpdateRequest:
158 Present();
159 return true;
160 case QEvent::MouseButtonPress:
161 case QEvent::MouseButtonRelease:
162 case QEvent::MouseButtonDblClick:
163 case QEvent::MouseMove:
164 case QEvent::KeyPress:
165 case QEvent::KeyRelease:
166 case QEvent::FocusIn:
167 case QEvent::FocusOut:
168 case QEvent::FocusAboutToChange:
169 case QEvent::Enter:
170 case QEvent::Leave:
171 case QEvent::Wheel:
172 case QEvent::TabletMove:
173 case QEvent::TabletPress:
174 case QEvent::TabletRelease:
175 case QEvent::TabletEnterProximity:
176 case QEvent::TabletLeaveProximity:
177 case QEvent::TouchBegin:
178 case QEvent::TouchUpdate:
179 case QEvent::TouchEnd:
180 case QEvent::InputMethodQuery:
181 case QEvent::TouchCancel:
182 return QCoreApplication::sendEvent(event_handler, event);
183 case QEvent::Drop:
184 GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
185 return true;
186 case QEvent::DragResponse:
187 case QEvent::DragEnter:
188 case QEvent::DragLeave:
189 case QEvent::DragMove:
190 GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
191 return true;
192 default:
193 return QWindow::event(event);
194 }
144 } 195 }
145 196
146 void mousePressEvent(QMouseEvent* event) override { 197 void exposeEvent(QExposeEvent* event) override {
147 if (event->source() == Qt::MouseEventSynthesizedBySystem) 198 QWindow::requestUpdate();
148 return; // touch input is handled in TouchBeginEvent 199 QWindow::exposeEvent(event);
149
150 const auto pos{event->pos()};
151 if (event->button() == Qt::LeftButton) {
152 const auto [x, y] = parent->ScaleTouch(pos);
153 parent->TouchPressed(x, y);
154 } else if (event->button() == Qt::RightButton) {
155 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
156 }
157 } 200 }
158 201
159 void mouseMoveEvent(QMouseEvent* event) override { 202private:
160 if (event->source() == Qt::MouseEventSynthesizedBySystem) 203 QWidget* event_handler{};
161 return; // touch input is handled in TouchUpdateEvent 204};
162 205
163 const auto pos{event->pos()}; 206class OpenGLWindow final : public ChildRenderWindow {
164 const auto [x, y] = parent->ScaleTouch(pos); 207public:
165 parent->TouchMoved(x, y); 208 OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
166 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); 209 : ChildRenderWindow{parent, event_handler},
167 } 210 context(new QOpenGLContext(shared_context->parent())) {
168 211
169 void mouseReleaseEvent(QMouseEvent* event) override { 212 // disable vsync for any shared contexts
170 if (event->source() == Qt::MouseEventSynthesizedBySystem) 213 auto format = shared_context->format();
171 return; // touch input is handled in TouchEndEvent 214 format.setSwapInterval(Settings::values.use_vsync ? 1 : 0);
215 this->setFormat(format);
172 216
173 if (event->button() == Qt::LeftButton) 217 context->setShareContext(shared_context);
174 parent->TouchReleased(); 218 context->setScreen(this->screen());
175 else if (event->button() == Qt::RightButton) 219 context->setFormat(format);
176 InputCommon::GetMotionEmu()->EndTilt(); 220 context->create();
177 }
178 221
179 void DisablePainting() { 222 setSurfaceType(QWindow::OpenGLSurface);
180 do_painting = false;
181 }
182 223
183 void EnablePainting() { 224 // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
184 do_painting = true; 225 // WA_DontShowOnScreen, WA_DeleteOnClose
185 } 226 }
186 227
187 std::pair<unsigned, unsigned> GetSize() const { 228 ~OpenGLWindow() override {
188 return std::make_pair(width(), height()); 229 context->doneCurrent();
189 } 230 }
190 231
191protected: 232 void Present() override {
192 bool IsPaintingEnabled() const { 233 if (!isExposed()) {
193 return do_painting; 234 return;
235 }
236
237 context->makeCurrent(this);
238 Core::System::GetInstance().Renderer().TryPresent(100);
239 context->swapBuffers(this);
240 auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
241 f->glFinish();
242 QWindow::requestUpdate();
194 } 243 }
195 244
196private: 245private:
197 GRenderWindow* parent; 246 QOpenGLContext* context{};
198 bool do_painting = false;
199};
200
201// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
202// context.
203// The corresponding functionality is handled in EmuThread instead
204class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow {
205public:
206 GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
207 : GWidgetInternal(parent), QOpenGLWindow(shared_context) {}
208 ~GGLWidgetInternal() override = default;
209
210 void paintEvent(QPaintEvent* ev) override {
211 if (IsPaintingEnabled()) {
212 QPainter painter(this);
213 }
214 }
215}; 247};
216 248
217#ifdef HAS_VULKAN 249#ifdef HAS_VULKAN
218class GVKWidgetInternal final : public GWidgetInternal { 250class VulkanWindow final : public ChildRenderWindow {
219public: 251public:
220 GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { 252 VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance)
253 : ChildRenderWindow{parent, event_handler} {
221 setSurfaceType(QSurface::SurfaceType::VulkanSurface); 254 setSurfaceType(QSurface::SurfaceType::VulkanSurface);
222 setVulkanInstance(instance); 255 setVulkanInstance(instance);
223 } 256 }
224 ~GVKWidgetInternal() override = default; 257
258 ~VulkanWindow() override = default;
259
260 void Present() override {
261 // TODO(bunnei): ImplementMe
262 }
263
264private:
265 QWidget* event_handler{};
225}; 266};
226#endif 267#endif
227 268
228GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) 269GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
229 : QWidget(parent), emu_thread(emu_thread) { 270 : QWidget(parent_), emu_thread(emu_thread) {
230 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") 271 setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
231 .arg(QString::fromUtf8(Common::g_build_name), 272 .arg(QString::fromUtf8(Common::g_build_name),
232 QString::fromUtf8(Common::g_scm_branch), 273 QString::fromUtf8(Common::g_scm_branch),
233 QString::fromUtf8(Common::g_scm_desc))); 274 QString::fromUtf8(Common::g_scm_desc)));
234 setAttribute(Qt::WA_AcceptTouchEvents); 275 setAttribute(Qt::WA_AcceptTouchEvents);
235 276 auto layout = new QHBoxLayout(this);
277 layout->setMargin(0);
278 setLayout(layout);
236 InputCommon::Init(); 279 InputCommon::Init();
280
281 GMainWindow* parent = GetMainWindow();
237 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 282 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
238} 283}
239 284
240GRenderWindow::~GRenderWindow() { 285GRenderWindow::~GRenderWindow() {
241 InputCommon::Shutdown(); 286 InputCommon::Shutdown();
242
243 // Avoid an unordered destruction that generates a segfault
244 delete child;
245} 287}
246 288
247void GRenderWindow::moveContext() { 289void GRenderWindow::MakeCurrent() {
248 if (!context) { 290 if (core_context) {
249 return; 291 core_context->MakeCurrent();
250 } 292 }
251 DoneCurrent();
252
253 // If the thread started running, move the GL Context to the new thread. Otherwise, move it
254 // back.
255 auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr)
256 ? emu_thread
257 : qApp->thread();
258 context->moveToThread(thread);
259} 293}
260 294
261void GRenderWindow::SwapBuffers() { 295void GRenderWindow::DoneCurrent() {
262 if (context) { 296 if (core_context) {
263 context->swapBuffers(child); 297 core_context->DoneCurrent();
264 } 298 }
299}
300
301void GRenderWindow::PollEvents() {
265 if (!first_frame) { 302 if (!first_frame) {
266 first_frame = true; 303 first_frame = true;
267 emit FirstFrameDisplayed(); 304 emit FirstFrameDisplayed();
268 } 305 }
269} 306}
270 307
271void GRenderWindow::MakeCurrent() {
272 if (context) {
273 context->makeCurrent(child);
274 }
275}
276
277void GRenderWindow::DoneCurrent() {
278 if (context) {
279 context->doneCurrent();
280 }
281}
282
283void GRenderWindow::PollEvents() {}
284
285bool GRenderWindow::IsShown() const { 308bool GRenderWindow::IsShown() const {
286 return !isMinimized(); 309 return !isMinimized();
287} 310}
@@ -291,7 +314,7 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i
291#ifdef HAS_VULKAN 314#ifdef HAS_VULKAN
292 const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); 315 const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
293 const VkInstance instance_copy = vk_instance->vkInstance(); 316 const VkInstance instance_copy = vk_instance->vkInstance();
294 const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child); 317 const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window);
295 318
296 std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); 319 std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
297 std::memcpy(instance, &instance_copy, sizeof(instance_copy)); 320 std::memcpy(instance, &instance_copy, sizeof(instance_copy));
@@ -309,21 +332,10 @@ void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* i
309void GRenderWindow::OnFramebufferSizeChanged() { 332void GRenderWindow::OnFramebufferSizeChanged() {
310 // Screen changes potentially incur a change in screen DPI, hence we should update the 333 // Screen changes potentially incur a change in screen DPI, hence we should update the
311 // framebuffer size 334 // framebuffer size
312 const qreal pixelRatio{GetWindowPixelRatio()}; 335 const qreal pixel_ratio = windowPixelRatio();
313 const auto size{child->GetSize()}; 336 const u32 width = this->width() * pixel_ratio;
314 UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); 337 const u32 height = this->height() * pixel_ratio;
315} 338 UpdateCurrentFramebufferLayout(width, height);
316
317void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
318 if (child) {
319 child->keyPressEvent(event);
320 }
321}
322
323void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) {
324 if (child) {
325 child->keyReleaseEvent(event);
326 }
327} 339}
328 340
329void GRenderWindow::BackupGeometry() { 341void GRenderWindow::BackupGeometry() {
@@ -351,13 +363,12 @@ QByteArray GRenderWindow::saveGeometry() {
351 return geometry; 363 return geometry;
352} 364}
353 365
354qreal GRenderWindow::GetWindowPixelRatio() const { 366qreal GRenderWindow::windowPixelRatio() const {
355 // windowHandle() might not be accessible until the window is displayed to screen. 367 return devicePixelRatio();
356 return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f;
357} 368}
358 369
359std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { 370std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
360 const qreal pixel_ratio{GetWindowPixelRatio()}; 371 const qreal pixel_ratio = windowPixelRatio();
361 return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), 372 return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
362 static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; 373 static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
363} 374}
@@ -367,6 +378,47 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
367 QWidget::closeEvent(event); 378 QWidget::closeEvent(event);
368} 379}
369 380
381void GRenderWindow::keyPressEvent(QKeyEvent* event) {
382 InputCommon::GetKeyboard()->PressKey(event->key());
383}
384
385void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
386 InputCommon::GetKeyboard()->ReleaseKey(event->key());
387}
388
389void GRenderWindow::mousePressEvent(QMouseEvent* event) {
390 if (event->source() == Qt::MouseEventSynthesizedBySystem)
391 return; // touch input is handled in TouchBeginEvent
392
393 auto pos = event->pos();
394 if (event->button() == Qt::LeftButton) {
395 const auto [x, y] = ScaleTouch(pos);
396 this->TouchPressed(x, y);
397 } else if (event->button() == Qt::RightButton) {
398 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
399 }
400}
401
402void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
403 if (event->source() == Qt::MouseEventSynthesizedBySystem)
404 return; // touch input is handled in TouchUpdateEvent
405
406 auto pos = event->pos();
407 const auto [x, y] = ScaleTouch(pos);
408 this->TouchMoved(x, y);
409 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
410}
411
412void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
413 if (event->source() == Qt::MouseEventSynthesizedBySystem)
414 return; // touch input is handled in TouchEndEvent
415
416 if (event->button() == Qt::LeftButton)
417 this->TouchReleased();
418 else if (event->button() == Qt::RightButton)
419 InputCommon::GetMotionEmu()->EndTilt();
420}
421
370void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { 422void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
371 // TouchBegin always has exactly one touch point, so take the .first() 423 // TouchBegin always has exactly one touch point, so take the .first()
372 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); 424 const auto [x, y] = ScaleTouch(event->touchPoints().first().pos());
@@ -415,26 +467,20 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) {
415 InputCommon::GetKeyboard()->ReleaseAllKeys(); 467 InputCommon::GetKeyboard()->ReleaseAllKeys();
416} 468}
417 469
418void GRenderWindow::OnClientAreaResized(u32 width, u32 height) { 470void GRenderWindow::resizeEvent(QResizeEvent* event) {
419 NotifyClientAreaSizeChanged(std::make_pair(width, height)); 471 QWidget::resizeEvent(event);
472 OnFramebufferSizeChanged();
420} 473}
421 474
422std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { 475std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
423 return std::make_unique<GGLContext>(context.get()); 476 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
477 return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext());
478 }
479 return {};
424} 480}
425 481
426bool GRenderWindow::InitRenderTarget() { 482bool GRenderWindow::InitRenderTarget() {
427 shared_context.reset(); 483 ReleaseRenderTarget();
428 context.reset();
429 if (child) {
430 delete child;
431 }
432 if (container) {
433 delete container;
434 }
435 if (layout()) {
436 delete layout();
437 }
438 484
439 first_frame = false; 485 first_frame = false;
440 486
@@ -451,13 +497,6 @@ bool GRenderWindow::InitRenderTarget() {
451 break; 497 break;
452 } 498 }
453 499
454 container = QWidget::createWindowContainer(child, this);
455 QBoxLayout* layout = new QHBoxLayout(this);
456
457 layout->addWidget(container);
458 layout->setMargin(0);
459 setLayout(layout);
460
461 // Reset minimum required size to avoid resizing issues on the main window after restarting. 500 // Reset minimum required size to avoid resizing issues on the main window after restarting.
462 setMinimumSize(1, 1); 501 setMinimumSize(1, 1);
463 502
@@ -467,14 +506,9 @@ bool GRenderWindow::InitRenderTarget() {
467 hide(); 506 hide();
468 507
469 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); 508 resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
470 child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
471 container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
472 509
473 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 510 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
474
475 OnFramebufferSizeChanged(); 511 OnFramebufferSizeChanged();
476 NotifyClientAreaSizeChanged(child->GetSize());
477
478 BackupGeometry(); 512 BackupGeometry();
479 513
480 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { 514 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
@@ -486,6 +520,14 @@ bool GRenderWindow::InitRenderTarget() {
486 return true; 520 return true;
487} 521}
488 522
523void GRenderWindow::ReleaseRenderTarget() {
524 if (child_widget) {
525 layout()->removeWidget(child_widget);
526 delete child_widget;
527 child_widget = nullptr;
528 }
529}
530
489void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { 531void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
490 auto& renderer = Core::System::GetInstance().Renderer(); 532 auto& renderer = Core::System::GetInstance().Renderer();
491 533
@@ -521,16 +563,19 @@ bool GRenderWindow::InitializeOpenGL() {
521 fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); 563 fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
522 // TODO: expose a setting for buffer value (ie default/single/double/triple) 564 // TODO: expose a setting for buffer value (ie default/single/double/triple)
523 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); 565 fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
524 shared_context = std::make_unique<QOpenGLContext>(); 566 fmt.setSwapInterval(0);
525 shared_context->setFormat(fmt); 567 QSurfaceFormat::setDefaultFormat(fmt);
526 shared_context->create(); 568
527 context = std::make_unique<QOpenGLContext>(); 569 GMainWindow* parent = GetMainWindow();
528 context->setShareContext(shared_context.get()); 570 QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
529 context->setFormat(fmt); 571 child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext());
530 context->create(); 572 child_window->create();
531 fmt.setSwapInterval(false); 573 child_widget = createWindowContainer(child_window, this);
532 574 child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
533 child = new GGLWidgetInternal(this, shared_context.get()); 575 layout()->addWidget(child_widget);
576
577 core_context = CreateSharedContext();
578
534 return true; 579 return true;
535} 580}
536 581
@@ -559,7 +604,14 @@ bool GRenderWindow::InitializeVulkan() {
559 return false; 604 return false;
560 } 605 }
561 606
562 child = new GVKWidgetInternal(this, vk_instance.get()); 607 GMainWindow* parent = GetMainWindow();
608 QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
609 child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get());
610 child_window->create();
611 child_widget = createWindowContainer(child_window, this);
612 child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
613 layout()->addWidget(child_widget);
614
563 return true; 615 return true;
564#else 616#else
565 QMessageBox::critical(this, tr("Vulkan not available!"), 617 QMessageBox::critical(this, tr("Vulkan not available!"),
@@ -569,7 +621,7 @@ bool GRenderWindow::InitializeVulkan() {
569} 621}
570 622
571bool GRenderWindow::LoadOpenGL() { 623bool GRenderWindow::LoadOpenGL() {
572 Core::Frontend::ScopeAcquireWindowContext acquire_context{*this}; 624 Core::Frontend::ScopeAcquireContext acquire_context{*this};
573 if (!gladLoadGL()) { 625 if (!gladLoadGL()) {
574 QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), 626 QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
575 tr("Your GPU may not support OpenGL 4.3, or you do not have the " 627 tr("Your GPU may not support OpenGL 4.3, or you do not have the "
@@ -621,12 +673,10 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
621 673
622void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { 674void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
623 this->emu_thread = emu_thread; 675 this->emu_thread = emu_thread;
624 child->DisablePainting();
625} 676}
626 677
627void GRenderWindow::OnEmulationStopping() { 678void GRenderWindow::OnEmulationStopping() {
628 emu_thread = nullptr; 679 emu_thread = nullptr;
629 child->EnablePainting();
630} 680}
631 681
632void GRenderWindow::showEvent(QShowEvent* event) { 682void GRenderWindow::showEvent(QShowEvent* event) {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 71a2fa321..79b030304 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -11,11 +11,13 @@
11#include <QImage> 11#include <QImage>
12#include <QThread> 12#include <QThread>
13#include <QWidget> 13#include <QWidget>
14#include <QWindow>
14 15
15#include "common/thread.h" 16#include "common/thread.h"
16#include "core/core.h" 17#include "core/core.h"
17#include "core/frontend/emu_window.h" 18#include "core/frontend/emu_window.h"
18 19
20class GRenderWindow;
19class QKeyEvent; 21class QKeyEvent;
20class QScreen; 22class QScreen;
21class QTouchEvent; 23class QTouchEvent;
@@ -26,14 +28,6 @@ class QOpenGLContext;
26class QVulkanInstance; 28class QVulkanInstance;
27#endif 29#endif
28 30
29class GWidgetInternal;
30class GGLWidgetInternal;
31class GVKWidgetInternal;
32class GMainWindow;
33class GRenderWindow;
34class QSurface;
35class QOpenGLContext;
36
37namespace VideoCore { 31namespace VideoCore {
38enum class LoadCallbackStage; 32enum class LoadCallbackStage;
39} 33}
@@ -42,7 +36,7 @@ class EmuThread final : public QThread {
42 Q_OBJECT 36 Q_OBJECT
43 37
44public: 38public:
45 explicit EmuThread(GRenderWindow* render_window); 39 explicit EmuThread(GRenderWindow& window);
46 ~EmuThread() override; 40 ~EmuThread() override;
47 41
48 /** 42 /**
@@ -96,7 +90,11 @@ private:
96 std::mutex running_mutex; 90 std::mutex running_mutex;
97 std::condition_variable running_cv; 91 std::condition_variable running_cv;
98 92
99 GRenderWindow* render_window; 93 /// Only used in asynchronous GPU mode
94 std::unique_ptr<Core::Frontend::GraphicsContext> shared_context;
95
96 /// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode
97 Core::Frontend::GraphicsContext& context;
100 98
101signals: 99signals:
102 /** 100 /**
@@ -126,11 +124,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
126 Q_OBJECT 124 Q_OBJECT
127 125
128public: 126public:
129 GRenderWindow(GMainWindow* parent, EmuThread* emu_thread); 127 GRenderWindow(QWidget* parent, EmuThread* emu_thread);
130 ~GRenderWindow() override; 128 ~GRenderWindow() override;
131 129
132 // EmuWindow implementation 130 // EmuWindow implementation.
133 void SwapBuffers() override;
134 void MakeCurrent() override; 131 void MakeCurrent() override;
135 void DoneCurrent() override; 132 void DoneCurrent() override;
136 void PollEvents() override; 133 void PollEvents() override;
@@ -139,30 +136,36 @@ public:
139 void* surface) const override; 136 void* surface) const override;
140 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 137 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
141 138
142 void ForwardKeyPressEvent(QKeyEvent* event);
143 void ForwardKeyReleaseEvent(QKeyEvent* event);
144
145 void BackupGeometry(); 139 void BackupGeometry();
146 void RestoreGeometry(); 140 void RestoreGeometry();
147 void restoreGeometry(const QByteArray& geometry); // overridden 141 void restoreGeometry(const QByteArray& geometry); // overridden
148 QByteArray saveGeometry(); // overridden 142 QByteArray saveGeometry(); // overridden
149 143
150 qreal GetWindowPixelRatio() const; 144 qreal windowPixelRatio() const;
151 std::pair<u32, u32> ScaleTouch(QPointF pos) const;
152 145
153 void closeEvent(QCloseEvent* event) override; 146 void closeEvent(QCloseEvent* event) override;
147
148 void resizeEvent(QResizeEvent* event) override;
149
150 void keyPressEvent(QKeyEvent* event) override;
151 void keyReleaseEvent(QKeyEvent* event) override;
152
153 void mousePressEvent(QMouseEvent* event) override;
154 void mouseMoveEvent(QMouseEvent* event) override;
155 void mouseReleaseEvent(QMouseEvent* event) override;
156
154 bool event(QEvent* event) override; 157 bool event(QEvent* event) override;
155 void focusOutEvent(QFocusEvent* event) override;
156 158
157 void OnClientAreaResized(u32 width, u32 height); 159 void focusOutEvent(QFocusEvent* event) override;
158 160
159 bool InitRenderTarget(); 161 bool InitRenderTarget();
160 162
163 /// Destroy the previous run's child_widget which should also destroy the child_window
164 void ReleaseRenderTarget();
165
161 void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); 166 void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
162 167
163public slots: 168public slots:
164 void moveContext(); // overridden
165
166 void OnEmulationStarting(EmuThread* emu_thread); 169 void OnEmulationStarting(EmuThread* emu_thread);
167 void OnEmulationStopping(); 170 void OnEmulationStopping();
168 void OnFramebufferSizeChanged(); 171 void OnFramebufferSizeChanged();
@@ -173,6 +176,7 @@ signals:
173 void FirstFrameDisplayed(); 176 void FirstFrameDisplayed();
174 177
175private: 178private:
179 std::pair<u32, u32> ScaleTouch(QPointF pos) const;
176 void TouchBeginEvent(const QTouchEvent* event); 180 void TouchBeginEvent(const QTouchEvent* event);
177 void TouchUpdateEvent(const QTouchEvent* event); 181 void TouchUpdateEvent(const QTouchEvent* event);
178 void TouchEndEvent(); 182 void TouchEndEvent();
@@ -184,15 +188,9 @@ private:
184 bool LoadOpenGL(); 188 bool LoadOpenGL();
185 QStringList GetUnsupportedGLExtensions() const; 189 QStringList GetUnsupportedGLExtensions() const;
186 190
187 QWidget* container = nullptr;
188 GWidgetInternal* child = nullptr;
189
190 EmuThread* emu_thread; 191 EmuThread* emu_thread;
191 // Context that backs the GGLWidgetInternal (and will be used by core to render) 192
192 std::unique_ptr<QOpenGLContext> context; 193 std::unique_ptr<GraphicsContext> core_context;
193 // Context that will be shared between all newly created contexts. This should never be made
194 // current
195 std::unique_ptr<QOpenGLContext> shared_context;
196 194
197#ifdef HAS_VULKAN 195#ifdef HAS_VULKAN
198 std::unique_ptr<QVulkanInstance> vk_instance; 196 std::unique_ptr<QVulkanInstance> vk_instance;
@@ -202,6 +200,15 @@ private:
202 QImage screenshot_image; 200 QImage screenshot_image;
203 201
204 QByteArray geometry; 202 QByteArray geometry;
203
204 /// Native window handle that backs this presentation widget
205 QWindow* child_window = nullptr;
206
207 /// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
208 /// put the child_window into a widget then add it to the layout. This child_widget can be
209 /// parented to GRenderWindow and use Qt's lifetime system
210 QWidget* child_widget = nullptr;
211
205 bool first_frame = false; 212 bool first_frame = false;
206 213
207protected: 214protected:
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 6209fff75..d0f574147 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -640,6 +640,7 @@ void Config::ReadRendererValues() {
640 ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool(); 640 ReadSetting(QStringLiteral("use_accurate_gpu_emulation"), false).toBool();
641 Settings::values.use_asynchronous_gpu_emulation = 641 Settings::values.use_asynchronous_gpu_emulation =
642 ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); 642 ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool();
643 Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool();
643 Settings::values.force_30fps_mode = 644 Settings::values.force_30fps_mode =
644 ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); 645 ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool();
645 646
@@ -1074,6 +1075,7 @@ void Config::SaveRendererValues() {
1074 Settings::values.use_accurate_gpu_emulation, false); 1075 Settings::values.use_accurate_gpu_emulation, false);
1075 WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), 1076 WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"),
1076 Settings::values.use_asynchronous_gpu_emulation, false); 1077 Settings::values.use_asynchronous_gpu_emulation, false);
1078 WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
1077 WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); 1079 WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false);
1078 1080
1079 // Cast to double because Qt's written float values are not human-readable 1081 // Cast to double because Qt's written float values are not human-readable
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index ea899c080..fe64c7d81 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -103,6 +103,8 @@ void ConfigureGraphics::SetConfiguration() {
103 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); 103 ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);
104 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); 104 ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
105 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); 105 ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation);
106 ui->use_vsync->setEnabled(runtime_lock);
107 ui->use_vsync->setChecked(Settings::values.use_vsync);
106 ui->force_30fps_mode->setEnabled(runtime_lock); 108 ui->force_30fps_mode->setEnabled(runtime_lock);
107 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); 109 ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
108 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, 110 UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
@@ -120,6 +122,7 @@ void ConfigureGraphics::ApplyConfiguration() {
120 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); 122 Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();
121 Settings::values.use_asynchronous_gpu_emulation = 123 Settings::values.use_asynchronous_gpu_emulation =
122 ui->use_asynchronous_gpu_emulation->isChecked(); 124 ui->use_asynchronous_gpu_emulation->isChecked();
125 Settings::values.use_vsync = ui->use_vsync->isChecked();
123 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); 126 Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked();
124 Settings::values.bg_red = static_cast<float>(bg_color.redF()); 127 Settings::values.bg_red = static_cast<float>(bg_color.redF());
125 Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 128 Settings::values.bg_green = static_cast<float>(bg_color.greenF());
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index db60426ab..9acc7dd93 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -85,6 +85,16 @@
85 </widget> 85 </widget>
86 </item> 86 </item>
87 <item> 87 <item>
88 <widget class="QCheckBox" name="use_vsync">
89 <property name="toolTip">
90 <string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
91 </property>
92 <property name="text">
93 <string>Use VSync (OpenGL only)</string>
94 </property>
95 </widget>
96 </item>
97 <item>
88 <widget class="QCheckBox" name="use_accurate_gpu_emulation"> 98 <widget class="QCheckBox" name="use_accurate_gpu_emulation">
89 <property name="text"> 99 <property name="text">
90 <string>Use accurate GPU emulation (slow)</string> 100 <string>Use accurate GPU emulation (slow)</string>
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 3f1a94627..c1ea25fb8 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -116,7 +116,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
116 116
117 constexpr std::size_t BaseRegister = 29; 117 constexpr std::size_t BaseRegister = 29;
118 auto& memory = Core::System::GetInstance().Memory(); 118 auto& memory = Core::System::GetInstance().Memory();
119 u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; 119 u64 base_pointer = thread.GetContext64().cpu_registers[BaseRegister];
120 120
121 while (base_pointer != 0) { 121 while (base_pointer != 0) {
122 const u64 lr = memory.Read64(base_pointer + sizeof(u64)); 122 const u64 lr = memory.Read64(base_pointer + sizeof(u64));
@@ -240,7 +240,7 @@ QString WaitTreeThread::GetText() const {
240 break; 240 break;
241 } 241 }
242 242
243 const auto& context = thread.GetContext(); 243 const auto& context = thread.GetContext64();
244 const QString pc_info = tr(" PC = 0x%1 LR = 0x%2") 244 const QString pc_info = tr(" PC = 0x%1 LR = 0x%2")
245 .arg(context.pc, 8, 16, QLatin1Char{'0'}) 245 .arg(context.pc, 8, 16, QLatin1Char{'0'})
246 .arg(context.cpu_registers[30], 8, 16, QLatin1Char{'0'}); 246 .arg(context.cpu_registers[30], 8, 16, QLatin1Char{'0'});
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 54ca2dc1d..47615adfe 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -20,7 +20,6 @@
20#include "core/file_sys/vfs.h" 20#include "core/file_sys/vfs.h"
21#include "core/file_sys/vfs_real.h" 21#include "core/file_sys/vfs_real.h"
22#include "core/frontend/applets/general_frontend.h" 22#include "core/frontend/applets/general_frontend.h"
23#include "core/frontend/scope_acquire_window_context.h"
24#include "core/hle/service/acc/profile_manager.h" 23#include "core/hle/service/acc/profile_manager.h"
25#include "core/hle/service/am/applet_ae.h" 24#include "core/hle/service/am/applet_ae.h"
26#include "core/hle/service/am/applet_oe.h" 25#include "core/hle/service/am/applet_oe.h"
@@ -985,11 +984,8 @@ void GMainWindow::BootGame(const QString& filename) {
985 return; 984 return;
986 985
987 // Create and start the emulation thread 986 // Create and start the emulation thread
988 emu_thread = std::make_unique<EmuThread>(render_window); 987 emu_thread = std::make_unique<EmuThread>(*render_window);
989 emit EmulationStarting(emu_thread.get()); 988 emit EmulationStarting(emu_thread.get());
990 if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
991 render_window->moveContext();
992 }
993 emu_thread->start(); 989 emu_thread->start();
994 990
995 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 991 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
@@ -1087,6 +1083,9 @@ void GMainWindow::ShutdownGame() {
1087 emulation_running = false; 1083 emulation_running = false;
1088 1084
1089 game_path.clear(); 1085 game_path.clear();
1086
1087 // When closing the game, destroy the GLWindow to clear the context after the game is closed
1088 render_window->ReleaseRenderTarget();
1090} 1089}
1091 1090
1092void GMainWindow::StoreRecentFile(const QString& filename) { 1091void GMainWindow::StoreRecentFile(const QString& filename) {
@@ -2215,48 +2214,47 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
2215 QWidget::closeEvent(event); 2214 QWidget::closeEvent(event);
2216} 2215}
2217 2216
2218void GMainWindow::keyPressEvent(QKeyEvent* event) { 2217static bool IsSingleFileDropEvent(const QMimeData* mime) {
2219 if (render_window) { 2218 return mime->hasUrls() && mime->urls().length() == 1;
2220 render_window->ForwardKeyPressEvent(event);
2221 }
2222} 2219}
2223 2220
2224void GMainWindow::keyReleaseEvent(QKeyEvent* event) { 2221void GMainWindow::AcceptDropEvent(QDropEvent* event) {
2225 if (render_window) { 2222 if (IsSingleFileDropEvent(event->mimeData())) {
2226 render_window->ForwardKeyReleaseEvent(event); 2223 event->setDropAction(Qt::DropAction::LinkAction);
2224 event->accept();
2227 } 2225 }
2228} 2226}
2229 2227
2230static bool IsSingleFileDropEvent(QDropEvent* event) { 2228bool GMainWindow::DropAction(QDropEvent* event) {
2231 const QMimeData* mimeData = event->mimeData(); 2229 if (!IsSingleFileDropEvent(event->mimeData())) {
2232 return mimeData->hasUrls() && mimeData->urls().length() == 1; 2230 return false;
2233}
2234
2235void GMainWindow::dropEvent(QDropEvent* event) {
2236 if (!IsSingleFileDropEvent(event)) {
2237 return;
2238 } 2231 }
2239 2232
2240 const QMimeData* mime_data = event->mimeData(); 2233 const QMimeData* mime_data = event->mimeData();
2241 const QString filename = mime_data->urls().at(0).toLocalFile(); 2234 const QString& filename = mime_data->urls().at(0).toLocalFile();
2242 2235
2243 if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) { 2236 if (emulation_running && QFileInfo(filename).suffix() == QStringLiteral("bin")) {
2237 // Amiibo
2244 LoadAmiibo(filename); 2238 LoadAmiibo(filename);
2245 } else { 2239 } else {
2240 // Game
2246 if (ConfirmChangeGame()) { 2241 if (ConfirmChangeGame()) {
2247 BootGame(filename); 2242 BootGame(filename);
2248 } 2243 }
2249 } 2244 }
2245 return true;
2246}
2247
2248void GMainWindow::dropEvent(QDropEvent* event) {
2249 DropAction(event);
2250} 2250}
2251 2251
2252void GMainWindow::dragEnterEvent(QDragEnterEvent* event) { 2252void GMainWindow::dragEnterEvent(QDragEnterEvent* event) {
2253 if (IsSingleFileDropEvent(event)) { 2253 AcceptDropEvent(event);
2254 event->acceptProposedAction();
2255 }
2256} 2254}
2257 2255
2258void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { 2256void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
2259 event->acceptProposedAction(); 2257 AcceptDropEvent(event);
2260} 2258}
2261 2259
2262bool GMainWindow::ConfirmChangeGame() { 2260bool GMainWindow::ConfirmChangeGame() {
@@ -2377,6 +2375,7 @@ int main(int argc, char* argv[]) {
2377 2375
2378 // Enables the core to make the qt created contexts current on std::threads 2376 // Enables the core to make the qt created contexts current on std::threads
2379 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 2377 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
2378 QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
2380 QApplication app(argc, argv); 2379 QApplication app(argc, argv);
2381 2380
2382 // Qt changes the locale and causes issues in float conversion using std::to_string() when 2381 // Qt changes the locale and causes issues in float conversion using std::to_string() when
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 8eba2172c..a67125567 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -78,6 +78,9 @@ public:
78 78
79 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; 79 std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
80 80
81 bool DropAction(QDropEvent* event);
82 void AcceptDropEvent(QDropEvent* event);
83
81signals: 84signals:
82 85
83 /** 86 /**
@@ -264,8 +267,4 @@ protected:
264 void dropEvent(QDropEvent* event) override; 267 void dropEvent(QDropEvent* event) override;
265 void dragEnterEvent(QDragEnterEvent* event) override; 268 void dragEnterEvent(QDragEnterEvent* event) override;
266 void dragMoveEvent(QDragMoveEvent* event) override; 269 void dragMoveEvent(QDragMoveEvent* event) override;
267
268 // Overrides used to forward signals to the render window when the focus moves out.
269 void keyPressEvent(QKeyEvent* event) override;
270 void keyReleaseEvent(QKeyEvent* event) override;
271}; 270};
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 96f1ce3af..b77c12baf 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -390,6 +390,8 @@ void Config::ReadValues() {
390 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false); 390 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
391 Settings::values.use_asynchronous_gpu_emulation = 391 Settings::values.use_asynchronous_gpu_emulation =
392 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false); 392 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false);
393 Settings::values.use_vsync =
394 static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1));
393 395
394 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0)); 396 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0));
395 Settings::values.bg_green = 397 Settings::values.bg_green =
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 8a2b658cd..085ffbc81 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -84,7 +84,7 @@ touch_device=
84# from any cemuhook compatible motion program. 84# from any cemuhook compatible motion program.
85 85
86# IPv4 address of the udp input server (Default "127.0.0.1") 86# IPv4 address of the udp input server (Default "127.0.0.1")
87udp_input_address= 87udp_input_address=127.0.0.1
88 88
89# Port of the udp input server. (Default 26760) 89# Port of the udp input server. (Default 26760)
90udp_input_port= 90udp_input_port=
@@ -150,6 +150,11 @@ use_accurate_gpu_emulation =
150# 0 : Off (slow), 1 (default): On (fast) 150# 0 : Off (slow), 1 (default): On (fast)
151use_asynchronous_gpu_emulation = 151use_asynchronous_gpu_emulation =
152 152
153# Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can
154# so only turn this off if you notice a speed difference.
155# 0: Off, 1 (default): On
156use_vsync =
157
153# The clear color for the renderer. What shows up on the sides of the bottom screen. 158# The clear color for the renderer. What shows up on the sides of the bottom screen.
154# Must be in range of 0.0-1.0. Defaults to 1.0 for all. 159# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
155bg_red = 160bg_red =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index e96139885..19584360c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -13,7 +13,7 @@
13#include "input_common/sdl/sdl.h" 13#include "input_common/sdl/sdl.h"
14#include "yuzu_cmd/emu_window/emu_window_sdl2.h" 14#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
15 15
16EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { 16EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen) : system{system} {
17 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 17 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
18 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 18 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
19 exit(1); 19 exit(1);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index b38f56661..fffac4252 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -10,9 +10,13 @@
10 10
11struct SDL_Window; 11struct SDL_Window;
12 12
13namespace Core {
14class System;
15}
16
13class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 17class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
14public: 18public:
15 explicit EmuWindow_SDL2(bool fullscreen); 19 explicit EmuWindow_SDL2(Core::System& system, bool fullscreen);
16 ~EmuWindow_SDL2(); 20 ~EmuWindow_SDL2();
17 21
18 /// Polls window events 22 /// Polls window events
@@ -24,6 +28,9 @@ public:
24 /// Returns if window is shown (not minimized) 28 /// Returns if window is shown (not minimized)
25 bool IsShown() const override; 29 bool IsShown() const override;
26 30
31 /// Presents the next frame
32 virtual void Present() = 0;
33
27protected: 34protected:
28 /// Called by PollEvents when a key is pressed or released. 35 /// Called by PollEvents when a key is pressed or released.
29 void OnKeyEvent(int key, u8 state); 36 void OnKeyEvent(int key, u8 state);
@@ -55,6 +62,9 @@ protected:
55 /// Called when a configuration change affects the minimal size of the window 62 /// Called when a configuration change affects the minimal size of the window
56 void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; 63 void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override;
57 64
65 /// Instance of the system, used to access renderer for the presentation thread
66 Core::System& system;
67
58 /// Is the window still open? 68 /// Is the window still open?
59 bool is_open = true; 69 bool is_open = true;
60 70
@@ -62,7 +72,7 @@ protected:
62 bool is_shown = true; 72 bool is_shown = true;
63 73
64 /// Internal SDL2 render window 74 /// Internal SDL2 render window
65 SDL_Window* render_window; 75 SDL_Window* render_window{};
66 76
67 /// Keeps track of how often to update the title bar during gameplay 77 /// Keeps track of how often to update the title bar during gameplay
68 u32 last_time = 0; 78 u32 last_time = 0;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 7ffa0ac09..c0d373477 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -13,24 +13,25 @@
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "common/scm_rev.h" 14#include "common/scm_rev.h"
15#include "common/string_util.h" 15#include "common/string_util.h"
16#include "core/core.h"
16#include "core/settings.h" 17#include "core/settings.h"
17#include "input_common/keyboard.h" 18#include "input_common/keyboard.h"
18#include "input_common/main.h" 19#include "input_common/main.h"
19#include "input_common/motion_emu.h" 20#include "input_common/motion_emu.h"
21#include "video_core/renderer_base.h"
20#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" 22#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
21 23
22class SDLGLContext : public Core::Frontend::GraphicsContext { 24class SDLGLContext : public Core::Frontend::GraphicsContext {
23public: 25public:
24 explicit SDLGLContext() { 26 explicit SDLGLContext() {
25 // create a hidden window to make the shared context against 27 // create a hidden window to make the shared context against
26 window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position 28 window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
27 SDL_WINDOWPOS_UNDEFINED, // y position 29 SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
28 Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
29 SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
30 context = SDL_GL_CreateContext(window); 30 context = SDL_GL_CreateContext(window);
31 } 31 }
32 32
33 ~SDLGLContext() { 33 ~SDLGLContext() {
34 DoneCurrent();
34 SDL_GL_DeleteContext(context); 35 SDL_GL_DeleteContext(context);
35 SDL_DestroyWindow(window); 36 SDL_DestroyWindow(window);
36 } 37 }
@@ -43,8 +44,6 @@ public:
43 SDL_GL_MakeCurrent(window, nullptr); 44 SDL_GL_MakeCurrent(window, nullptr);
44 } 45 }
45 46
46 void SwapBuffers() override {}
47
48private: 47private:
49 SDL_Window* window; 48 SDL_Window* window;
50 SDL_GLContext context; 49 SDL_GLContext context;
@@ -80,7 +79,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
80 return unsupported_ext.empty(); 79 return unsupported_ext.empty();
81} 80}
82 81
83EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscreen) { 82EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen)
83 : EmuWindow_SDL2{system, fullscreen} {
84 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 84 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
85 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 85 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
86 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 86 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
@@ -90,6 +90,7 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree
90 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); 90 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
91 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); 91 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
92 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); 92 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
93 SDL_GL_SetSwapInterval(0);
93 94
94 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, 95 std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname,
95 Common::g_scm_branch, Common::g_scm_desc); 96 Common::g_scm_branch, Common::g_scm_desc);
@@ -105,13 +106,22 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree
105 exit(1); 106 exit(1);
106 } 107 }
107 108
109 dummy_window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
110 SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
111
108 if (fullscreen) { 112 if (fullscreen) {
109 Fullscreen(); 113 Fullscreen();
110 } 114 }
111 gl_context = SDL_GL_CreateContext(render_window);
112 115
113 if (gl_context == nullptr) { 116 window_context = SDL_GL_CreateContext(render_window);
114 LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); 117 core_context = CreateSharedContext();
118
119 if (window_context == nullptr) {
120 LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context: {}", SDL_GetError());
121 exit(1);
122 }
123 if (core_context == nullptr) {
124 LOG_CRITICAL(Frontend, "Failed to create shared SDL2 GL context: {}", SDL_GetError());
115 exit(1); 125 exit(1);
116 } 126 }
117 127
@@ -128,28 +138,22 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscree
128 OnResize(); 138 OnResize();
129 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 139 OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
130 SDL_PumpEvents(); 140 SDL_PumpEvents();
131 SDL_GL_SetSwapInterval(false);
132 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, 141 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
133 Common::g_scm_desc); 142 Common::g_scm_desc);
134 Settings::LogSettings(); 143 Settings::LogSettings();
135
136 DoneCurrent();
137} 144}
138 145
139EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { 146EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
140 SDL_GL_DeleteContext(gl_context); 147 core_context.reset();
141} 148 SDL_GL_DeleteContext(window_context);
142
143void EmuWindow_SDL2_GL::SwapBuffers() {
144 SDL_GL_SwapWindow(render_window);
145} 149}
146 150
147void EmuWindow_SDL2_GL::MakeCurrent() { 151void EmuWindow_SDL2_GL::MakeCurrent() {
148 SDL_GL_MakeCurrent(render_window, gl_context); 152 core_context->MakeCurrent();
149} 153}
150 154
151void EmuWindow_SDL2_GL::DoneCurrent() { 155void EmuWindow_SDL2_GL::DoneCurrent() {
152 SDL_GL_MakeCurrent(render_window, nullptr); 156 core_context->DoneCurrent();
153} 157}
154 158
155void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, 159void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
@@ -161,3 +165,13 @@ void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, voi
161std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { 165std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
162 return std::make_unique<SDLGLContext>(); 166 return std::make_unique<SDLGLContext>();
163} 167}
168
169void EmuWindow_SDL2_GL::Present() {
170 SDL_GL_MakeCurrent(render_window, window_context);
171 SDL_GL_SetSwapInterval(Settings::values.use_vsync ? 1 : 0);
172 while (IsOpen()) {
173 system.Renderer().TryPresent(100);
174 SDL_GL_SwapWindow(render_window);
175 }
176 SDL_GL_MakeCurrent(render_window, nullptr);
177}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index c753085a8..b80669ff0 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -10,17 +10,12 @@
10 10
11class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 11class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
12public: 12public:
13 explicit EmuWindow_SDL2_GL(bool fullscreen); 13 explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);
14 ~EmuWindow_SDL2_GL(); 14 ~EmuWindow_SDL2_GL();
15 15
16 /// Swap buffers to display the next frame
17 void SwapBuffers() override;
18
19 /// Makes the graphics context current for the caller thread
20 void MakeCurrent() override; 16 void MakeCurrent() override;
21
22 /// Releases the GL context from the caller thread
23 void DoneCurrent() override; 17 void DoneCurrent() override;
18 void Present() override;
24 19
25 /// Ignored in OpenGL 20 /// Ignored in OpenGL
26 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, 21 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
@@ -29,10 +24,17 @@ public:
29 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; 24 std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
30 25
31private: 26private:
27 /// Fake hidden window for the core context
28 SDL_Window* dummy_window{};
29
32 /// Whether the GPU and driver supports the OpenGL extension required 30 /// Whether the GPU and driver supports the OpenGL extension required
33 bool SupportsRequiredGLExtensions(); 31 bool SupportsRequiredGLExtensions();
34 32
35 using SDL_GLContext = void*; 33 using SDL_GLContext = void*;
34
36 /// The OpenGL context associated with the window 35 /// The OpenGL context associated with the window
37 SDL_GLContext gl_context; 36 SDL_GLContext window_context;
37
38 /// The OpenGL context associated with the core
39 std::unique_ptr<Core::Frontend::GraphicsContext> core_context;
38}; 40};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index a203f0da9..abcc58165 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -15,7 +15,8 @@
15#include "core/settings.h" 15#include "core/settings.h"
16#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" 16#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
17 17
18EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) { 18EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
19 : EmuWindow_SDL2{system, fullscreen} {
19 if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { 20 if (SDL_Vulkan_LoadLibrary(nullptr) != 0) {
20 LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); 21 LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError());
21 exit(EXIT_FAILURE); 22 exit(EXIT_FAILURE);
@@ -110,8 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
110 vkDestroyInstance(vk_instance, nullptr); 111 vkDestroyInstance(vk_instance, nullptr);
111} 112}
112 113
113void EmuWindow_SDL2_VK::SwapBuffers() {}
114
115void EmuWindow_SDL2_VK::MakeCurrent() { 114void EmuWindow_SDL2_VK::MakeCurrent() {
116 // Unused on Vulkan 115 // Unused on Vulkan
117} 116}
@@ -160,3 +159,7 @@ bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanc
160 return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); 159 return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation");
161 }) != layers.end(); 160 }) != layers.end();
162} 161}
162
163void EmuWindow_SDL2_VK::Present() {
164 // TODO (bunnei): ImplementMe
165}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index 2a7c06a24..1eb8c0868 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -10,19 +10,12 @@
10 10
11class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 11class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
12public: 12public:
13 explicit EmuWindow_SDL2_VK(bool fullscreen); 13 explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
14 ~EmuWindow_SDL2_VK(); 14 ~EmuWindow_SDL2_VK();
15 15
16 /// Swap buffers to display the next frame
17 void SwapBuffers() override;
18
19 /// Makes the graphics context current for the caller thread
20 void MakeCurrent() override; 16 void MakeCurrent() override;
21
22 /// Releases the GL context from the caller thread
23 void DoneCurrent() override; 17 void DoneCurrent() override;
24 18 void Present() override;
25 /// Retrieves Vulkan specific handlers from the window
26 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, 19 void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
27 void* surface) const override; 20 void* surface) const override;
28 21
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 325795321..babf4c3a4 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -177,14 +177,16 @@ int main(int argc, char** argv) {
177 Settings::values.use_gdbstub = use_gdbstub; 177 Settings::values.use_gdbstub = use_gdbstub;
178 Settings::Apply(); 178 Settings::Apply();
179 179
180 Core::System& system{Core::System::GetInstance()};
181
180 std::unique_ptr<EmuWindow_SDL2> emu_window; 182 std::unique_ptr<EmuWindow_SDL2> emu_window;
181 switch (Settings::values.renderer_backend) { 183 switch (Settings::values.renderer_backend) {
182 case Settings::RendererBackend::OpenGL: 184 case Settings::RendererBackend::OpenGL:
183 emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen); 185 emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen);
184 break; 186 break;
185 case Settings::RendererBackend::Vulkan: 187 case Settings::RendererBackend::Vulkan:
186#ifdef HAS_VULKAN 188#ifdef HAS_VULKAN
187 emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen); 189 emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen);
188 break; 190 break;
189#else 191#else
190 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); 192 LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
@@ -192,12 +194,6 @@ int main(int argc, char** argv) {
192#endif 194#endif
193 } 195 }
194 196
195 if (!Settings::values.use_multi_core) {
196 // Single core mode must acquire OpenGL context for entire emulation session
197 emu_window->MakeCurrent();
198 }
199
200 Core::System& system{Core::System::GetInstance()};
201 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 197 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
202 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 198 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
203 system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); 199 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
@@ -234,12 +230,23 @@ int main(int argc, char** argv) {
234 230
235 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); 231 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
236 232
237 emu_window->MakeCurrent();
238 system.Renderer().Rasterizer().LoadDiskResources(); 233 system.Renderer().Rasterizer().LoadDiskResources();
239 234
235 // Acquire render context for duration of the thread if this is the rendering thread
236 if (!Settings::values.use_asynchronous_gpu_emulation) {
237 emu_window->MakeCurrent();
238 }
239 SCOPE_EXIT({
240 if (!Settings::values.use_asynchronous_gpu_emulation) {
241 emu_window->DoneCurrent();
242 }
243 });
244
245 std::thread render_thread([&emu_window] { emu_window->Present(); });
240 while (emu_window->IsOpen()) { 246 while (emu_window->IsOpen()) {
241 system.RunLoop(); 247 system.RunLoop();
242 } 248 }
249 render_thread.join();
243 250
244 system.Shutdown(); 251 system.Shutdown();
245 252
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
index f2cc4a797..a1bdb1a12 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
@@ -112,10 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
112 SDL_Quit(); 112 SDL_Quit();
113} 113}
114 114
115void EmuWindow_SDL2_Hide::SwapBuffers() {
116 SDL_GL_SwapWindow(render_window);
117}
118
119void EmuWindow_SDL2_Hide::PollEvents() {} 115void EmuWindow_SDL2_Hide::PollEvents() {}
120 116
121void EmuWindow_SDL2_Hide::MakeCurrent() { 117void EmuWindow_SDL2_Hide::MakeCurrent() {
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
index c7fccc002..b13e15309 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
@@ -13,9 +13,6 @@ public:
13 explicit EmuWindow_SDL2_Hide(); 13 explicit EmuWindow_SDL2_Hide();
14 ~EmuWindow_SDL2_Hide(); 14 ~EmuWindow_SDL2_Hide();
15 15
16 /// Swap buffers to display the next frame
17 void SwapBuffers() override;
18
19 /// Polls window events 16 /// Polls window events
20 void PollEvents() override; 17 void PollEvents() override;
21 18