diff options
83 files changed, 2167 insertions, 784 deletions
| @@ -1,6 +1,6 @@ | |||
| 1 | yuzu emulator | 1 | yuzu emulator |
| 2 | ============= | 2 | ============= |
| 3 | [](https://travis-ci.org/yuzu-emu/yuzu) | 3 | [](https://travis-ci.com/yuzu-emu/yuzu) |
| 4 | [](https://dev.azure.com/yuzu-emu/yuzu/) | 4 | [](https://dev.azure.com/yuzu-emu/yuzu/) |
| 5 | 5 | ||
| 6 | yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/). | 6 | yuzu 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 | ||
| 22 | Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted. | 22 | Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted. |
| 23 | 23 | ||
| 24 | If 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. | 24 | If 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 | ||
| 15 | namespace AudioCore { | 16 | namespace AudioCore { |
| 16 | 17 | ||
| 17 | constexpr std::array<s16, 512> curve_lut0 = { | 18 | constexpr 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 | ||
| 59 | constexpr std::array<s16, 512> curve_lut1 = { | 60 | constexpr 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 | ||
| 101 | constexpr std::array<s16, 512> curve_lut2 = { | 102 | constexpr 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 | ||
| 11 | namespace AudioCore { | 12 | namespace AudioCore { |
| 12 | 13 | ||
| 13 | struct InterpolationState { | 14 | struct 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 | ||
| 596 | if (ARCHITECTURE_x86_64) | 596 | if (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) |
| 602 | endif() | 606 | endif() |
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 | |||
| 20 | namespace Core { | ||
| 21 | |||
| 22 | class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { | ||
| 23 | public: | ||
| 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 | |||
| 96 | std::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 | |||
| 107 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_32, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); | ||
| 108 | |||
| 109 | void ARM_Dynarmic_32::Run() { | ||
| 110 | MICROPROFILE_SCOPE(ARM_Jit_Dynarmic_32); | ||
| 111 | jit->Run(); | ||
| 112 | } | ||
| 113 | |||
| 114 | void ARM_Dynarmic_32::Step() { | ||
| 115 | cb->InterpreterFallback(jit->Regs()[15], 1); | ||
| 116 | } | ||
| 117 | |||
| 118 | ARM_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 | |||
| 124 | ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; | ||
| 125 | |||
| 126 | void ARM_Dynarmic_32::SetPC(u64 pc) { | ||
| 127 | jit->Regs()[15] = static_cast<u32>(pc); | ||
| 128 | } | ||
| 129 | |||
| 130 | u64 ARM_Dynarmic_32::GetPC() const { | ||
| 131 | return jit->Regs()[15]; | ||
| 132 | } | ||
| 133 | |||
| 134 | u64 ARM_Dynarmic_32::GetReg(int index) const { | ||
| 135 | return jit->Regs()[index]; | ||
| 136 | } | ||
| 137 | |||
| 138 | void ARM_Dynarmic_32::SetReg(int index, u64 value) { | ||
| 139 | jit->Regs()[index] = static_cast<u32>(value); | ||
| 140 | } | ||
| 141 | |||
| 142 | u128 ARM_Dynarmic_32::GetVectorReg(int index) const { | ||
| 143 | return {}; | ||
| 144 | } | ||
| 145 | |||
| 146 | void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {} | ||
| 147 | |||
| 148 | u32 ARM_Dynarmic_32::GetPSTATE() const { | ||
| 149 | return jit->Cpsr(); | ||
| 150 | } | ||
| 151 | |||
| 152 | void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) { | ||
| 153 | jit->SetCpsr(cpsr); | ||
| 154 | } | ||
| 155 | |||
| 156 | u64 ARM_Dynarmic_32::GetTlsAddress() const { | ||
| 157 | return CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)]; | ||
| 158 | } | ||
| 159 | |||
| 160 | void ARM_Dynarmic_32::SetTlsAddress(VAddr address) { | ||
| 161 | CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)] = static_cast<u32>(address); | ||
| 162 | } | ||
| 163 | |||
| 164 | u64 ARM_Dynarmic_32::GetTPIDR_EL0() const { | ||
| 165 | return cb->tpidr_el0; | ||
| 166 | } | ||
| 167 | |||
| 168 | void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) { | ||
| 169 | cb->tpidr_el0 = value; | ||
| 170 | } | ||
| 171 | |||
| 172 | void 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 | |||
| 179 | void 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 | |||
| 186 | void ARM_Dynarmic_32::PrepareReschedule() { | ||
| 187 | jit->HaltExecution(); | ||
| 188 | } | ||
| 189 | |||
| 190 | void ARM_Dynarmic_32::ClearInstructionCache() { | ||
| 191 | jit->ClearCache(); | ||
| 192 | } | ||
| 193 | |||
| 194 | void ARM_Dynarmic_32::ClearExclusiveState() {} | ||
| 195 | |||
| 196 | void 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 | |||
| 18 | namespace Memory { | ||
| 19 | class Memory; | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace Core { | ||
| 23 | |||
| 24 | class DynarmicCallbacks32; | ||
| 25 | class DynarmicExclusiveMonitor; | ||
| 26 | class System; | ||
| 27 | |||
| 28 | class ARM_Dynarmic_32 final : public ARM_Interface { | ||
| 29 | public: | ||
| 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 | |||
| 60 | private: | ||
| 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 | ||
| 26 | using Vector = Dynarmic::A64::Vector; | 26 | using Vector = Dynarmic::A64::Vector; |
| 27 | 27 | ||
| 28 | class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks { | 28 | class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { |
| 29 | public: | 29 | public: |
| 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 | ||
| 135 | std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& page_table, | 135 | std::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 | ||
| 165 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); | 165 | MICROPROFILE_DEFINE(ARM_Jit_Dynarmic_64, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64)); |
| 166 | 166 | ||
| 167 | void ARM_Dynarmic::Run() { | 167 | void 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 | ||
| 173 | void ARM_Dynarmic::Step() { | 173 | void ARM_Dynarmic_64::Step() { |
| 174 | cb->InterpreterFallback(jit->GetPC(), 1); | 174 | cb->InterpreterFallback(jit->GetPC(), 1); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, | 177 | ARM_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 | ||
| 184 | ARM_Dynarmic::~ARM_Dynarmic() = default; | 184 | ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; |
| 185 | 185 | ||
| 186 | void ARM_Dynarmic::SetPC(u64 pc) { | 186 | void ARM_Dynarmic_64::SetPC(u64 pc) { |
| 187 | jit->SetPC(pc); | 187 | jit->SetPC(pc); |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | u64 ARM_Dynarmic::GetPC() const { | 190 | u64 ARM_Dynarmic_64::GetPC() const { |
| 191 | return jit->GetPC(); | 191 | return jit->GetPC(); |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | u64 ARM_Dynarmic::GetReg(int index) const { | 194 | u64 ARM_Dynarmic_64::GetReg(int index) const { |
| 195 | return jit->GetRegister(index); | 195 | return jit->GetRegister(index); |
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | void ARM_Dynarmic::SetReg(int index, u64 value) { | 198 | void ARM_Dynarmic_64::SetReg(int index, u64 value) { |
| 199 | jit->SetRegister(index, value); | 199 | jit->SetRegister(index, value); |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | u128 ARM_Dynarmic::GetVectorReg(int index) const { | 202 | u128 ARM_Dynarmic_64::GetVectorReg(int index) const { |
| 203 | return jit->GetVector(index); | 203 | return jit->GetVector(index); |
| 204 | } | 204 | } |
| 205 | 205 | ||
| 206 | void ARM_Dynarmic::SetVectorReg(int index, u128 value) { | 206 | void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) { |
| 207 | jit->SetVector(index, value); | 207 | jit->SetVector(index, value); |
| 208 | } | 208 | } |
| 209 | 209 | ||
| 210 | u32 ARM_Dynarmic::GetPSTATE() const { | 210 | u32 ARM_Dynarmic_64::GetPSTATE() const { |
| 211 | return jit->GetPstate(); | 211 | return jit->GetPstate(); |
| 212 | } | 212 | } |
| 213 | 213 | ||
| 214 | void ARM_Dynarmic::SetPSTATE(u32 pstate) { | 214 | void ARM_Dynarmic_64::SetPSTATE(u32 pstate) { |
| 215 | jit->SetPstate(pstate); | 215 | jit->SetPstate(pstate); |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | u64 ARM_Dynarmic::GetTlsAddress() const { | 218 | u64 ARM_Dynarmic_64::GetTlsAddress() const { |
| 219 | return cb->tpidrro_el0; | 219 | return cb->tpidrro_el0; |
| 220 | } | 220 | } |
| 221 | 221 | ||
| 222 | void ARM_Dynarmic::SetTlsAddress(VAddr address) { | 222 | void ARM_Dynarmic_64::SetTlsAddress(VAddr address) { |
| 223 | cb->tpidrro_el0 = address; | 223 | cb->tpidrro_el0 = address; |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | u64 ARM_Dynarmic::GetTPIDR_EL0() const { | 226 | u64 ARM_Dynarmic_64::GetTPIDR_EL0() const { |
| 227 | return cb->tpidr_el0; | 227 | return cb->tpidr_el0; |
| 228 | } | 228 | } |
| 229 | 229 | ||
| 230 | void ARM_Dynarmic::SetTPIDR_EL0(u64 value) { | 230 | void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) { |
| 231 | cb->tpidr_el0 = value; | 231 | cb->tpidr_el0 = value; |
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { | 234 | void 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 | ||
| 245 | void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { | 245 | void 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 | ||
| 256 | void ARM_Dynarmic::PrepareReschedule() { | 256 | void ARM_Dynarmic_64::PrepareReschedule() { |
| 257 | jit->HaltExecution(); | 257 | jit->HaltExecution(); |
| 258 | } | 258 | } |
| 259 | 259 | ||
| 260 | void ARM_Dynarmic::ClearInstructionCache() { | 260 | void ARM_Dynarmic_64::ClearInstructionCache() { |
| 261 | jit->ClearCache(); | 261 | jit->ClearCache(); |
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | void ARM_Dynarmic::ClearExclusiveState() { | 264 | void ARM_Dynarmic_64::ClearExclusiveState() { |
| 265 | jit->ClearExclusiveState(); | 265 | jit->ClearExclusiveState(); |
| 266 | } | 266 | } |
| 267 | 267 | ||
| 268 | void ARM_Dynarmic::PageTableChanged(Common::PageTable& page_table, | 268 | void 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 | ||
| 273 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count) | 280 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory, std::size_t core_count) |
| 274 | : monitor(core_count), memory{memory_} {} | 281 | : monitor(core_count), memory{memory} {} |
| 275 | 282 | ||
| 276 | DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; | 283 | DynarmicExclusiveMonitor::~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 | ||
| 19 | namespace Core { | 22 | namespace Core { |
| 20 | 23 | ||
| 21 | class ARM_Dynarmic_Callbacks; | 24 | class DynarmicCallbacks64; |
| 22 | class DynarmicExclusiveMonitor; | 25 | class DynarmicExclusiveMonitor; |
| 23 | class System; | 26 | class System; |
| 24 | 27 | ||
| 25 | class ARM_Dynarmic final : public ARM_Interface { | 28 | class ARM_Dynarmic_64 final : public ARM_Interface { |
| 26 | public: | 29 | public: |
| 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 | ||
| 55 | private: | 60 | private: |
| 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 | ||
| 68 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | 78 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { |
| 69 | public: | 79 | public: |
| 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 | ||
| 82 | private: | 92 | private: |
| 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 | |||
| 7 | using Callback = Dynarmic::A32::Coprocessor::Callback; | ||
| 8 | using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord; | ||
| 9 | using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords; | ||
| 10 | |||
| 11 | std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigned opc1, | ||
| 12 | CoprocReg CRd, CoprocReg CRn, | ||
| 13 | CoprocReg CRm, unsigned opc2) { | ||
| 14 | return {}; | ||
| 15 | } | ||
| 16 | |||
| 17 | CallbackOrAccessOneWord 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 | |||
| 46 | CallbackOrAccessTwoWords DynarmicCP15::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) { | ||
| 47 | return {}; | ||
| 48 | } | ||
| 49 | |||
| 50 | CallbackOrAccessOneWord 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 | |||
| 68 | CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { | ||
| 69 | return {}; | ||
| 70 | } | ||
| 71 | |||
| 72 | std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd, | ||
| 73 | std::optional<u8> option) { | ||
| 74 | return {}; | ||
| 75 | } | ||
| 76 | |||
| 77 | std::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 | |||
| 13 | enum 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 | |||
| 130 | class DynarmicCP15 final : public Dynarmic::A32::Coprocessor { | ||
| 131 | public: | ||
| 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 | |||
| 150 | private: | ||
| 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 | ||
| 191 | void ARM_Unicorn::SaveContext(ThreadContext& ctx) { | 191 | void 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 | ||
| 218 | void ARM_Unicorn::LoadContext(const ThreadContext& ctx) { | 218 | void 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 | |||
| 44 | private: | 47 | private: |
| 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 { | |||
| 29 | struct FramebufferLayout { | 29 | struct 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 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context) | ||
| 11 | : context{context} { | ||
| 12 | context.MakeCurrent(); | ||
| 13 | } | ||
| 14 | ScopeAcquireContext::~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 | ||
| 9 | namespace Core::Frontend { | 9 | namespace Core::Frontend { |
| 10 | 10 | ||
| 11 | class EmuWindow; | 11 | class 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 |
| 14 | class ScopeAcquireWindowContext : NonCopyable { | 14 | class ScopeAcquireContext : NonCopyable { |
| 15 | public: | 15 | public: |
| 16 | explicit ScopeAcquireWindowContext(Core::Frontend::EmuWindow& window); | 16 | explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context); |
| 17 | ~ScopeAcquireWindowContext(); | 17 | ~ScopeAcquireContext(); |
| 18 | 18 | ||
| 19 | private: | 19 | private: |
| 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 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | ScopeAcquireWindowContext::ScopeAcquireWindowContext(Core::Frontend::EmuWindow& emu_window_) | ||
| 11 | : emu_window{emu_window_} { | ||
| 12 | emu_window.MakeCurrent(); | ||
| 13 | } | ||
| 14 | ScopeAcquireWindowContext::~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 | ||
| 32 | PhysicalCore::~PhysicalCore() = default; | 37 | PhysicalCore::~PhysicalCore() = default; |
| @@ -48,4 +53,12 @@ void PhysicalCore::Shutdown() { | |||
| 48 | scheduler->Shutdown(); | 53 | scheduler->Shutdown(); |
| 49 | } | 54 | } |
| 50 | 55 | ||
| 56 | void 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 | |||
| 71 | private: | 73 | private: |
| 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 | ||
| 386 | Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id) | 386 | Scheduler::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 | ||
| 389 | Scheduler::~Scheduler() = default; | 389 | Scheduler::~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 | ||
| 182 | class Scheduler final { | 182 | class Scheduler final { |
| 183 | public: | 183 | public: |
| 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 | ||
| 190 | static 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 | |||
| 190 | static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) { | 197 | static 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 | ||
| 381 | static 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. |
| 375 | static ResultCode SendSyncRequest(Core::System& system, Handle handle) { | 388 | static 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 | ||
| 406 | static 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. |
| 394 | static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) { | 411 | static 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 | ||
| 425 | static 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. |
| 409 | static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) { | 437 | static 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 | ||
| 510 | static 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 |
| 483 | static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) { | 517 | static 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 | ||
| 954 | static 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 |
| 921 | static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { | 967 | static 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 | ||
| 1137 | static 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 |
| 1092 | static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) { | 1142 | static 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 | ||
| 1312 | static 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 | |||
| 1262 | static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, | 1317 | static 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 | ||
| 1733 | static 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) |
| 1679 | static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, | 1738 | static 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 | ||
| 1822 | static 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. |
| 1764 | static ResultCode ResetSignal(Core::System& system, Handle handle) { | 1827 | static 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 | ||
| 2320 | static const FunctionDef SVC_Table[] = { | 2383 | static 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 | |||
| 2510 | static 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 | ||
| 2451 | static const FunctionDef* GetSVCInfo(u32 func_num) { | 2641 | static 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 | |||
| 2649 | static 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 | ||
| 2459 | MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); | 2657 | MICROPROFILE_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 | ||
| 18 | static 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 | ||
| 31 | static 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 | ||
| 30 | template <ResultCode func(Core::System&, u64)> | 38 | template <ResultCode func(Core::System&, u64)> |
| 31 | void SvcWrap(Core::System& system) { | 39 | void 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 | ||
| 35 | template <ResultCode func(Core::System&, u64, u64)> | 43 | template <ResultCode func(Core::System&, u64, u64)> |
| 36 | void SvcWrap(Core::System& system) { | 44 | void 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 | ||
| 40 | template <ResultCode func(Core::System&, u32)> | 48 | template <ResultCode func(Core::System&, u32)> |
| 41 | void SvcWrap(Core::System& system) { | 49 | void 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 | ||
| 45 | template <ResultCode func(Core::System&, u32, u32)> | 53 | template <ResultCode func(Core::System&, u32, u32)> |
| 46 | void SvcWrap(Core::System& system) { | 54 | void 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 | ||
| 52 | template <ResultCode func(Core::System&, u32, u64, u64, u64)> | 60 | template <ResultCode func(Core::System&, u32, u64, u64, u64)> |
| 53 | void SvcWrap(Core::System& system) { | 61 | void 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 | ||
| 59 | template <ResultCode func(Core::System&, u32*)> | 67 | template <ResultCode func(Core::System&, u32*)> |
| 60 | void SvcWrap(Core::System& system) { | 68 | void SvcWrap64(Core::System& system) { |
| 61 | u32 param = 0; | 69 | u32 param = 0; |
| 62 | const u32 retval = func(system, ¶m).raw; | 70 | const u32 retval = func(system, ¶m).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 | ||
| 67 | template <ResultCode func(Core::System&, u32*, u32)> | 75 | template <ResultCode func(Core::System&, u32*, u32)> |
| 68 | void SvcWrap(Core::System& system) { | 76 | void SvcWrap64(Core::System& system) { |
| 69 | u32 param_1 = 0; | 77 | u32 param_1 = 0; |
| 70 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; | 78 | const u32 retval = func(system, ¶m_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 | ||
| 75 | template <ResultCode func(Core::System&, u32*, u32*)> | 83 | template <ResultCode func(Core::System&, u32*, u32*)> |
| 76 | void SvcWrap(Core::System& system) { | 84 | void 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, ¶m_1, ¶m_2).raw; | 87 | const u32 retval = func(system, ¶m_1, ¶m_2).raw; |
| @@ -86,7 +94,7 @@ void SvcWrap(Core::System& system) { | |||
| 86 | } | 94 | } |
| 87 | 95 | ||
| 88 | template <ResultCode func(Core::System&, u32*, u64)> | 96 | template <ResultCode func(Core::System&, u32*, u64)> |
| 89 | void SvcWrap(Core::System& system) { | 97 | void SvcWrap64(Core::System& system) { |
| 90 | u32 param_1 = 0; | 98 | u32 param_1 = 0; |
| 91 | const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; | 99 | const u32 retval = func(system, ¶m_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 | ||
| 96 | template <ResultCode func(Core::System&, u32*, u64, u32)> | 104 | template <ResultCode func(Core::System&, u32*, u64, u32)> |
| 97 | void SvcWrap(Core::System& system) { | 105 | void 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, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw; | 108 | func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw; |
| @@ -104,7 +112,7 @@ void SvcWrap(Core::System& system) { | |||
| 104 | } | 112 | } |
| 105 | 113 | ||
| 106 | template <ResultCode func(Core::System&, u64*, u32)> | 114 | template <ResultCode func(Core::System&, u64*, u32)> |
| 107 | void SvcWrap(Core::System& system) { | 115 | void SvcWrap64(Core::System& system) { |
| 108 | u64 param_1 = 0; | 116 | u64 param_1 = 0; |
| 109 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; | 117 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; |
| 110 | 118 | ||
| @@ -113,12 +121,12 @@ void SvcWrap(Core::System& system) { | |||
| 113 | } | 121 | } |
| 114 | 122 | ||
| 115 | template <ResultCode func(Core::System&, u64, u32)> | 123 | template <ResultCode func(Core::System&, u64, u32)> |
| 116 | void SvcWrap(Core::System& system) { | 124 | void 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 | ||
| 120 | template <ResultCode func(Core::System&, u64*, u64)> | 128 | template <ResultCode func(Core::System&, u64*, u64)> |
| 121 | void SvcWrap(Core::System& system) { | 129 | void SvcWrap64(Core::System& system) { |
| 122 | u64 param_1 = 0; | 130 | u64 param_1 = 0; |
| 123 | const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; | 131 | const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; |
| 124 | 132 | ||
| @@ -127,7 +135,7 @@ void SvcWrap(Core::System& system) { | |||
| 127 | } | 135 | } |
| 128 | 136 | ||
| 129 | template <ResultCode func(Core::System&, u64*, u32, u32)> | 137 | template <ResultCode func(Core::System&, u64*, u32, u32)> |
| 130 | void SvcWrap(Core::System& system) { | 138 | void SvcWrap64(Core::System& system) { |
| 131 | u64 param_1 = 0; | 139 | u64 param_1 = 0; |
| 132 | const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1)), | 140 | const u32 retval = func(system, ¶m_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 | ||
| 140 | template <ResultCode func(Core::System&, u32, u64)> | 148 | template <ResultCode func(Core::System&, u32, u64)> |
| 141 | void SvcWrap(Core::System& system) { | 149 | void 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 | ||
| 145 | template <ResultCode func(Core::System&, u32, u32, u64)> | 153 | template <ResultCode func(Core::System&, u32, u32, u64)> |
| 146 | void SvcWrap(Core::System& system) { | 154 | void 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 | ||
| 152 | template <ResultCode func(Core::System&, u32, u32*, u64*)> | 160 | template <ResultCode func(Core::System&, u32, u32*, u64*)> |
| 153 | void SvcWrap(Core::System& system) { | 161 | void 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)), ¶m_1, ¶m_2); | 164 | const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); |
| @@ -161,54 +169,54 @@ void SvcWrap(Core::System& system) { | |||
| 161 | } | 169 | } |
| 162 | 170 | ||
| 163 | template <ResultCode func(Core::System&, u64, u64, u32, u32)> | 171 | template <ResultCode func(Core::System&, u64, u64, u32, u32)> |
| 164 | void SvcWrap(Core::System& system) { | 172 | void 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 | ||
| 170 | template <ResultCode func(Core::System&, u64, u64, u32, u64)> | 178 | template <ResultCode func(Core::System&, u64, u64, u32, u64)> |
| 171 | void SvcWrap(Core::System& system) { | 179 | void 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 | ||
| 177 | template <ResultCode func(Core::System&, u32, u64, u32)> | 185 | template <ResultCode func(Core::System&, u32, u64, u32)> |
| 178 | void SvcWrap(Core::System& system) { | 186 | void 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 | ||
| 184 | template <ResultCode func(Core::System&, u64, u64, u64)> | 192 | template <ResultCode func(Core::System&, u64, u64, u64)> |
| 185 | void SvcWrap(Core::System& system) { | 193 | void 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 | ||
| 189 | template <ResultCode func(Core::System&, u64, u64, u32)> | 197 | template <ResultCode func(Core::System&, u64, u64, u32)> |
| 190 | void SvcWrap(Core::System& system) { | 198 | void 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 | ||
| 196 | template <ResultCode func(Core::System&, u32, u64, u64, u32)> | 204 | template <ResultCode func(Core::System&, u32, u64, u64, u32)> |
| 197 | void SvcWrap(Core::System& system) { | 205 | void 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 | ||
| 203 | template <ResultCode func(Core::System&, u32, u64, u64)> | 211 | template <ResultCode func(Core::System&, u32, u64, u64)> |
| 204 | void SvcWrap(Core::System& system) { | 212 | void 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 | ||
| 210 | template <ResultCode func(Core::System&, u32*, u64, u64, s64)> | 218 | template <ResultCode func(Core::System&, u32*, u64, u64, s64)> |
| 211 | void SvcWrap(Core::System& system) { | 219 | void SvcWrap64(Core::System& system) { |
| 212 | u32 param_1 = 0; | 220 | u32 param_1 = 0; |
| 213 | const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), | 221 | const u32 retval = func(system, ¶m_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 | ||
| 221 | template <ResultCode func(Core::System&, u64, u64, u32, s64)> | 229 | template <ResultCode func(Core::System&, u64, u64, u32, s64)> |
| 222 | void SvcWrap(Core::System& system) { | 230 | void 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 | ||
| 228 | template <ResultCode func(Core::System&, u64*, u64, u64, u64)> | 236 | template <ResultCode func(Core::System&, u64*, u64, u64, u64)> |
| 229 | void SvcWrap(Core::System& system) { | 237 | void 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, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw; | 240 | func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw; |
| @@ -236,7 +244,7 @@ void SvcWrap(Core::System& system) { | |||
| 236 | } | 244 | } |
| 237 | 245 | ||
| 238 | template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)> | 246 | template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)> |
| 239 | void SvcWrap(Core::System& system) { | 247 | void SvcWrap64(Core::System& system) { |
| 240 | u32 param_1 = 0; | 248 | u32 param_1 = 0; |
| 241 | const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3), | 249 | const u32 retval = func(system, ¶m_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 | ||
| 249 | template <ResultCode func(Core::System&, u32*, u64, u64, u32)> | 257 | template <ResultCode func(Core::System&, u32*, u64, u64, u32)> |
| 250 | void SvcWrap(Core::System& system) { | 258 | void SvcWrap64(Core::System& system) { |
| 251 | u32 param_1 = 0; | 259 | u32 param_1 = 0; |
| 252 | const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), | 260 | const u32 retval = func(system, ¶m_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 | ||
| 260 | template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> | 268 | template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> |
| 261 | void SvcWrap(Core::System& system) { | 269 | void SvcWrap64(Core::System& system) { |
| 262 | u32 param_1 = 0; | 270 | u32 param_1 = 0; |
| 263 | const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), | 271 | const u32 retval = func(system, ¶m_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 | ||
| 271 | template <ResultCode func(Core::System&, u64, u32, s32, s64)> | 279 | template <ResultCode func(Core::System&, u64, u32, s32, s64)> |
| 272 | void SvcWrap(Core::System& system) { | 280 | void 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 | ||
| 278 | template <ResultCode func(Core::System&, u64, u32, s32, s32)> | 286 | template <ResultCode func(Core::System&, u64, u32, s32, s32)> |
| 279 | void SvcWrap(Core::System& system) { | 287 | void 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 | ||
| 288 | template <u32 func(Core::System&)> | 296 | template <u32 func(Core::System&)> |
| 289 | void SvcWrap(Core::System& system) { | 297 | void 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 | ||
| 296 | template <u64 func(Core::System&)> | 304 | template <u64 func(Core::System&)> |
| 297 | void SvcWrap(Core::System& system) { | 305 | void 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 | ||
| 304 | template <void func(Core::System&)> | 312 | template <void func(Core::System&)> |
| 305 | void SvcWrap(Core::System& system) { | 313 | void SvcWrap64(Core::System& system) { |
| 306 | func(system); | 314 | func(system); |
| 307 | } | 315 | } |
| 308 | 316 | ||
| 309 | template <void func(Core::System&, u32)> | 317 | template <void func(Core::System&, u32)> |
| 310 | void SvcWrap(Core::System& system) { | 318 | void 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 | ||
| 314 | template <void func(Core::System&, u32, u64, u64, u64)> | 322 | template <void func(Core::System&, u32, u64, u64, u64)> |
| 315 | void SvcWrap(Core::System& system) { | 323 | void 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 | ||
| 320 | template <void func(Core::System&, s64)> | 328 | template <void func(Core::System&, s64)> |
| 321 | void SvcWrap(Core::System& system) { | 329 | void 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 | ||
| 325 | template <void func(Core::System&, u64, s32)> | 333 | template <void func(Core::System&, u64, s32)> |
| 326 | void SvcWrap(Core::System& system) { | 334 | void 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 | ||
| 330 | template <void func(Core::System&, u64, u64)> | 338 | template <void func(Core::System&, u64, u64)> |
| 331 | void SvcWrap(Core::System& system) { | 339 | void 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 | ||
| 335 | template <void func(Core::System&, u64, u64, u64)> | 343 | template <void func(Core::System&, u64, u64, u64)> |
| 336 | void SvcWrap(Core::System& system) { | 344 | void 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 | ||
| 340 | template <void func(Core::System&, u32, u64, u64)> | 348 | template <void func(Core::System&, u32, u64, u64)> |
| 341 | void SvcWrap(Core::System& system) { | 349 | void 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 | ||
| 354 | template <ResultCode func(Core::System&, u32, u32, u32)> | ||
| 355 | void 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 | ||
| 361 | template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)> | ||
| 362 | void SvcWrap32(Core::System& system) { | ||
| 363 | u32 param_1 = 0; | ||
| 364 | u32 param_2 = 0; | ||
| 365 | |||
| 366 | const u32 retval = func(system, ¶m_1, ¶m_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 | ||
| 376 | template <ResultCode func(Core::System&, u32*, u32)> | ||
| 377 | void SvcWrap32(Core::System& system) { | ||
| 378 | u32 param_1 = 0; | ||
| 379 | const u32 retval = func(system, ¶m_1, Param32(system, 1)).raw; | ||
| 380 | system.CurrentArmInterface().SetReg(1, param_1); | ||
| 381 | FuncReturn(system, retval); | ||
| 382 | } | ||
| 383 | |||
| 384 | // Used by GetThreadId32 | ||
| 385 | template <ResultCode func(Core::System&, u32*, u32*, u32)> | ||
| 386 | void SvcWrap32(Core::System& system) { | ||
| 387 | u32 param_1 = 0; | ||
| 388 | u32 param_2 = 0; | ||
| 389 | |||
| 390 | const u32 retval = func(system, ¶m_1, ¶m_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 | ||
| 397 | template <void func(Core::System&, u32, s32)> | ||
| 398 | void 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 | ||
| 403 | template <ResultCode func(Core::System&, u32)> | ||
| 404 | void SvcWrap32(Core::System& system) { | ||
| 405 | FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); | ||
| 406 | } | ||
| 407 | |||
| 408 | // Used by WaitSynchronization32 | ||
| 409 | template <ResultCode func(Core::System&, u32, u32, s32, u32, Handle*)> | ||
| 410 | void 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), ¶m_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 | /** | 136 | static 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 | } |
| 143 | static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top, | 143 | |
| 144 | VAddr entry_point, u64 arg) { | 144 | static 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 | ||
| 215 | void Thread::SetWaitSynchronizationResult(ResultCode result) { | 216 | void 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 | ||
| 219 | void Thread::SetWaitSynchronizationOutput(s32 output) { | 221 | void 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 | ||
| 223 | s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { | 226 | s32 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 | ||
| 675 | void 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 | |||
| 675 | void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { | 686 | void 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 | */ |
| 32 | std::optional<Type> Validate(u8* data, std::size_t size) { | 32 | std::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 | ||
| 37 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 37 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 38 | 38 | ||
| 39 | using Tegra::Engines::ShaderType; | ||
| 39 | using VideoCore::Surface::PixelFormat; | 40 | using VideoCore::Surface::PixelFormat; |
| 40 | using VideoCore::Surface::SurfaceTarget; | 41 | using VideoCore::Surface::SurfaceTarget; |
| 41 | using VideoCore::Surface::SurfaceType; | 42 | using VideoCore::Surface::SurfaceType; |
| @@ -56,8 +57,7 @@ namespace { | |||
| 56 | 57 | ||
| 57 | template <typename Engine, typename Entry> | 58 | template <typename Engine, typename Entry> |
| 58 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, | 59 | Tegra::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 | ||
| 16 | namespace OpenGL { | 16 | namespace OpenGL { |
| 17 | 17 | ||
| 18 | void OGLRenderbuffer::Create() { | ||
| 19 | if (handle != 0) | ||
| 20 | return; | ||
| 21 | |||
| 22 | MICROPROFILE_SCOPE(OpenGL_ResourceCreation); | ||
| 23 | glGenRenderbuffers(1, &handle); | ||
| 24 | } | ||
| 25 | |||
| 26 | void 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 | |||
| 18 | void OGLTexture::Create(GLenum target) { | 36 | void 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 | ||
| 12 | namespace OpenGL { | 12 | namespace OpenGL { |
| 13 | 13 | ||
| 14 | class OGLRenderbuffer : private NonCopyable { | ||
| 15 | public: | ||
| 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 | |||
| 14 | class OGLTexture : private NonCopyable { | 39 | class OGLTexture : private NonCopyable { |
| 15 | public: | 40 | public: |
| 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 | ||
| 426 | void OpenGLState::ApplyRenderBuffer() { | ||
| 427 | if (cur_state.renderbuffer != renderbuffer) { | ||
| 428 | cur_state.renderbuffer = renderbuffer; | ||
| 429 | glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 426 | void OpenGLState::ApplyTextures() { | 433 | void 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 | ||
| 483 | void OpenGLState::EmulateViewportWithScissor() { | 491 | void OpenGLState::EmulateViewportWithScissor() { |
| @@ -551,4 +559,11 @@ OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) { | |||
| 551 | return *this; | 559 | return *this; |
| 552 | } | 560 | } |
| 553 | 561 | ||
| 562 | OpenGLState& 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 | ||
| 25 | namespace OpenGL { | 25 | namespace 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. | ||
| 29 | constexpr std::size_t SWAP_CHAIN_SIZE = 3; | ||
| 30 | |||
| 31 | struct 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 | */ | ||
| 49 | class FrameMailbox { | ||
| 50 | public: | ||
| 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 | |||
| 27 | namespace { | 165 | namespace { |
| 28 | 166 | ||
| 29 | constexpr char vertex_shader[] = R"( | 167 | constexpr 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 | ||
| 160 | RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) | 298 | RendererOpenGL::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 | ||
| 163 | RendererOpenGL::~RendererOpenGL() = default; | 302 | RendererOpenGL::~RendererOpenGL() = default; |
| 164 | 303 | ||
| 304 | MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64)); | ||
| 305 | MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); | ||
| 306 | |||
| 165 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 307 | void 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 | |||
| 377 | void 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 | ||
| 202 | void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { | 395 | void 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++; | 616 | void 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 | ||
| 425 | void RendererOpenGL::UpdateFramerate() {} | 651 | void RendererOpenGL::RenderScreenshot() { |
| 652 | if (!renderer_settings.screenshot_requested) { | ||
| 653 | return; | ||
| 654 | } | ||
| 426 | 655 | ||
| 427 | void 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 | ||
| 461 | bool RendererOpenGL::Init() { | 689 | bool 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 | ||
| 47 | struct PresentationTexture { | ||
| 48 | u32 width = 0; | ||
| 49 | u32 height = 0; | ||
| 50 | OGLTexture texture; | ||
| 51 | }; | ||
| 52 | |||
| 53 | class FrameMailbox; | ||
| 54 | |||
| 47 | class RendererOpenGL final : public VideoCore::RendererBase { | 55 | class RendererOpenGL final : public VideoCore::RendererBase { |
| 48 | public: | 56 | public: |
| 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 | ||
| 61 | private: | 65 | private: |
| 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 | ||
| 108 | void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 108 | void 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 | ||
| 143 | void RendererVulkan::TryPresent(int /*timeout_ms*/) { | ||
| 144 | // TODO (bunnei): ImplementMe | ||
| 145 | } | ||
| 146 | |||
| 138 | bool RendererVulkan::Init() { | 147 | bool 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 | ||
| 48 | private: | 44 | private: |
| 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 | ||
| 37 | namespace { | 37 | namespace { |
| 38 | 38 | ||
| 39 | // C++20's using enum | ||
| 40 | constexpr auto eUniformBuffer = vk::DescriptorType::eUniformBuffer; | ||
| 41 | constexpr auto eStorageBuffer = vk::DescriptorType::eStorageBuffer; | ||
| 42 | constexpr auto eUniformTexelBuffer = vk::DescriptorType::eUniformTexelBuffer; | ||
| 43 | constexpr auto eCombinedImageSampler = vk::DescriptorType::eCombinedImageSampler; | ||
| 44 | constexpr auto eStorageImage = vk::DescriptorType::eStorageImage; | ||
| 45 | |||
| 39 | constexpr VideoCommon::Shader::CompilerSettings compiler_settings{ | 46 | constexpr 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 | ||
| 129 | template <vk::DescriptorType descriptor_type, class Container> | ||
| 130 | void 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 | |||
| 122 | u32 FillDescriptorLayout(const ShaderEntries& entries, | 143 | u32 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 | ||
| 364 | void FillDescriptorUpdateTemplateEntries( | 380 | template <vk::DescriptorType descriptor_type, class Container> |
| 365 | const VKDevice& device, const ShaderEntries& entries, u32& binding, u32& offset, | 381 | void 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 | |||
| 411 | void 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 | ||
| 196 | void FillDescriptorUpdateTemplateEntries( | 196 | void 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 | ||
| 106 | template <typename Engine, typename Entry> | 106 | template <typename Engine, typename Entry> |
| 107 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, | 107 | Tegra::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 | ||
| 70 | struct SampledImage { | 70 | struct 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 | ||
| 76 | struct StorageImage { | 77 | struct 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 | ||
| 294 | void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Node op_b, Node op_c, | 294 | void 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 | ||
| 38 | EmuThread::EmuThread(GRenderWindow* render_window) : render_window(render_window) {} | 42 | EmuThread::EmuThread(GRenderWindow& window) |
| 43 | : shared_context{window.CreateSharedContext()}, | ||
| 44 | context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context | ||
| 45 | : window} {} | ||
| 39 | 46 | ||
| 40 | EmuThread::~EmuThread() = default; | 47 | EmuThread::~EmuThread() = default; |
| 41 | 48 | ||
| 42 | void EmuThread::run() { | 49 | static 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 | ||
| 58 | void 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 | ||
| 105 | class GGLContext : public Core::Frontend::GraphicsContext { | 114 | class GGLContext : public Core::Frontend::GraphicsContext { |
| 106 | public: | 115 | public: |
| 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 | |||
| 123 | private: | 140 | private: |
| 124 | QOpenGLContext* shared_context; | 141 | QOpenGLContext* context; |
| 125 | QOpenGLContext context; | 142 | QOffscreenSurface* surface; |
| 126 | }; | 143 | }; |
| 127 | 144 | ||
| 128 | class GWidgetInternal : public QWindow { | 145 | class ChildRenderWindow : public QWindow { |
| 129 | public: | 146 | public: |
| 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 { | 154 | protected: |
| 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 { | 202 | private: |
| 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()}; | 206 | class OpenGLWindow final : public ChildRenderWindow { |
| 164 | const auto [x, y] = parent->ScaleTouch(pos); | 207 | public: |
| 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 | ||
| 191 | protected: | 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 | ||
| 196 | private: | 245 | private: |
| 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 | ||
| 204 | class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow { | ||
| 205 | public: | ||
| 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 |
| 218 | class GVKWidgetInternal final : public GWidgetInternal { | 250 | class VulkanWindow final : public ChildRenderWindow { |
| 219 | public: | 251 | public: |
| 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 | |||
| 264 | private: | ||
| 265 | QWidget* event_handler{}; | ||
| 225 | }; | 266 | }; |
| 226 | #endif | 267 | #endif |
| 227 | 268 | ||
| 228 | GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) | 269 | GRenderWindow::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 | ||
| 240 | GRenderWindow::~GRenderWindow() { | 285 | GRenderWindow::~GRenderWindow() { |
| 241 | InputCommon::Shutdown(); | 286 | InputCommon::Shutdown(); |
| 242 | |||
| 243 | // Avoid an unordered destruction that generates a segfault | ||
| 244 | delete child; | ||
| 245 | } | 287 | } |
| 246 | 288 | ||
| 247 | void GRenderWindow::moveContext() { | 289 | void 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 | ||
| 261 | void GRenderWindow::SwapBuffers() { | 295 | void GRenderWindow::DoneCurrent() { |
| 262 | if (context) { | 296 | if (core_context) { |
| 263 | context->swapBuffers(child); | 297 | core_context->DoneCurrent(); |
| 264 | } | 298 | } |
| 299 | } | ||
| 300 | |||
| 301 | void 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 | ||
| 271 | void GRenderWindow::MakeCurrent() { | ||
| 272 | if (context) { | ||
| 273 | context->makeCurrent(child); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | void GRenderWindow::DoneCurrent() { | ||
| 278 | if (context) { | ||
| 279 | context->doneCurrent(); | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | void GRenderWindow::PollEvents() {} | ||
| 284 | |||
| 285 | bool GRenderWindow::IsShown() const { | 308 | bool 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 | |||
| 309 | void GRenderWindow::OnFramebufferSizeChanged() { | 332 | void 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 | |||
| 317 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { | ||
| 318 | if (child) { | ||
| 319 | child->keyPressEvent(event); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { | ||
| 324 | if (child) { | ||
| 325 | child->keyReleaseEvent(event); | ||
| 326 | } | ||
| 327 | } | 339 | } |
| 328 | 340 | ||
| 329 | void GRenderWindow::BackupGeometry() { | 341 | void GRenderWindow::BackupGeometry() { |
| @@ -351,13 +363,12 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 351 | return geometry; | 363 | return geometry; |
| 352 | } | 364 | } |
| 353 | 365 | ||
| 354 | qreal GRenderWindow::GetWindowPixelRatio() const { | 366 | qreal 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 | ||
| 359 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { | 370 | std::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 | ||
| 381 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | ||
| 382 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 383 | } | ||
| 384 | |||
| 385 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 386 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 387 | } | ||
| 388 | |||
| 389 | void 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 | |||
| 402 | void 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 | |||
| 412 | void 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 | |||
| 370 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 422 | void 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 | ||
| 418 | void GRenderWindow::OnClientAreaResized(u32 width, u32 height) { | 470 | void GRenderWindow::resizeEvent(QResizeEvent* event) { |
| 419 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); | 471 | QWidget::resizeEvent(event); |
| 472 | OnFramebufferSizeChanged(); | ||
| 420 | } | 473 | } |
| 421 | 474 | ||
| 422 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | 475 | std::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 | ||
| 426 | bool GRenderWindow::InitRenderTarget() { | 482 | bool 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 | ||
| 523 | void GRenderWindow::ReleaseRenderTarget() { | ||
| 524 | if (child_widget) { | ||
| 525 | layout()->removeWidget(child_widget); | ||
| 526 | delete child_widget; | ||
| 527 | child_widget = nullptr; | ||
| 528 | } | ||
| 529 | } | ||
| 530 | |||
| 489 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | 531 | void 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 | ||
| 571 | bool GRenderWindow::LoadOpenGL() { | 623 | bool 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 | ||
| 622 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { | 674 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { |
| 623 | this->emu_thread = emu_thread; | 675 | this->emu_thread = emu_thread; |
| 624 | child->DisablePainting(); | ||
| 625 | } | 676 | } |
| 626 | 677 | ||
| 627 | void GRenderWindow::OnEmulationStopping() { | 678 | void GRenderWindow::OnEmulationStopping() { |
| 628 | emu_thread = nullptr; | 679 | emu_thread = nullptr; |
| 629 | child->EnablePainting(); | ||
| 630 | } | 680 | } |
| 631 | 681 | ||
| 632 | void GRenderWindow::showEvent(QShowEvent* event) { | 682 | void 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 | ||
| 20 | class GRenderWindow; | ||
| 19 | class QKeyEvent; | 21 | class QKeyEvent; |
| 20 | class QScreen; | 22 | class QScreen; |
| 21 | class QTouchEvent; | 23 | class QTouchEvent; |
| @@ -26,14 +28,6 @@ class QOpenGLContext; | |||
| 26 | class QVulkanInstance; | 28 | class QVulkanInstance; |
| 27 | #endif | 29 | #endif |
| 28 | 30 | ||
| 29 | class GWidgetInternal; | ||
| 30 | class GGLWidgetInternal; | ||
| 31 | class GVKWidgetInternal; | ||
| 32 | class GMainWindow; | ||
| 33 | class GRenderWindow; | ||
| 34 | class QSurface; | ||
| 35 | class QOpenGLContext; | ||
| 36 | |||
| 37 | namespace VideoCore { | 31 | namespace VideoCore { |
| 38 | enum class LoadCallbackStage; | 32 | enum class LoadCallbackStage; |
| 39 | } | 33 | } |
| @@ -42,7 +36,7 @@ class EmuThread final : public QThread { | |||
| 42 | Q_OBJECT | 36 | Q_OBJECT |
| 43 | 37 | ||
| 44 | public: | 38 | public: |
| 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 | ||
| 101 | signals: | 99 | signals: |
| 102 | /** | 100 | /** |
| @@ -126,11 +124,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow { | |||
| 126 | Q_OBJECT | 124 | Q_OBJECT |
| 127 | 125 | ||
| 128 | public: | 126 | public: |
| 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 | ||
| 163 | public slots: | 168 | public 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 | ||
| 175 | private: | 178 | private: |
| 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 | ||
| 207 | protected: | 214 | protected: |
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 | ||
| 1092 | void GMainWindow::StoreRecentFile(const QString& filename) { | 1091 | void 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 | ||
| 2218 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | 2217 | static 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 | ||
| 2224 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | 2221 | void 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 | ||
| 2230 | static bool IsSingleFileDropEvent(QDropEvent* event) { | 2228 | bool 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 | |||
| 2235 | void 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 | |||
| 2248 | void GMainWindow::dropEvent(QDropEvent* event) { | ||
| 2249 | DropAction(event); | ||
| 2250 | } | 2250 | } |
| 2251 | 2251 | ||
| 2252 | void GMainWindow::dragEnterEvent(QDragEnterEvent* event) { | 2252 | void GMainWindow::dragEnterEvent(QDragEnterEvent* event) { |
| 2253 | if (IsSingleFileDropEvent(event)) { | 2253 | AcceptDropEvent(event); |
| 2254 | event->acceptProposedAction(); | ||
| 2255 | } | ||
| 2256 | } | 2254 | } |
| 2257 | 2255 | ||
| 2258 | void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { | 2256 | void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { |
| 2259 | event->acceptProposedAction(); | 2257 | AcceptDropEvent(event); |
| 2260 | } | 2258 | } |
| 2261 | 2259 | ||
| 2262 | bool GMainWindow::ConfirmChangeGame() { | 2260 | bool 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 | |||
| 81 | signals: | 84 | signals: |
| 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") |
| 87 | udp_input_address= | 87 | udp_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) |
| 90 | udp_input_port= | 90 | udp_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) |
| 151 | use_asynchronous_gpu_emulation = | 151 | use_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 | ||
| 156 | use_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. |
| 155 | bg_red = | 160 | bg_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 | ||
| 16 | EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | 16 | EmuWindow_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 | ||
| 11 | struct SDL_Window; | 11 | struct SDL_Window; |
| 12 | 12 | ||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 13 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { | 17 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { |
| 14 | public: | 18 | public: |
| 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 | |||
| 27 | protected: | 34 | protected: |
| 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 | ||
| 22 | class SDLGLContext : public Core::Frontend::GraphicsContext { | 24 | class SDLGLContext : public Core::Frontend::GraphicsContext { |
| 23 | public: | 25 | public: |
| 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 | |||
| 48 | private: | 47 | private: |
| 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 | ||
| 83 | EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(bool fullscreen) : EmuWindow_SDL2(fullscreen) { | 82 | EmuWindow_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 | ||
| 139 | EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { | 146 | EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { |
| 140 | SDL_GL_DeleteContext(gl_context); | 147 | core_context.reset(); |
| 141 | } | 148 | SDL_GL_DeleteContext(window_context); |
| 142 | |||
| 143 | void EmuWindow_SDL2_GL::SwapBuffers() { | ||
| 144 | SDL_GL_SwapWindow(render_window); | ||
| 145 | } | 149 | } |
| 146 | 150 | ||
| 147 | void EmuWindow_SDL2_GL::MakeCurrent() { | 151 | void EmuWindow_SDL2_GL::MakeCurrent() { |
| 148 | SDL_GL_MakeCurrent(render_window, gl_context); | 152 | core_context->MakeCurrent(); |
| 149 | } | 153 | } |
| 150 | 154 | ||
| 151 | void EmuWindow_SDL2_GL::DoneCurrent() { | 155 | void EmuWindow_SDL2_GL::DoneCurrent() { |
| 152 | SDL_GL_MakeCurrent(render_window, nullptr); | 156 | core_context->DoneCurrent(); |
| 153 | } | 157 | } |
| 154 | 158 | ||
| 155 | void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | 159 | void 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 | |||
| 161 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { | 165 | std::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 | |||
| 169 | void 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 | ||
| 11 | class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { | 11 | class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { |
| 12 | public: | 12 | public: |
| 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 | ||
| 31 | private: | 26 | private: |
| 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 | ||
| 18 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) { | 18 | EmuWindow_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 | ||
| 113 | void EmuWindow_SDL2_VK::SwapBuffers() {} | ||
| 114 | |||
| 115 | void EmuWindow_SDL2_VK::MakeCurrent() { | 114 | void 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 | |||
| 163 | void 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 | ||
| 11 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { | 11 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { |
| 12 | public: | 12 | public: |
| 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 | ||
| 115 | void EmuWindow_SDL2_Hide::SwapBuffers() { | ||
| 116 | SDL_GL_SwapWindow(render_window); | ||
| 117 | } | ||
| 118 | |||
| 119 | void EmuWindow_SDL2_Hide::PollEvents() {} | 115 | void EmuWindow_SDL2_Hide::PollEvents() {} |
| 120 | 116 | ||
| 121 | void EmuWindow_SDL2_Hide::MakeCurrent() { | 117 | void 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 | ||