diff options
Diffstat (limited to 'src')
25 files changed, 489 insertions, 314 deletions
diff --git a/src/common/scope_exit.h b/src/common/scope_exit.h index baf1f1c9e..1176a72b1 100644 --- a/src/common/scope_exit.h +++ b/src/common/scope_exit.h | |||
| @@ -20,7 +20,7 @@ struct ScopeExitHelper { | |||
| 20 | 20 | ||
| 21 | template <typename Func> | 21 | template <typename Func> |
| 22 | ScopeExitHelper<Func> ScopeExit(Func&& func) { | 22 | ScopeExitHelper<Func> ScopeExit(Func&& func) { |
| 23 | return ScopeExitHelper<Func>(std::move(func)); | 23 | return ScopeExitHelper<Func>(std::forward<Func>(func)); |
| 24 | } | 24 | } |
| 25 | } // namespace detail | 25 | } // namespace detail |
| 26 | 26 | ||
diff --git a/src/common/swap.h b/src/common/swap.h index b3eab1324..71932c2bb 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -21,11 +21,6 @@ | |||
| 21 | 21 | ||
| 22 | #if defined(_MSC_VER) | 22 | #if defined(_MSC_VER) |
| 23 | #include <cstdlib> | 23 | #include <cstdlib> |
| 24 | #elif defined(__linux__) | ||
| 25 | #include <byteswap.h> | ||
| 26 | #elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || \ | ||
| 27 | defined(__NetBSD__) || defined(__OpenBSD__) | ||
| 28 | #include <sys/endian.h> | ||
| 29 | #endif | 24 | #endif |
| 30 | #include <cstring> | 25 | #include <cstring> |
| 31 | #include "common/common_types.h" | 26 | #include "common/common_types.h" |
| @@ -62,86 +57,49 @@ | |||
| 62 | namespace Common { | 57 | namespace Common { |
| 63 | 58 | ||
| 64 | #ifdef _MSC_VER | 59 | #ifdef _MSC_VER |
| 65 | inline u16 swap16(u16 _data) { | 60 | [[nodiscard]] inline u16 swap16(u16 data) noexcept { |
| 66 | return _byteswap_ushort(_data); | 61 | return _byteswap_ushort(data); |
| 67 | } | 62 | } |
| 68 | inline u32 swap32(u32 _data) { | 63 | [[nodiscard]] inline u32 swap32(u32 data) noexcept { |
| 69 | return _byteswap_ulong(_data); | 64 | return _byteswap_ulong(data); |
| 70 | } | 65 | } |
| 71 | inline u64 swap64(u64 _data) { | 66 | [[nodiscard]] inline u64 swap64(u64 data) noexcept { |
| 72 | return _byteswap_uint64(_data); | 67 | return _byteswap_uint64(data); |
| 73 | } | 68 | } |
| 74 | #elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6) | 69 | #elif defined(__clang__) || defined(__GNUC__) |
| 75 | inline u16 swap16(u16 _data) { | 70 | #if defined(__Bitrig__) || defined(__OpenBSD__) |
| 76 | u32 data = _data; | ||
| 77 | __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); | ||
| 78 | return (u16)data; | ||
| 79 | } | ||
| 80 | inline u32 swap32(u32 _data) { | ||
| 81 | __asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data)); | ||
| 82 | return _data; | ||
| 83 | } | ||
| 84 | inline u64 swap64(u64 _data) { | ||
| 85 | return ((u64)swap32(_data) << 32) | swap32(_data >> 32); | ||
| 86 | } | ||
| 87 | #elif __linux__ | ||
| 88 | inline u16 swap16(u16 _data) { | ||
| 89 | return bswap_16(_data); | ||
| 90 | } | ||
| 91 | inline u32 swap32(u32 _data) { | ||
| 92 | return bswap_32(_data); | ||
| 93 | } | ||
| 94 | inline u64 swap64(u64 _data) { | ||
| 95 | return bswap_64(_data); | ||
| 96 | } | ||
| 97 | #elif __APPLE__ | ||
| 98 | inline __attribute__((always_inline)) u16 swap16(u16 _data) { | ||
| 99 | return (_data >> 8) | (_data << 8); | ||
| 100 | } | ||
| 101 | inline __attribute__((always_inline)) u32 swap32(u32 _data) { | ||
| 102 | return __builtin_bswap32(_data); | ||
| 103 | } | ||
| 104 | inline __attribute__((always_inline)) u64 swap64(u64 _data) { | ||
| 105 | return __builtin_bswap64(_data); | ||
| 106 | } | ||
| 107 | #elif defined(__Bitrig__) || defined(__OpenBSD__) | ||
| 108 | // redefine swap16, swap32, swap64 as inline functions | 71 | // redefine swap16, swap32, swap64 as inline functions |
| 109 | #undef swap16 | 72 | #undef swap16 |
| 110 | #undef swap32 | 73 | #undef swap32 |
| 111 | #undef swap64 | 74 | #undef swap64 |
| 112 | inline u16 swap16(u16 _data) { | 75 | #endif |
| 113 | return __swap16(_data); | 76 | [[nodiscard]] inline u16 swap16(u16 data) noexcept { |
| 114 | } | 77 | return __builtin_bswap16(data); |
| 115 | inline u32 swap32(u32 _data) { | ||
| 116 | return __swap32(_data); | ||
| 117 | } | ||
| 118 | inline u64 swap64(u64 _data) { | ||
| 119 | return __swap64(_data); | ||
| 120 | } | ||
| 121 | #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) | ||
| 122 | inline u16 swap16(u16 _data) { | ||
| 123 | return bswap16(_data); | ||
| 124 | } | 78 | } |
| 125 | inline u32 swap32(u32 _data) { | 79 | [[nodiscard]] inline u32 swap32(u32 data) noexcept { |
| 126 | return bswap32(_data); | 80 | return __builtin_bswap32(data); |
| 127 | } | 81 | } |
| 128 | inline u64 swap64(u64 _data) { | 82 | [[nodiscard]] inline u64 swap64(u64 data) noexcept { |
| 129 | return bswap64(_data); | 83 | return __builtin_bswap64(data); |
| 130 | } | 84 | } |
| 131 | #else | 85 | #else |
| 132 | // Slow generic implementation. | 86 | // Generic implementation. |
| 133 | inline u16 swap16(u16 data) { | 87 | [[nodiscard]] inline u16 swap16(u16 data) noexcept { |
| 134 | return (data >> 8) | (data << 8); | 88 | return (data >> 8) | (data << 8); |
| 135 | } | 89 | } |
| 136 | inline u32 swap32(u32 data) { | 90 | [[nodiscard]] inline u32 swap32(u32 data) noexcept { |
| 137 | return (swap16(data) << 16) | swap16(data >> 16); | 91 | return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) | |
| 92 | ((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24); | ||
| 138 | } | 93 | } |
| 139 | inline u64 swap64(u64 data) { | 94 | [[nodiscard]] inline u64 swap64(u64 data) noexcept { |
| 140 | return ((u64)swap32(data) << 32) | swap32(data >> 32); | 95 | return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) | |
| 96 | ((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) | | ||
| 97 | ((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) | | ||
| 98 | ((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56); | ||
| 141 | } | 99 | } |
| 142 | #endif | 100 | #endif |
| 143 | 101 | ||
| 144 | inline float swapf(float f) { | 102 | [[nodiscard]] inline float swapf(float f) noexcept { |
| 145 | static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t."); | 103 | static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t."); |
| 146 | 104 | ||
| 147 | u32 value; | 105 | u32 value; |
| @@ -153,7 +111,7 @@ inline float swapf(float f) { | |||
| 153 | return f; | 111 | return f; |
| 154 | } | 112 | } |
| 155 | 113 | ||
| 156 | inline double swapd(double f) { | 114 | [[nodiscard]] inline double swapd(double f) noexcept { |
| 157 | static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t."); | 115 | static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t."); |
| 158 | 116 | ||
| 159 | u64 value; | 117 | u64 value; |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index d0bcb4660..70a522556 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -13,6 +13,23 @@ | |||
| 13 | namespace Core::Frontend { | 13 | namespace Core::Frontend { |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * Represents a graphics context that can be used for background computation or drawing. If the | ||
| 17 | * graphics backend doesn't require the context, then the implementation of these methods can be | ||
| 18 | * stubs | ||
| 19 | */ | ||
| 20 | class GraphicsContext { | ||
| 21 | public: | ||
| 22 | /// Makes the graphics context current for the caller thread | ||
| 23 | virtual void MakeCurrent() = 0; | ||
| 24 | |||
| 25 | /// Releases (dunno if this is the "right" word) the context from the caller thread | ||
| 26 | virtual void DoneCurrent() = 0; | ||
| 27 | |||
| 28 | /// Swap buffers to display the next frame | ||
| 29 | virtual void SwapBuffers() = 0; | ||
| 30 | }; | ||
| 31 | |||
| 32 | /** | ||
| 16 | * Abstraction class used to provide an interface between emulation code and the frontend | 33 | * Abstraction class used to provide an interface between emulation code and the frontend |
| 17 | * (e.g. SDL, QGLWidget, GLFW, etc...). | 34 | * (e.g. SDL, QGLWidget, GLFW, etc...). |
| 18 | * | 35 | * |
| @@ -30,7 +47,7 @@ namespace Core::Frontend { | |||
| 30 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please | 47 | * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please |
| 31 | * re-read the upper points again and think about it if you don't see this. | 48 | * re-read the upper points again and think about it if you don't see this. |
| 32 | */ | 49 | */ |
| 33 | class EmuWindow { | 50 | class EmuWindow : public GraphicsContext { |
| 34 | public: | 51 | public: |
| 35 | /// Data structure to store emuwindow configuration | 52 | /// Data structure to store emuwindow configuration |
| 36 | struct WindowConfig { | 53 | struct WindowConfig { |
| @@ -40,17 +57,21 @@ public: | |||
| 40 | std::pair<unsigned, unsigned> min_client_area_size; | 57 | std::pair<unsigned, unsigned> min_client_area_size; |
| 41 | }; | 58 | }; |
| 42 | 59 | ||
| 43 | /// Swap buffers to display the next frame | ||
| 44 | virtual void SwapBuffers() = 0; | ||
| 45 | |||
| 46 | /// Polls window events | 60 | /// Polls window events |
| 47 | virtual void PollEvents() = 0; | 61 | virtual void PollEvents() = 0; |
| 48 | 62 | ||
| 49 | /// Makes the graphics context current for the caller thread | 63 | /** |
| 50 | virtual void MakeCurrent() = 0; | 64 | * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This |
| 51 | 65 | * context can be used from other threads for background graphics computation. If the frontend | |
| 52 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread | 66 | * is using a graphics backend that doesn't need anything specific to run on a different thread, |
| 53 | virtual void DoneCurrent() = 0; | 67 | * then it can use a stubbed implemenation for GraphicsContext. |
| 68 | * | ||
| 69 | * If the return value is null, then the core should assume that the frontend cannot provide a | ||
| 70 | * Shared Context | ||
| 71 | */ | ||
| 72 | virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const { | ||
| 73 | return nullptr; | ||
| 74 | } | ||
| 54 | 75 | ||
| 55 | /** | 76 | /** |
| 56 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) | 77 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1b891f632..ca52267b2 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -220,11 +220,6 @@ void Thread::SetPriority(u32 priority) { | |||
| 220 | UpdatePriority(); | 220 | UpdatePriority(); |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | void Thread::BoostPriority(u32 priority) { | ||
| 224 | scheduler->SetThreadPriority(this, priority); | ||
| 225 | current_priority = priority; | ||
| 226 | } | ||
| 227 | |||
| 228 | void Thread::SetWaitSynchronizationResult(ResultCode result) { | 223 | void Thread::SetWaitSynchronizationResult(ResultCode result) { |
| 229 | context.cpu_registers[0] = result.raw; | 224 | context.cpu_registers[0] = result.raw; |
| 230 | } | 225 | } |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 83c83e45a..32026d7f0 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -136,12 +136,6 @@ public: | |||
| 136 | */ | 136 | */ |
| 137 | void SetPriority(u32 priority); | 137 | void SetPriority(u32 priority); |
| 138 | 138 | ||
| 139 | /** | ||
| 140 | * Temporarily boosts the thread's priority until the next time it is scheduled | ||
| 141 | * @param priority The new priority | ||
| 142 | */ | ||
| 143 | void BoostPriority(u32 priority); | ||
| 144 | |||
| 145 | /// Adds a thread to the list of threads that are waiting for a lock held by this thread. | 139 | /// Adds a thread to the list of threads that are waiting for a lock held by this thread. |
| 146 | void AddMutexWaiter(SharedPtr<Thread> thread); | 140 | void AddMutexWaiter(SharedPtr<Thread> thread); |
| 147 | 141 | ||
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 657baddb8..0249b6992 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -115,11 +115,12 @@ private: | |||
| 115 | 115 | ||
| 116 | void Read(Kernel::HLERequestContext& ctx) { | 116 | void Read(Kernel::HLERequestContext& ctx) { |
| 117 | IPC::RequestParser rp{ctx}; | 117 | IPC::RequestParser rp{ctx}; |
| 118 | const u64 unk = rp.Pop<u64>(); | 118 | const u64 option = rp.Pop<u64>(); |
| 119 | const s64 offset = rp.Pop<s64>(); | 119 | const s64 offset = rp.Pop<s64>(); |
| 120 | const s64 length = rp.Pop<s64>(); | 120 | const s64 length = rp.Pop<s64>(); |
| 121 | 121 | ||
| 122 | LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); | 122 | LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, |
| 123 | length); | ||
| 123 | 124 | ||
| 124 | // Error checking | 125 | // Error checking |
| 125 | if (length < 0) { | 126 | if (length < 0) { |
| @@ -148,11 +149,12 @@ private: | |||
| 148 | 149 | ||
| 149 | void Write(Kernel::HLERequestContext& ctx) { | 150 | void Write(Kernel::HLERequestContext& ctx) { |
| 150 | IPC::RequestParser rp{ctx}; | 151 | IPC::RequestParser rp{ctx}; |
| 151 | const u64 unk = rp.Pop<u64>(); | 152 | const u64 option = rp.Pop<u64>(); |
| 152 | const s64 offset = rp.Pop<s64>(); | 153 | const s64 offset = rp.Pop<s64>(); |
| 153 | const s64 length = rp.Pop<s64>(); | 154 | const s64 length = rp.Pop<s64>(); |
| 154 | 155 | ||
| 155 | LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); | 156 | LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, |
| 157 | length); | ||
| 156 | 158 | ||
| 157 | // Error checking | 159 | // Error checking |
| 158 | if (length < 0) { | 160 | if (length < 0) { |
| @@ -250,10 +252,7 @@ private: | |||
| 250 | u64 next_entry_index = 0; | 252 | u64 next_entry_index = 0; |
| 251 | 253 | ||
| 252 | void Read(Kernel::HLERequestContext& ctx) { | 254 | void Read(Kernel::HLERequestContext& ctx) { |
| 253 | IPC::RequestParser rp{ctx}; | 255 | LOG_DEBUG(Service_FS, "called."); |
| 254 | const u64 unk = rp.Pop<u64>(); | ||
| 255 | |||
| 256 | LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk); | ||
| 257 | 256 | ||
| 258 | // Calculate how many entries we can fit in the output buffer | 257 | // Calculate how many entries we can fit in the output buffer |
| 259 | const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); | 258 | const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index d65693fc7..609102f2c 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -93,12 +93,18 @@ public: | |||
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | void LoadNrr(Kernel::HLERequestContext& ctx) { | 95 | void LoadNrr(Kernel::HLERequestContext& ctx) { |
| 96 | struct Parameters { | ||
| 97 | u64_le process_id; | ||
| 98 | u64_le nrr_address; | ||
| 99 | u64_le nrr_size; | ||
| 100 | }; | ||
| 101 | |||
| 96 | IPC::RequestParser rp{ctx}; | 102 | IPC::RequestParser rp{ctx}; |
| 97 | rp.Skip(2, false); | 103 | const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>(); |
| 98 | const VAddr nrr_addr{rp.Pop<VAddr>()}; | 104 | |
| 99 | const u64 nrr_size{rp.Pop<u64>()}; | 105 | LOG_DEBUG(Service_LDR, |
| 100 | LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr, | 106 | "called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}", |
| 101 | nrr_size); | 107 | process_id, nrr_address, nrr_size); |
| 102 | 108 | ||
| 103 | if (!initialized) { | 109 | if (!initialized) { |
| 104 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | 110 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
| @@ -116,24 +122,26 @@ public: | |||
| 116 | } | 122 | } |
| 117 | 123 | ||
| 118 | // NRR Address does not fall on 0x1000 byte boundary | 124 | // NRR Address does not fall on 0x1000 byte boundary |
| 119 | if (!Common::Is4KBAligned(nrr_addr)) { | 125 | if (!Common::Is4KBAligned(nrr_address)) { |
| 120 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | 126 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", |
| 127 | nrr_address); | ||
| 121 | IPC::ResponseBuilder rb{ctx, 2}; | 128 | IPC::ResponseBuilder rb{ctx, 2}; |
| 122 | rb.Push(ERROR_INVALID_ALIGNMENT); | 129 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 123 | return; | 130 | return; |
| 124 | } | 131 | } |
| 125 | 132 | ||
| 126 | // NRR Size is zero or causes overflow | 133 | // NRR Size is zero or causes overflow |
| 127 | if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) { | 134 | if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 || |
| 135 | !Common::Is4KBAligned(nrr_size)) { | ||
| 128 | LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", | 136 | LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", |
| 129 | nrr_addr, nrr_size); | 137 | nrr_address, nrr_size); |
| 130 | IPC::ResponseBuilder rb{ctx, 2}; | 138 | IPC::ResponseBuilder rb{ctx, 2}; |
| 131 | rb.Push(ERROR_INVALID_SIZE); | 139 | rb.Push(ERROR_INVALID_SIZE); |
| 132 | return; | 140 | return; |
| 133 | } | 141 | } |
| 134 | // Read NRR data from memory | 142 | // Read NRR data from memory |
| 135 | std::vector<u8> nrr_data(nrr_size); | 143 | std::vector<u8> nrr_data(nrr_size); |
| 136 | Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size); | 144 | Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size); |
| 137 | NRRHeader header; | 145 | NRRHeader header; |
| 138 | std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); | 146 | std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); |
| 139 | 147 | ||
| @@ -174,7 +182,7 @@ public: | |||
| 174 | hashes.emplace_back(hash); | 182 | hashes.emplace_back(hash); |
| 175 | } | 183 | } |
| 176 | 184 | ||
| 177 | nrr.insert_or_assign(nrr_addr, std::move(hashes)); | 185 | nrr.insert_or_assign(nrr_address, std::move(hashes)); |
| 178 | 186 | ||
| 179 | IPC::ResponseBuilder rb{ctx, 2}; | 187 | IPC::ResponseBuilder rb{ctx, 2}; |
| 180 | rb.Push(RESULT_SUCCESS); | 188 | rb.Push(RESULT_SUCCESS); |
| @@ -188,23 +196,30 @@ public: | |||
| 188 | return; | 196 | return; |
| 189 | } | 197 | } |
| 190 | 198 | ||
| 199 | struct Parameters { | ||
| 200 | u64_le process_id; | ||
| 201 | u64_le nrr_address; | ||
| 202 | }; | ||
| 203 | |||
| 191 | IPC::RequestParser rp{ctx}; | 204 | IPC::RequestParser rp{ctx}; |
| 192 | rp.Skip(2, false); | 205 | const auto [process_id, nrr_address] = rp.PopRaw<Parameters>(); |
| 193 | const auto nrr_addr{rp.Pop<VAddr>()}; | 206 | |
| 194 | LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr); | 207 | LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id, |
| 208 | nrr_address); | ||
| 195 | 209 | ||
| 196 | if (!Common::Is4KBAligned(nrr_addr)) { | 210 | if (!Common::Is4KBAligned(nrr_address)) { |
| 197 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); | 211 | LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", |
| 212 | nrr_address); | ||
| 198 | IPC::ResponseBuilder rb{ctx, 2}; | 213 | IPC::ResponseBuilder rb{ctx, 2}; |
| 199 | rb.Push(ERROR_INVALID_ALIGNMENT); | 214 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 200 | return; | 215 | return; |
| 201 | } | 216 | } |
| 202 | 217 | ||
| 203 | const auto iter = nrr.find(nrr_addr); | 218 | const auto iter = nrr.find(nrr_address); |
| 204 | if (iter == nrr.end()) { | 219 | if (iter == nrr.end()) { |
| 205 | LOG_ERROR(Service_LDR, | 220 | LOG_ERROR(Service_LDR, |
| 206 | "Attempting to unload NRR which has not been loaded! (addr={:016X})", | 221 | "Attempting to unload NRR which has not been loaded! (addr={:016X})", |
| 207 | nrr_addr); | 222 | nrr_address); |
| 208 | IPC::ResponseBuilder rb{ctx, 2}; | 223 | IPC::ResponseBuilder rb{ctx, 2}; |
| 209 | rb.Push(ERROR_INVALID_NRR_ADDRESS); | 224 | rb.Push(ERROR_INVALID_NRR_ADDRESS); |
| 210 | return; | 225 | return; |
| @@ -216,16 +231,22 @@ public: | |||
| 216 | } | 231 | } |
| 217 | 232 | ||
| 218 | void LoadNro(Kernel::HLERequestContext& ctx) { | 233 | void LoadNro(Kernel::HLERequestContext& ctx) { |
| 234 | struct Parameters { | ||
| 235 | u64_le process_id; | ||
| 236 | u64_le image_address; | ||
| 237 | u64_le image_size; | ||
| 238 | u64_le bss_address; | ||
| 239 | u64_le bss_size; | ||
| 240 | }; | ||
| 241 | |||
| 219 | IPC::RequestParser rp{ctx}; | 242 | IPC::RequestParser rp{ctx}; |
| 220 | rp.Skip(2, false); | 243 | const auto [process_id, nro_address, nro_size, bss_address, bss_size] = |
| 221 | const VAddr nro_addr{rp.Pop<VAddr>()}; | 244 | rp.PopRaw<Parameters>(); |
| 222 | const u64 nro_size{rp.Pop<u64>()}; | 245 | |
| 223 | const VAddr bss_addr{rp.Pop<VAddr>()}; | 246 | LOG_DEBUG(Service_LDR, |
| 224 | const u64 bss_size{rp.Pop<u64>()}; | 247 | "called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, " |
| 225 | LOG_DEBUG( | 248 | "bss_size={:016X}", |
| 226 | Service_LDR, | 249 | process_id, nro_address, nro_size, bss_address, bss_size); |
| 227 | "called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}", | ||
| 228 | nro_addr, nro_size, bss_addr, bss_size); | ||
| 229 | 250 | ||
| 230 | if (!initialized) { | 251 | if (!initialized) { |
| 231 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | 252 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
| @@ -243,8 +264,9 @@ public: | |||
| 243 | } | 264 | } |
| 244 | 265 | ||
| 245 | // NRO Address does not fall on 0x1000 byte boundary | 266 | // NRO Address does not fall on 0x1000 byte boundary |
| 246 | if (!Common::Is4KBAligned(nro_addr)) { | 267 | if (!Common::Is4KBAligned(nro_address)) { |
| 247 | LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); | 268 | LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", |
| 269 | nro_address); | ||
| 248 | IPC::ResponseBuilder rb{ctx, 2}; | 270 | IPC::ResponseBuilder rb{ctx, 2}; |
| 249 | rb.Push(ERROR_INVALID_ALIGNMENT); | 271 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 250 | return; | 272 | return; |
| @@ -252,15 +274,15 @@ public: | |||
| 252 | 274 | ||
| 253 | // NRO Size or BSS Size is zero or causes overflow | 275 | // NRO Size or BSS Size is zero or causes overflow |
| 254 | const auto nro_size_valid = | 276 | const auto nro_size_valid = |
| 255 | nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size); | 277 | nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size); |
| 256 | const auto bss_size_valid = | 278 | const auto bss_size_valid = nro_size + bss_size >= nro_size && |
| 257 | nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr); | 279 | (bss_size == 0 || bss_address + bss_size > bss_address); |
| 258 | 280 | ||
| 259 | if (!nro_size_valid || !bss_size_valid) { | 281 | if (!nro_size_valid || !bss_size_valid) { |
| 260 | LOG_ERROR(Service_LDR, | 282 | LOG_ERROR(Service_LDR, |
| 261 | "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " | 283 | "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " |
| 262 | "bss_address={:016X}, bss_size={:016X})", | 284 | "bss_address={:016X}, bss_size={:016X})", |
| 263 | nro_addr, nro_size, bss_addr, bss_size); | 285 | nro_address, nro_size, bss_address, bss_size); |
| 264 | IPC::ResponseBuilder rb{ctx, 2}; | 286 | IPC::ResponseBuilder rb{ctx, 2}; |
| 265 | rb.Push(ERROR_INVALID_SIZE); | 287 | rb.Push(ERROR_INVALID_SIZE); |
| 266 | return; | 288 | return; |
| @@ -268,7 +290,7 @@ public: | |||
| 268 | 290 | ||
| 269 | // Read NRO data from memory | 291 | // Read NRO data from memory |
| 270 | std::vector<u8> nro_data(nro_size); | 292 | std::vector<u8> nro_data(nro_size); |
| 271 | Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); | 293 | Memory::ReadBlock(nro_address, nro_data.data(), nro_size); |
| 272 | 294 | ||
| 273 | SHA256Hash hash{}; | 295 | SHA256Hash hash{}; |
| 274 | mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); | 296 | mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); |
| @@ -318,17 +340,18 @@ public: | |||
| 318 | return; | 340 | return; |
| 319 | } | 341 | } |
| 320 | 342 | ||
| 321 | ASSERT(vm_manager | 343 | ASSERT( |
| 322 | .MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode) | 344 | vm_manager |
| 323 | .IsSuccess()); | 345 | .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) |
| 324 | ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); | 346 | .IsSuccess()); |
| 347 | ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); | ||
| 325 | 348 | ||
| 326 | if (bss_size > 0) { | 349 | if (bss_size > 0) { |
| 327 | ASSERT(vm_manager | 350 | ASSERT(vm_manager |
| 328 | .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, | 351 | .MirrorMemory(*map_address + nro_size, bss_address, bss_size, |
| 329 | Kernel::MemoryState::ModuleCode) | 352 | Kernel::MemoryState::ModuleCode) |
| 330 | .IsSuccess()); | 353 | .IsSuccess()); |
| 331 | ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); | 354 | ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess()); |
| 332 | } | 355 | } |
| 333 | 356 | ||
| 334 | vm_manager.ReprotectRange(*map_address, header.text_size, | 357 | vm_manager.ReprotectRange(*map_address, header.text_size, |
| @@ -348,13 +371,6 @@ public: | |||
| 348 | } | 371 | } |
| 349 | 372 | ||
| 350 | void UnloadNro(Kernel::HLERequestContext& ctx) { | 373 | void UnloadNro(Kernel::HLERequestContext& ctx) { |
| 351 | IPC::RequestParser rp{ctx}; | ||
| 352 | rp.Skip(2, false); | ||
| 353 | const VAddr mapped_addr{rp.PopRaw<VAddr>()}; | ||
| 354 | const VAddr heap_addr{rp.PopRaw<VAddr>()}; | ||
| 355 | LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr, | ||
| 356 | heap_addr); | ||
| 357 | |||
| 358 | if (!initialized) { | 374 | if (!initialized) { |
| 359 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); | 375 | LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
| 360 | IPC::ResponseBuilder rb{ctx, 2}; | 376 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -362,22 +378,30 @@ public: | |||
| 362 | return; | 378 | return; |
| 363 | } | 379 | } |
| 364 | 380 | ||
| 365 | if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) { | 381 | struct Parameters { |
| 366 | LOG_ERROR(Service_LDR, | 382 | u64_le process_id; |
| 367 | "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, " | 383 | u64_le nro_address; |
| 368 | "bss_addr={:016X})!", | 384 | }; |
| 369 | mapped_addr, heap_addr); | 385 | |
| 386 | IPC::RequestParser rp{ctx}; | ||
| 387 | const auto [process_id, nro_address] = rp.PopRaw<Parameters>(); | ||
| 388 | LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id, | ||
| 389 | nro_address); | ||
| 390 | |||
| 391 | if (!Common::Is4KBAligned(nro_address)) { | ||
| 392 | LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})", | ||
| 393 | nro_address); | ||
| 370 | IPC::ResponseBuilder rb{ctx, 2}; | 394 | IPC::ResponseBuilder rb{ctx, 2}; |
| 371 | rb.Push(ERROR_INVALID_ALIGNMENT); | 395 | rb.Push(ERROR_INVALID_ALIGNMENT); |
| 372 | return; | 396 | return; |
| 373 | } | 397 | } |
| 374 | 398 | ||
| 375 | const auto iter = nro.find(mapped_addr); | 399 | const auto iter = nro.find(nro_address); |
| 376 | if (iter == nro.end()) { | 400 | if (iter == nro.end()) { |
| 377 | LOG_ERROR(Service_LDR, | 401 | LOG_ERROR(Service_LDR, |
| 378 | "The NRO attempting to unmap was not mapped or has an invalid address " | 402 | "The NRO attempting to be unmapped was not mapped or has an invalid address " |
| 379 | "(actual {:016X})!", | 403 | "(nro_address=0x{:016X})!", |
| 380 | mapped_addr); | 404 | nro_address); |
| 381 | IPC::ResponseBuilder rb{ctx, 2}; | 405 | IPC::ResponseBuilder rb{ctx, 2}; |
| 382 | rb.Push(ERROR_INVALID_NRO_ADDRESS); | 406 | rb.Push(ERROR_INVALID_NRO_ADDRESS); |
| 383 | return; | 407 | return; |
| @@ -386,10 +410,7 @@ public: | |||
| 386 | auto& vm_manager = Core::CurrentProcess()->VMManager(); | 410 | auto& vm_manager = Core::CurrentProcess()->VMManager(); |
| 387 | const auto& nro_size = iter->second.size; | 411 | const auto& nro_size = iter->second.size; |
| 388 | 412 | ||
| 389 | ASSERT(vm_manager | 413 | ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); |
| 390 | .MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode) | ||
| 391 | .IsSuccess()); | ||
| 392 | ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); | ||
| 393 | 414 | ||
| 394 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 415 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); |
| 395 | 416 | ||
| @@ -459,11 +480,10 @@ private: | |||
| 459 | std::map<VAddr, NROInfo> nro; | 480 | std::map<VAddr, NROInfo> nro; |
| 460 | std::map<VAddr, std::vector<SHA256Hash>> nrr; | 481 | std::map<VAddr, std::vector<SHA256Hash>> nrr; |
| 461 | 482 | ||
| 462 | bool IsValidNROHash(const SHA256Hash& hash) { | 483 | bool IsValidNROHash(const SHA256Hash& hash) const { |
| 463 | return std::any_of( | 484 | return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { |
| 464 | nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { | 485 | return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); |
| 465 | return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); | 486 | }); |
| 466 | }); | ||
| 467 | } | 487 | } |
| 468 | 488 | ||
| 469 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { | 489 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index c7f5bbf28..3c5c53e24 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -21,12 +21,13 @@ | |||
| 21 | #include "core/hle/service/vi/display/vi_display.h" | 21 | #include "core/hle/service/vi/display/vi_display.h" |
| 22 | #include "core/hle/service/vi/layer/vi_layer.h" | 22 | #include "core/hle/service/vi/layer/vi_layer.h" |
| 23 | #include "core/perf_stats.h" | 23 | #include "core/perf_stats.h" |
| 24 | #include "core/settings.h" | ||
| 24 | #include "video_core/renderer_base.h" | 25 | #include "video_core/renderer_base.h" |
| 25 | 26 | ||
| 26 | namespace Service::NVFlinger { | 27 | namespace Service::NVFlinger { |
| 27 | 28 | ||
| 28 | constexpr std::size_t SCREEN_REFRESH_RATE = 60; | 29 | constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); |
| 29 | constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); | 30 | constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30); |
| 30 | 31 | ||
| 31 | NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { | 32 | NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { |
| 32 | displays.emplace_back(0, "Default"); | 33 | displays.emplace_back(0, "Default"); |
| @@ -36,13 +37,15 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t | |||
| 36 | displays.emplace_back(4, "Null"); | 37 | displays.emplace_back(4, "Null"); |
| 37 | 38 | ||
| 38 | // Schedule the screen composition events | 39 | // Schedule the screen composition events |
| 39 | composition_event = | 40 | const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks; |
| 40 | core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { | 41 | |
| 42 | composition_event = core_timing.RegisterEvent( | ||
| 43 | "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) { | ||
| 41 | Compose(); | 44 | Compose(); |
| 42 | this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event); | 45 | this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event); |
| 43 | }); | 46 | }); |
| 44 | 47 | ||
| 45 | core_timing.ScheduleEvent(frame_ticks, composition_event); | 48 | core_timing.ScheduleEvent(ticks, composition_event); |
| 46 | } | 49 | } |
| 47 | 50 | ||
| 48 | NVFlinger::~NVFlinger() { | 51 | NVFlinger::~NVFlinger() { |
| @@ -62,6 +65,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { | |||
| 62 | const auto itr = | 65 | const auto itr = |
| 63 | std::find_if(displays.begin(), displays.end(), | 66 | std::find_if(displays.begin(), displays.end(), |
| 64 | [&](const VI::Display& display) { return display.GetName() == name; }); | 67 | [&](const VI::Display& display) { return display.GetName() == name; }); |
| 68 | |||
| 65 | if (itr == displays.end()) { | 69 | if (itr == displays.end()) { |
| 66 | return {}; | 70 | return {}; |
| 67 | } | 71 | } |
diff --git a/src/core/settings.h b/src/core/settings.h index d543eb32f..b84390745 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -393,6 +393,7 @@ struct Values { | |||
| 393 | bool use_disk_shader_cache; | 393 | bool use_disk_shader_cache; |
| 394 | bool use_accurate_gpu_emulation; | 394 | bool use_accurate_gpu_emulation; |
| 395 | bool use_asynchronous_gpu_emulation; | 395 | bool use_asynchronous_gpu_emulation; |
| 396 | bool force_30fps_mode; | ||
| 396 | 397 | ||
| 397 | float bg_red; | 398 | float bg_red; |
| 398 | float bg_green; | 399 | float bg_green; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 55b6d8591..7a68b8738 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -112,11 +112,26 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, | |||
| 112 | params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), | 112 | params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), |
| 113 | params.srgb_conversion); | 113 | params.srgb_conversion); |
| 114 | 114 | ||
| 115 | if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) { | 115 | if (config.tsc.depth_compare_enabled) { |
| 116 | // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled, | 116 | // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled, |
| 117 | // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also | 117 | // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also |
| 118 | // causes GetFormatType to properly return 'Depth' below). | 118 | // causes GetFormatType to properly return 'Depth' below). |
| 119 | params.pixel_format = PixelFormat::Z16; | 119 | if (GetFormatType(params.pixel_format) == SurfaceType::ColorTexture) { |
| 120 | switch (params.pixel_format) { | ||
| 121 | case PixelFormat::R16S: | ||
| 122 | case PixelFormat::R16U: | ||
| 123 | case PixelFormat::R16F: | ||
| 124 | params.pixel_format = PixelFormat::Z16; | ||
| 125 | break; | ||
| 126 | case PixelFormat::R32F: | ||
| 127 | params.pixel_format = PixelFormat::Z32F; | ||
| 128 | break; | ||
| 129 | default: | ||
| 130 | LOG_WARNING(HW_GPU, "Color texture format being used with depth compare: {}", | ||
| 131 | static_cast<u32>(params.pixel_format)); | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | } | ||
| 120 | } | 135 | } |
| 121 | 136 | ||
| 122 | params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); | 137 | params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); |
| @@ -266,10 +281,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, | |||
| 266 | params.component_type = ComponentTypeFromRenderTarget(config.format); | 281 | params.component_type = ComponentTypeFromRenderTarget(config.format); |
| 267 | params.type = GetFormatType(params.pixel_format); | 282 | params.type = GetFormatType(params.pixel_format); |
| 268 | params.width = config.width; | 283 | params.width = config.width; |
| 269 | if (!params.is_tiled) { | 284 | params.pitch = config.pitch; |
| 270 | const u32 bpp = params.GetFormatBpp() / 8; | ||
| 271 | params.pitch = config.width * bpp; | ||
| 272 | } | ||
| 273 | params.height = config.height; | 285 | params.height = config.height; |
| 274 | params.unaligned_height = config.height; | 286 | params.unaligned_height = config.height; |
| 275 | params.target = SurfaceTarget::Texture2D; | 287 | params.target = SurfaceTarget::Texture2D; |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index eaf3e03a0..05ab01dcb 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp | |||
| @@ -2,12 +2,44 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | ||
| 6 | #include "video_core/engines/maxwell_3d.h" | ||
| 5 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 7 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 6 | 8 | ||
| 7 | namespace OpenGL::GLShader { | 9 | namespace OpenGL::GLShader { |
| 8 | 10 | ||
| 9 | using Tegra::Engines::Maxwell3D; | 11 | using Tegra::Engines::Maxwell3D; |
| 10 | 12 | ||
| 13 | ProgramManager::ProgramManager() { | ||
| 14 | pipeline.Create(); | ||
| 15 | } | ||
| 16 | |||
| 17 | ProgramManager::~ProgramManager() = default; | ||
| 18 | |||
| 19 | void ProgramManager::ApplyTo(OpenGLState& state) { | ||
| 20 | UpdatePipeline(); | ||
| 21 | state.draw.shader_program = 0; | ||
| 22 | state.draw.program_pipeline = pipeline.handle; | ||
| 23 | } | ||
| 24 | |||
| 25 | void ProgramManager::UpdatePipeline() { | ||
| 26 | // Avoid updating the pipeline when values have no changed | ||
| 27 | if (old_state == current_state) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | |||
| 31 | // Workaround for AMD bug | ||
| 32 | constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | | ||
| 33 | GL_FRAGMENT_SHADER_BIT}; | ||
| 34 | glUseProgramStages(pipeline.handle, all_used_stages, 0); | ||
| 35 | |||
| 36 | glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader); | ||
| 37 | glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, current_state.geometry_shader); | ||
| 38 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current_state.fragment_shader); | ||
| 39 | |||
| 40 | old_state = current_state; | ||
| 41 | } | ||
| 42 | |||
| 11 | void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) { | 43 | void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) { |
| 12 | const auto& regs = maxwell.regs; | 44 | const auto& regs = maxwell.regs; |
| 13 | const auto& state = maxwell.state; | 45 | const auto& state = maxwell.state; |
| @@ -16,7 +48,7 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shade | |||
| 16 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; | 48 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; |
| 17 | viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; | 49 | viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; |
| 18 | 50 | ||
| 19 | u32 func = static_cast<u32>(regs.alpha_test_func); | 51 | auto func{static_cast<u32>(regs.alpha_test_func)}; |
| 20 | // Normalize the gl variants of opCompare to be the same as the normal variants | 52 | // Normalize the gl variants of opCompare to be the same as the normal variants |
| 21 | const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never); | 53 | const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never); |
| 22 | if (func >= op_gl_variant_base) { | 54 | if (func >= op_gl_variant_base) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 37dcfefdb..cec18a832 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | |||
| 7 | #include <glad/glad.h> | 9 | #include <glad/glad.h> |
| 8 | 10 | ||
| 9 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 11 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| @@ -38,55 +40,48 @@ static_assert(sizeof(MaxwellUniformData) < 16384, | |||
| 38 | 40 | ||
| 39 | class ProgramManager { | 41 | class ProgramManager { |
| 40 | public: | 42 | public: |
| 41 | ProgramManager() { | 43 | explicit ProgramManager(); |
| 42 | pipeline.Create(); | 44 | ~ProgramManager(); |
| 43 | } | 45 | |
| 46 | void ApplyTo(OpenGLState& state); | ||
| 44 | 47 | ||
| 45 | void UseProgrammableVertexShader(GLuint program) { | 48 | void UseProgrammableVertexShader(GLuint program) { |
| 46 | vs = program; | 49 | current_state.vertex_shader = program; |
| 47 | } | 50 | } |
| 48 | 51 | ||
| 49 | void UseProgrammableGeometryShader(GLuint program) { | 52 | void UseProgrammableGeometryShader(GLuint program) { |
| 50 | gs = program; | 53 | current_state.geometry_shader = program; |
| 51 | } | 54 | } |
| 52 | 55 | ||
| 53 | void UseProgrammableFragmentShader(GLuint program) { | 56 | void UseProgrammableFragmentShader(GLuint program) { |
| 54 | fs = program; | 57 | current_state.fragment_shader = program; |
| 55 | } | 58 | } |
| 56 | 59 | ||
| 57 | void UseTrivialGeometryShader() { | 60 | void UseTrivialGeometryShader() { |
| 58 | gs = 0; | 61 | current_state.geometry_shader = 0; |
| 59 | } | ||
| 60 | |||
| 61 | void ApplyTo(OpenGLState& state) { | ||
| 62 | UpdatePipeline(); | ||
| 63 | state.draw.shader_program = 0; | ||
| 64 | state.draw.program_pipeline = pipeline.handle; | ||
| 65 | } | 62 | } |
| 66 | 63 | ||
| 67 | private: | 64 | private: |
| 68 | void UpdatePipeline() { | 65 | struct PipelineState { |
| 69 | // Avoid updating the pipeline when values have no changed | 66 | bool operator==(const PipelineState& rhs) const { |
| 70 | if (old_vs == vs && old_fs == fs && old_gs == gs) | 67 | return vertex_shader == rhs.vertex_shader && fragment_shader == rhs.fragment_shader && |
| 71 | return; | 68 | geometry_shader == rhs.geometry_shader; |
| 72 | // Workaround for AMD bug | 69 | } |
| 73 | glUseProgramStages(pipeline.handle, | 70 | |
| 74 | GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, | 71 | bool operator!=(const PipelineState& rhs) const { |
| 75 | 0); | 72 | return !operator==(rhs); |
| 76 | 73 | } | |
| 77 | glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vs); | 74 | |
| 78 | glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, gs); | 75 | GLuint vertex_shader{}; |
| 79 | glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); | 76 | GLuint fragment_shader{}; |
| 80 | 77 | GLuint geometry_shader{}; | |
| 81 | // Update the old values | 78 | }; |
| 82 | old_vs = vs; | 79 | |
| 83 | old_fs = fs; | 80 | void UpdatePipeline(); |
| 84 | old_gs = gs; | ||
| 85 | } | ||
| 86 | 81 | ||
| 87 | OGLPipeline pipeline; | 82 | OGLPipeline pipeline; |
| 88 | GLuint vs{}, fs{}, gs{}; | 83 | PipelineState current_state; |
| 89 | GLuint old_vs{}, old_fs{}, old_gs{}; | 84 | PipelineState old_state; |
| 90 | }; | 85 | }; |
| 91 | 86 | ||
| 92 | } // namespace OpenGL::GLShader | 87 | } // namespace OpenGL::GLShader |
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index a7ac26d71..3b022a456 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp | |||
| @@ -294,6 +294,8 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 294 | return PixelFormat::Z16; | 294 | return PixelFormat::Z16; |
| 295 | case Tegra::Texture::TextureFormat::Z24S8: | 295 | case Tegra::Texture::TextureFormat::Z24S8: |
| 296 | return PixelFormat::Z24S8; | 296 | return PixelFormat::Z24S8; |
| 297 | case Tegra::Texture::TextureFormat::ZF32_X24S8: | ||
| 298 | return PixelFormat::Z32FS8; | ||
| 297 | case Tegra::Texture::TextureFormat::DXT1: | 299 | case Tegra::Texture::TextureFormat::DXT1: |
| 298 | return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1; | 300 | return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1; |
| 299 | case Tegra::Texture::TextureFormat::DXT23: | 301 | case Tegra::Texture::TextureFormat::DXT23: |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7438fbc0a..c29f2d2dc 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -1,6 +1,13 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 1 | #include <QApplication> | 5 | #include <QApplication> |
| 2 | #include <QHBoxLayout> | 6 | #include <QHBoxLayout> |
| 3 | #include <QKeyEvent> | 7 | #include <QKeyEvent> |
| 8 | #include <QOffscreenSurface> | ||
| 9 | #include <QOpenGLWindow> | ||
| 10 | #include <QPainter> | ||
| 4 | #include <QScreen> | 11 | #include <QScreen> |
| 5 | #include <QWindow> | 12 | #include <QWindow> |
| 6 | #include <fmt/format.h> | 13 | #include <fmt/format.h> |
| @@ -82,13 +89,36 @@ void EmuThread::run() { | |||
| 82 | render_window->moveContext(); | 89 | render_window->moveContext(); |
| 83 | } | 90 | } |
| 84 | 91 | ||
| 92 | class GGLContext : public Core::Frontend::GraphicsContext { | ||
| 93 | public: | ||
| 94 | explicit GGLContext(QOpenGLContext* shared_context) : surface() { | ||
| 95 | context = std::make_unique<QOpenGLContext>(shared_context); | ||
| 96 | surface.setFormat(shared_context->format()); | ||
| 97 | surface.create(); | ||
| 98 | } | ||
| 99 | |||
| 100 | void MakeCurrent() override { | ||
| 101 | context->makeCurrent(&surface); | ||
| 102 | } | ||
| 103 | |||
| 104 | void DoneCurrent() override { | ||
| 105 | context->doneCurrent(); | ||
| 106 | } | ||
| 107 | |||
| 108 | void SwapBuffers() override {} | ||
| 109 | |||
| 110 | private: | ||
| 111 | std::unique_ptr<QOpenGLContext> context; | ||
| 112 | QOffscreenSurface surface; | ||
| 113 | }; | ||
| 114 | |||
| 85 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL | 115 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL |
| 86 | // context. | 116 | // context. |
| 87 | // The corresponding functionality is handled in EmuThread instead | 117 | // The corresponding functionality is handled in EmuThread instead |
| 88 | class GGLWidgetInternal : public QGLWidget { | 118 | class GGLWidgetInternal : public QOpenGLWindow { |
| 89 | public: | 119 | public: |
| 90 | GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) | 120 | GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) |
| 91 | : QGLWidget(fmt, parent), parent(parent) {} | 121 | : QOpenGLWindow(shared_context), parent(parent) {} |
| 92 | 122 | ||
| 93 | void paintEvent(QPaintEvent* ev) override { | 123 | void paintEvent(QPaintEvent* ev) override { |
| 94 | if (do_painting) { | 124 | if (do_painting) { |
| @@ -101,9 +131,51 @@ public: | |||
| 101 | parent->OnFramebufferSizeChanged(); | 131 | parent->OnFramebufferSizeChanged(); |
| 102 | } | 132 | } |
| 103 | 133 | ||
| 134 | void keyPressEvent(QKeyEvent* event) override { | ||
| 135 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 136 | } | ||
| 137 | |||
| 138 | void keyReleaseEvent(QKeyEvent* event) override { | ||
| 139 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 140 | } | ||
| 141 | |||
| 142 | void mousePressEvent(QMouseEvent* event) override { | ||
| 143 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 144 | return; // touch input is handled in TouchBeginEvent | ||
| 145 | |||
| 146 | const auto pos{event->pos()}; | ||
| 147 | if (event->button() == Qt::LeftButton) { | ||
| 148 | const auto [x, y] = parent->ScaleTouch(pos); | ||
| 149 | parent->TouchPressed(x, y); | ||
| 150 | } else if (event->button() == Qt::RightButton) { | ||
| 151 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | void mouseMoveEvent(QMouseEvent* event) override { | ||
| 156 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 157 | return; // touch input is handled in TouchUpdateEvent | ||
| 158 | |||
| 159 | const auto pos{event->pos()}; | ||
| 160 | const auto [x, y] = parent->ScaleTouch(pos); | ||
| 161 | parent->TouchMoved(x, y); | ||
| 162 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||
| 163 | } | ||
| 164 | |||
| 165 | void mouseReleaseEvent(QMouseEvent* event) override { | ||
| 166 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 167 | return; // touch input is handled in TouchEndEvent | ||
| 168 | |||
| 169 | if (event->button() == Qt::LeftButton) | ||
| 170 | parent->TouchReleased(); | ||
| 171 | else if (event->button() == Qt::RightButton) | ||
| 172 | InputCommon::GetMotionEmu()->EndTilt(); | ||
| 173 | } | ||
| 174 | |||
| 104 | void DisablePainting() { | 175 | void DisablePainting() { |
| 105 | do_painting = false; | 176 | do_painting = false; |
| 106 | } | 177 | } |
| 178 | |||
| 107 | void EnablePainting() { | 179 | void EnablePainting() { |
| 108 | do_painting = true; | 180 | do_painting = true; |
| 109 | } | 181 | } |
| @@ -114,7 +186,7 @@ private: | |||
| 114 | }; | 186 | }; |
| 115 | 187 | ||
| 116 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 188 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) |
| 117 | : QWidget(parent), child(nullptr), emu_thread(emu_thread) { | 189 | : QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) { |
| 118 | 190 | ||
| 119 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 191 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 120 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); | 192 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); |
| @@ -137,19 +209,19 @@ void GRenderWindow::moveContext() { | |||
| 137 | auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) | 209 | auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) |
| 138 | ? emu_thread | 210 | ? emu_thread |
| 139 | : qApp->thread(); | 211 | : qApp->thread(); |
| 140 | child->context()->moveToThread(thread); | 212 | context->moveToThread(thread); |
| 141 | } | 213 | } |
| 142 | 214 | ||
| 143 | void GRenderWindow::SwapBuffers() { | 215 | void GRenderWindow::SwapBuffers() { |
| 144 | // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, | 216 | // In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`, |
| 145 | // since we never call `doneCurrent` in this thread. | 217 | // since we never call `doneCurrent` in this thread. |
| 146 | // However: | 218 | // However: |
| 147 | // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called | 219 | // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called |
| 148 | // since the last time `swapBuffers` was executed; | 220 | // since the last time `swapBuffers` was executed; |
| 149 | // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. | 221 | // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. |
| 150 | child->makeCurrent(); | 222 | context->makeCurrent(child); |
| 151 | 223 | ||
| 152 | child->swapBuffers(); | 224 | context->swapBuffers(child); |
| 153 | if (!first_frame) { | 225 | if (!first_frame) { |
| 154 | emit FirstFrameDisplayed(); | 226 | emit FirstFrameDisplayed(); |
| 155 | first_frame = true; | 227 | first_frame = true; |
| @@ -157,11 +229,11 @@ void GRenderWindow::SwapBuffers() { | |||
| 157 | } | 229 | } |
| 158 | 230 | ||
| 159 | void GRenderWindow::MakeCurrent() { | 231 | void GRenderWindow::MakeCurrent() { |
| 160 | child->makeCurrent(); | 232 | context->makeCurrent(child); |
| 161 | } | 233 | } |
| 162 | 234 | ||
| 163 | void GRenderWindow::DoneCurrent() { | 235 | void GRenderWindow::DoneCurrent() { |
| 164 | child->doneCurrent(); | 236 | context->doneCurrent(); |
| 165 | } | 237 | } |
| 166 | 238 | ||
| 167 | void GRenderWindow::PollEvents() {} | 239 | void GRenderWindow::PollEvents() {} |
| @@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {} | |||
| 174 | void GRenderWindow::OnFramebufferSizeChanged() { | 246 | void GRenderWindow::OnFramebufferSizeChanged() { |
| 175 | // Screen changes potentially incur a change in screen DPI, hence we should update the | 247 | // Screen changes potentially incur a change in screen DPI, hence we should update the |
| 176 | // framebuffer size | 248 | // framebuffer size |
| 177 | qreal pixelRatio = windowPixelRatio(); | 249 | qreal pixelRatio = GetWindowPixelRatio(); |
| 178 | unsigned width = child->QPaintDevice::width() * pixelRatio; | 250 | unsigned width = child->QPaintDevice::width() * pixelRatio; |
| 179 | unsigned height = child->QPaintDevice::height() * pixelRatio; | 251 | unsigned height = child->QPaintDevice::height() * pixelRatio; |
| 180 | UpdateCurrentFramebufferLayout(width, height); | 252 | UpdateCurrentFramebufferLayout(width, height); |
| 181 | } | 253 | } |
| 182 | 254 | ||
| 255 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { | ||
| 256 | if (child) { | ||
| 257 | child->keyPressEvent(event); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { | ||
| 262 | if (child) { | ||
| 263 | child->keyReleaseEvent(event); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 183 | void GRenderWindow::BackupGeometry() { | 267 | void GRenderWindow::BackupGeometry() { |
| 184 | geometry = ((QGLWidget*)this)->saveGeometry(); | 268 | geometry = ((QWidget*)this)->saveGeometry(); |
| 185 | } | 269 | } |
| 186 | 270 | ||
| 187 | void GRenderWindow::RestoreGeometry() { | 271 | void GRenderWindow::RestoreGeometry() { |
| @@ -199,18 +283,18 @@ QByteArray GRenderWindow::saveGeometry() { | |||
| 199 | // If we are a top-level widget, store the current geometry | 283 | // If we are a top-level widget, store the current geometry |
| 200 | // otherwise, store the last backup | 284 | // otherwise, store the last backup |
| 201 | if (parent() == nullptr) | 285 | if (parent() == nullptr) |
| 202 | return ((QGLWidget*)this)->saveGeometry(); | 286 | return ((QWidget*)this)->saveGeometry(); |
| 203 | else | 287 | else |
| 204 | return geometry; | 288 | return geometry; |
| 205 | } | 289 | } |
| 206 | 290 | ||
| 207 | qreal GRenderWindow::windowPixelRatio() const { | 291 | qreal GRenderWindow::GetWindowPixelRatio() const { |
| 208 | // windowHandle() might not be accessible until the window is displayed to screen. | 292 | // windowHandle() might not be accessible until the window is displayed to screen. |
| 209 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; | 293 | return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; |
| 210 | } | 294 | } |
| 211 | 295 | ||
| 212 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { | 296 | std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { |
| 213 | const qreal pixel_ratio = windowPixelRatio(); | 297 | const qreal pixel_ratio = GetWindowPixelRatio(); |
| 214 | return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 298 | return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), |
| 215 | static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | 299 | static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| 216 | } | 300 | } |
| @@ -220,47 +304,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { | |||
| 220 | QWidget::closeEvent(event); | 304 | QWidget::closeEvent(event); |
| 221 | } | 305 | } |
| 222 | 306 | ||
| 223 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | ||
| 224 | InputCommon::GetKeyboard()->PressKey(event->key()); | ||
| 225 | } | ||
| 226 | |||
| 227 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 228 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); | ||
| 229 | } | ||
| 230 | |||
| 231 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | ||
| 232 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 233 | return; // touch input is handled in TouchBeginEvent | ||
| 234 | |||
| 235 | auto pos = event->pos(); | ||
| 236 | if (event->button() == Qt::LeftButton) { | ||
| 237 | const auto [x, y] = ScaleTouch(pos); | ||
| 238 | this->TouchPressed(x, y); | ||
| 239 | } else if (event->button() == Qt::RightButton) { | ||
| 240 | InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { | ||
| 245 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 246 | return; // touch input is handled in TouchUpdateEvent | ||
| 247 | |||
| 248 | auto pos = event->pos(); | ||
| 249 | const auto [x, y] = ScaleTouch(pos); | ||
| 250 | this->TouchMoved(x, y); | ||
| 251 | InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); | ||
| 252 | } | ||
| 253 | |||
| 254 | void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | ||
| 255 | if (event->source() == Qt::MouseEventSynthesizedBySystem) | ||
| 256 | return; // touch input is handled in TouchEndEvent | ||
| 257 | |||
| 258 | if (event->button() == Qt::LeftButton) | ||
| 259 | this->TouchReleased(); | ||
| 260 | else if (event->button() == Qt::RightButton) | ||
| 261 | InputCommon::GetMotionEmu()->EndTilt(); | ||
| 262 | } | ||
| 263 | |||
| 264 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { | 307 | void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { |
| 265 | // TouchBegin always has exactly one touch point, so take the .first() | 308 | // TouchBegin always has exactly one touch point, so take the .first() |
| 266 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); | 309 | const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); |
| @@ -313,35 +356,60 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { | |||
| 313 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); | 356 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); |
| 314 | } | 357 | } |
| 315 | 358 | ||
| 359 | std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { | ||
| 360 | return std::make_unique<GGLContext>(shared_context.get()); | ||
| 361 | } | ||
| 362 | |||
| 316 | void GRenderWindow::InitRenderTarget() { | 363 | void GRenderWindow::InitRenderTarget() { |
| 317 | if (child) { | 364 | shared_context.reset(); |
| 318 | delete child; | 365 | context.reset(); |
| 319 | } | ||
| 320 | 366 | ||
| 321 | if (layout()) { | 367 | delete child; |
| 322 | delete layout(); | 368 | child = nullptr; |
| 323 | } | 369 | |
| 370 | delete container; | ||
| 371 | container = nullptr; | ||
| 372 | |||
| 373 | delete layout(); | ||
| 324 | 374 | ||
| 325 | first_frame = false; | 375 | first_frame = false; |
| 326 | 376 | ||
| 327 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 377 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 328 | // WA_DontShowOnScreen, WA_DeleteOnClose | 378 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 329 | QGLFormat fmt; | 379 | QSurfaceFormat fmt; |
| 330 | fmt.setVersion(4, 3); | 380 | fmt.setVersion(4, 3); |
| 331 | fmt.setProfile(QGLFormat::CoreProfile); | 381 | fmt.setProfile(QSurfaceFormat::CoreProfile); |
| 382 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | ||
| 383 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||
| 384 | shared_context = std::make_unique<QOpenGLContext>(); | ||
| 385 | shared_context->setFormat(fmt); | ||
| 386 | shared_context->create(); | ||
| 387 | context = std::make_unique<QOpenGLContext>(); | ||
| 388 | context->setShareContext(shared_context.get()); | ||
| 389 | context->setFormat(fmt); | ||
| 390 | context->create(); | ||
| 332 | fmt.setSwapInterval(false); | 391 | fmt.setSwapInterval(false); |
| 333 | 392 | ||
| 334 | // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X | 393 | child = new GGLWidgetInternal(this, shared_context.get()); |
| 335 | fmt.setOption(QGL::NoDeprecatedFunctions); | 394 | container = QWidget::createWindowContainer(child, this); |
| 336 | 395 | ||
| 337 | child = new GGLWidgetInternal(fmt, this); | ||
| 338 | QBoxLayout* layout = new QHBoxLayout(this); | 396 | QBoxLayout* layout = new QHBoxLayout(this); |
| 339 | 397 | layout->addWidget(container); | |
| 340 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 341 | layout->addWidget(child); | ||
| 342 | layout->setMargin(0); | 398 | layout->setMargin(0); |
| 343 | setLayout(layout); | 399 | setLayout(layout); |
| 344 | 400 | ||
| 401 | // Reset minimum size to avoid unwanted resizes when this function is called for a second time. | ||
| 402 | setMinimumSize(1, 1); | ||
| 403 | |||
| 404 | // Show causes the window to actually be created and the OpenGL context as well, but we don't | ||
| 405 | // want the widget to be shown yet, so immediately hide it. | ||
| 406 | show(); | ||
| 407 | hide(); | ||
| 408 | |||
| 409 | resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 410 | child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 411 | container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); | ||
| 412 | |||
| 345 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 413 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 346 | 414 | ||
| 347 | OnFramebufferSizeChanged(); | 415 | OnFramebufferSizeChanged(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 3183621bc..9608b959f 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -7,9 +7,9 @@ | |||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <condition_variable> | 8 | #include <condition_variable> |
| 9 | #include <mutex> | 9 | #include <mutex> |
| 10 | #include <QGLWidget> | ||
| 11 | #include <QImage> | 10 | #include <QImage> |
| 12 | #include <QThread> | 11 | #include <QThread> |
| 12 | #include <QWidget> | ||
| 13 | #include "common/thread.h" | 13 | #include "common/thread.h" |
| 14 | #include "core/core.h" | 14 | #include "core/core.h" |
| 15 | #include "core/frontend/emu_window.h" | 15 | #include "core/frontend/emu_window.h" |
| @@ -21,6 +21,8 @@ class QTouchEvent; | |||
| 21 | class GGLWidgetInternal; | 21 | class GGLWidgetInternal; |
| 22 | class GMainWindow; | 22 | class GMainWindow; |
| 23 | class GRenderWindow; | 23 | class GRenderWindow; |
| 24 | class QSurface; | ||
| 25 | class QOpenGLContext; | ||
| 24 | 26 | ||
| 25 | namespace VideoCore { | 27 | namespace VideoCore { |
| 26 | enum class LoadCallbackStage; | 28 | enum class LoadCallbackStage; |
| @@ -121,25 +123,21 @@ public: | |||
| 121 | void MakeCurrent() override; | 123 | void MakeCurrent() override; |
| 122 | void DoneCurrent() override; | 124 | void DoneCurrent() override; |
| 123 | void PollEvents() override; | 125 | void PollEvents() override; |
| 126 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 127 | |||
| 128 | void ForwardKeyPressEvent(QKeyEvent* event); | ||
| 129 | void ForwardKeyReleaseEvent(QKeyEvent* event); | ||
| 124 | 130 | ||
| 125 | void BackupGeometry(); | 131 | void BackupGeometry(); |
| 126 | void RestoreGeometry(); | 132 | void RestoreGeometry(); |
| 127 | void restoreGeometry(const QByteArray& geometry); // overridden | 133 | void restoreGeometry(const QByteArray& geometry); // overridden |
| 128 | QByteArray saveGeometry(); // overridden | 134 | QByteArray saveGeometry(); // overridden |
| 129 | 135 | ||
| 130 | qreal windowPixelRatio() const; | 136 | qreal GetWindowPixelRatio() const; |
| 137 | std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||
| 131 | 138 | ||
| 132 | void closeEvent(QCloseEvent* event) override; | 139 | void closeEvent(QCloseEvent* event) override; |
| 133 | |||
| 134 | void keyPressEvent(QKeyEvent* event) override; | ||
| 135 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 136 | |||
| 137 | void mousePressEvent(QMouseEvent* event) override; | ||
| 138 | void mouseMoveEvent(QMouseEvent* event) override; | ||
| 139 | void mouseReleaseEvent(QMouseEvent* event) override; | ||
| 140 | |||
| 141 | bool event(QEvent* event) override; | 140 | bool event(QEvent* event) override; |
| 142 | |||
| 143 | void focusOutEvent(QFocusEvent* event) override; | 141 | void focusOutEvent(QFocusEvent* event) override; |
| 144 | 142 | ||
| 145 | void OnClientAreaResized(unsigned width, unsigned height); | 143 | void OnClientAreaResized(unsigned width, unsigned height); |
| @@ -161,7 +159,6 @@ signals: | |||
| 161 | void FirstFrameDisplayed(); | 159 | void FirstFrameDisplayed(); |
| 162 | 160 | ||
| 163 | private: | 161 | private: |
| 164 | std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; | ||
| 165 | void TouchBeginEvent(const QTouchEvent* event); | 162 | void TouchBeginEvent(const QTouchEvent* event); |
| 166 | void TouchUpdateEvent(const QTouchEvent* event); | 163 | void TouchUpdateEvent(const QTouchEvent* event); |
| 167 | void TouchEndEvent(); | 164 | void TouchEndEvent(); |
| @@ -169,11 +166,17 @@ private: | |||
| 169 | void OnMinimalClientAreaChangeRequest( | 166 | void OnMinimalClientAreaChangeRequest( |
| 170 | const std::pair<unsigned, unsigned>& minimal_size) override; | 167 | const std::pair<unsigned, unsigned>& minimal_size) override; |
| 171 | 168 | ||
| 172 | GGLWidgetInternal* child; | 169 | QWidget* container = nullptr; |
| 170 | GGLWidgetInternal* child = nullptr; | ||
| 173 | 171 | ||
| 174 | QByteArray geometry; | 172 | QByteArray geometry; |
| 175 | 173 | ||
| 176 | EmuThread* emu_thread; | 174 | EmuThread* emu_thread; |
| 175 | // Context that backs the GGLWidgetInternal (and will be used by core to render) | ||
| 176 | std::unique_ptr<QOpenGLContext> context; | ||
| 177 | // Context that will be shared between all newly created contexts. This should never be made | ||
| 178 | // current | ||
| 179 | std::unique_ptr<QOpenGLContext> shared_context; | ||
| 177 | 180 | ||
| 178 | /// Temporary storage of the screenshot taken | 181 | /// Temporary storage of the screenshot taken |
| 179 | QImage screenshot_image; | 182 | QImage screenshot_image; |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 802db3945..8725a78dc 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -394,6 +394,7 @@ void Config::ReadValues() { | |||
| 394 | ReadSetting("use_accurate_gpu_emulation", false).toBool(); | 394 | ReadSetting("use_accurate_gpu_emulation", false).toBool(); |
| 395 | Settings::values.use_asynchronous_gpu_emulation = | 395 | Settings::values.use_asynchronous_gpu_emulation = |
| 396 | ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); | 396 | ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); |
| 397 | Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool(); | ||
| 397 | 398 | ||
| 398 | Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); | 399 | Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); |
| 399 | Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); | 400 | Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); |
| @@ -523,8 +524,8 @@ void Config::ReadValues() { | |||
| 523 | qt_config->beginGroup("Paths"); | 524 | qt_config->beginGroup("Paths"); |
| 524 | UISettings::values.roms_path = ReadSetting("romsPath").toString(); | 525 | UISettings::values.roms_path = ReadSetting("romsPath").toString(); |
| 525 | UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); | 526 | UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); |
| 526 | UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString(); | 527 | UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString(); |
| 527 | UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool(); | 528 | UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool(); |
| 528 | UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); | 529 | UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); |
| 529 | qt_config->endGroup(); | 530 | qt_config->endGroup(); |
| 530 | 531 | ||
| @@ -664,6 +665,7 @@ void Config::SaveValues() { | |||
| 664 | WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); | 665 | WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); |
| 665 | WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, | 666 | WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, |
| 666 | false); | 667 | false); |
| 668 | WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false); | ||
| 667 | 669 | ||
| 668 | // Cast to double because Qt's written float values are not human-readable | 670 | // Cast to double because Qt's written float values are not human-readable |
| 669 | WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); | 671 | WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); |
| @@ -768,8 +770,8 @@ void Config::SaveValues() { | |||
| 768 | WriteSetting("romsPath", UISettings::values.roms_path); | 770 | WriteSetting("romsPath", UISettings::values.roms_path); |
| 769 | WriteSetting("symbolsPath", UISettings::values.symbols_path); | 771 | WriteSetting("symbolsPath", UISettings::values.symbols_path); |
| 770 | WriteSetting("screenshotPath", UISettings::values.screenshot_path); | 772 | WriteSetting("screenshotPath", UISettings::values.screenshot_path); |
| 771 | WriteSetting("gameListRootDir", UISettings::values.gamedir, "."); | 773 | WriteSetting("gameListRootDir", UISettings::values.game_directory_path, "."); |
| 772 | WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false); | 774 | WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false); |
| 773 | WriteSetting("recentFiles", UISettings::values.recent_files); | 775 | WriteSetting("recentFiles", UISettings::values.recent_files); |
| 774 | qt_config->endGroup(); | 776 | qt_config->endGroup(); |
| 775 | 777 | ||
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index eeb038afb..e48f4f5a3 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -28,7 +28,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 28 | ConfigureGeneral::~ConfigureGeneral() = default; | 28 | ConfigureGeneral::~ConfigureGeneral() = default; |
| 29 | 29 | ||
| 30 | void ConfigureGeneral::setConfiguration() { | 30 | void ConfigureGeneral::setConfiguration() { |
| 31 | ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); | 31 | ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan); |
| 32 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 32 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 33 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); | 33 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); |
| 34 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 34 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| @@ -36,7 +36,7 @@ void ConfigureGeneral::setConfiguration() { | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | void ConfigureGeneral::applyConfiguration() { | 38 | void ConfigureGeneral::applyConfiguration() { |
| 39 | UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); | 39 | UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked(); |
| 40 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); | 40 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); |
| 41 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); | 41 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); |
| 42 | UISettings::values.theme = | 42 | UISettings::values.theme = |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index dd1d67488..0a9883d37 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -77,6 +77,8 @@ void ConfigureGraphics::setConfiguration() { | |||
| 77 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 77 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| 78 | ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 78 | ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |
| 79 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); | 79 | ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); |
| 80 | ui->force_30fps_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||
| 81 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | ||
| 80 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 82 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| 81 | Settings::values.bg_blue)); | 83 | Settings::values.bg_blue)); |
| 82 | } | 84 | } |
| @@ -90,6 +92,7 @@ void ConfigureGraphics::applyConfiguration() { | |||
| 90 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 92 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); |
| 91 | Settings::values.use_asynchronous_gpu_emulation = | 93 | Settings::values.use_asynchronous_gpu_emulation = |
| 92 | ui->use_asynchronous_gpu_emulation->isChecked(); | 94 | ui->use_asynchronous_gpu_emulation->isChecked(); |
| 95 | Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); | ||
| 93 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); | 96 | Settings::values.bg_red = static_cast<float>(bg_color.redF()); |
| 94 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); | 97 | Settings::values.bg_green = static_cast<float>(bg_color.greenF()); |
| 95 | Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); | 98 | Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index c6767e0ca..15ab18ecd 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -71,6 +71,13 @@ | |||
| 71 | </widget> | 71 | </widget> |
| 72 | </item> | 72 | </item> |
| 73 | <item> | 73 | <item> |
| 74 | <widget class="QCheckBox" name="force_30fps_mode"> | ||
| 75 | <property name="text"> | ||
| 76 | <string>Force 30 FPS mode</string> | ||
| 77 | </property> | ||
| 78 | </widget> | ||
| 79 | </item> | ||
| 80 | <item> | ||
| 74 | <layout class="QHBoxLayout" name="horizontalLayout"> | 81 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 75 | <item> | 82 | <item> |
| 76 | <widget class="QLabel" name="label"> | 83 | <widget class="QLabel" name="label"> |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 4b67656ac..b0ca766ec 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -467,9 +467,10 @@ void GameList::LoadInterfaceLayout() { | |||
| 467 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; | 467 | const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; |
| 468 | 468 | ||
| 469 | void GameList::RefreshGameDirectory() { | 469 | void GameList::RefreshGameDirectory() { |
| 470 | if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { | 470 | if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { |
| 471 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); | 471 | LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); |
| 472 | search_field->clear(); | 472 | search_field->clear(); |
| 473 | PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 473 | PopulateAsync(UISettings::values.game_directory_path, |
| 474 | UISettings::values.game_directory_deepscan); | ||
| 474 | } | 475 | } |
| 475 | } | 476 | } |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ca231d710..bdee44b04 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -213,7 +213,8 @@ GMainWindow::GMainWindow() | |||
| 213 | OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); | 213 | OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); |
| 214 | 214 | ||
| 215 | game_list->LoadCompatibilityList(); | 215 | game_list->LoadCompatibilityList(); |
| 216 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 216 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 217 | UISettings::values.game_directory_deepscan); | ||
| 217 | 218 | ||
| 218 | // Show one-time "callout" messages to the user | 219 | // Show one-time "callout" messages to the user |
| 219 | ShowTelemetryCallout(); | 220 | ShowTelemetryCallout(); |
| @@ -1278,8 +1279,8 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | |||
| 1278 | 1279 | ||
| 1279 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); | 1280 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); |
| 1280 | if (reload) { | 1281 | if (reload) { |
| 1281 | game_list->PopulateAsync(UISettings::values.gamedir, | 1282 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1282 | UISettings::values.gamedir_deepscan); | 1283 | UISettings::values.game_directory_deepscan); |
| 1283 | } | 1284 | } |
| 1284 | 1285 | ||
| 1285 | config->Save(); | 1286 | config->Save(); |
| @@ -1367,7 +1368,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1367 | const auto success = [this]() { | 1368 | const auto success = [this]() { |
| 1368 | QMessageBox::information(this, tr("Successfully Installed"), | 1369 | QMessageBox::information(this, tr("Successfully Installed"), |
| 1369 | tr("The file was successfully installed.")); | 1370 | tr("The file was successfully installed.")); |
| 1370 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1371 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1372 | UISettings::values.game_directory_deepscan); | ||
| 1371 | }; | 1373 | }; |
| 1372 | 1374 | ||
| 1373 | const auto failed = [this]() { | 1375 | const auto failed = [this]() { |
| @@ -1494,8 +1496,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 1494 | void GMainWindow::OnMenuSelectGameListRoot() { | 1496 | void GMainWindow::OnMenuSelectGameListRoot() { |
| 1495 | QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); | 1497 | QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); |
| 1496 | if (!dir_path.isEmpty()) { | 1498 | if (!dir_path.isEmpty()) { |
| 1497 | UISettings::values.gamedir = dir_path; | 1499 | UISettings::values.game_directory_path = dir_path; |
| 1498 | game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan); | 1500 | game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan); |
| 1499 | } | 1501 | } |
| 1500 | } | 1502 | } |
| 1501 | 1503 | ||
| @@ -1517,7 +1519,8 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) | |||
| 1517 | : FileUtil::UserPath::NANDDir, | 1519 | : FileUtil::UserPath::NANDDir, |
| 1518 | dir_path.toStdString()); | 1520 | dir_path.toStdString()); |
| 1519 | Service::FileSystem::CreateFactories(*vfs); | 1521 | Service::FileSystem::CreateFactories(*vfs); |
| 1520 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1522 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1523 | UISettings::values.game_directory_deepscan); | ||
| 1521 | } | 1524 | } |
| 1522 | } | 1525 | } |
| 1523 | 1526 | ||
| @@ -1669,8 +1672,8 @@ void GMainWindow::OnConfigure() { | |||
| 1669 | 1672 | ||
| 1670 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); | 1673 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); |
| 1671 | if (reload) { | 1674 | if (reload) { |
| 1672 | game_list->PopulateAsync(UISettings::values.gamedir, | 1675 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1673 | UISettings::values.gamedir_deepscan); | 1676 | UISettings::values.game_directory_deepscan); |
| 1674 | } | 1677 | } |
| 1675 | 1678 | ||
| 1676 | config->Save(); | 1679 | config->Save(); |
| @@ -1920,7 +1923,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 1920 | Service::FileSystem::CreateFactories(*vfs); | 1923 | Service::FileSystem::CreateFactories(*vfs); |
| 1921 | 1924 | ||
| 1922 | if (behavior == ReinitializeKeyBehavior::Warning) { | 1925 | if (behavior == ReinitializeKeyBehavior::Warning) { |
| 1923 | game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); | 1926 | game_list->PopulateAsync(UISettings::values.game_directory_path, |
| 1927 | UISettings::values.game_directory_deepscan); | ||
| 1924 | } | 1928 | } |
| 1925 | } | 1929 | } |
| 1926 | 1930 | ||
| @@ -2027,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { | |||
| 2027 | event->acceptProposedAction(); | 2031 | event->acceptProposedAction(); |
| 2028 | } | 2032 | } |
| 2029 | 2033 | ||
| 2034 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | ||
| 2035 | if (render_window) { | ||
| 2036 | render_window->ForwardKeyPressEvent(event); | ||
| 2037 | } | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 2041 | if (render_window) { | ||
| 2042 | render_window->ForwardKeyReleaseEvent(event); | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | |||
| 2030 | bool GMainWindow::ConfirmChangeGame() { | 2046 | bool GMainWindow::ConfirmChangeGame() { |
| 2031 | if (emu_thread == nullptr) | 2047 | if (emu_thread == nullptr) |
| 2032 | return true; | 2048 | return true; |
| @@ -2094,7 +2110,8 @@ int main(int argc, char* argv[]) { | |||
| 2094 | QCoreApplication::setOrganizationName("yuzu team"); | 2110 | QCoreApplication::setOrganizationName("yuzu team"); |
| 2095 | QCoreApplication::setApplicationName("yuzu"); | 2111 | QCoreApplication::setApplicationName("yuzu"); |
| 2096 | 2112 | ||
| 2097 | QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2113 | // Enables the core to make the qt created contexts current on std::threads |
| 2114 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | ||
| 2098 | QApplication app(argc, argv); | 2115 | QApplication app(argc, argv); |
| 2099 | 2116 | ||
| 2100 | // Qt changes the locale and causes issues in float conversion using std::to_string() when | 2117 | // 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 85e3810f2..ce5045819 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -252,4 +252,8 @@ protected: | |||
| 252 | void dropEvent(QDropEvent* event) override; | 252 | void dropEvent(QDropEvent* event) override; |
| 253 | void dragEnterEvent(QDragEnterEvent* event) override; | 253 | void dragEnterEvent(QDragEnterEvent* event) override; |
| 254 | void dragMoveEvent(QDragMoveEvent* event) override; | 254 | void dragMoveEvent(QDragMoveEvent* event) override; |
| 255 | |||
| 256 | // Overrides used to forward signals to the render window when the focus moves out. | ||
| 257 | void keyPressEvent(QKeyEvent* event) override; | ||
| 258 | void keyReleaseEvent(QKeyEvent* event) override; | ||
| 255 | }; | 259 | }; |
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index 45e705b61..dbd318e20 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h | |||
| @@ -55,8 +55,8 @@ struct Values { | |||
| 55 | QString roms_path; | 55 | QString roms_path; |
| 56 | QString symbols_path; | 56 | QString symbols_path; |
| 57 | QString screenshot_path; | 57 | QString screenshot_path; |
| 58 | QString gamedir; | 58 | QString game_directory_path; |
| 59 | bool gamedir_deepscan; | 59 | bool game_directory_deepscan; |
| 60 | QStringList recent_files; | 60 | QStringList recent_files; |
| 61 | 61 | ||
| 62 | QString theme; | 62 | QString theme; |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index de7a26e14..68a176032 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -19,6 +19,37 @@ | |||
| 19 | #include "input_common/sdl/sdl.h" | 19 | #include "input_common/sdl/sdl.h" |
| 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 20 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 21 | 21 | ||
| 22 | class SDLGLContext : public Core::Frontend::GraphicsContext { | ||
| 23 | public: | ||
| 24 | explicit SDLGLContext() { | ||
| 25 | // create a hidden window to make the shared context against | ||
| 26 | window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 27 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 28 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 29 | SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); | ||
| 30 | context = SDL_GL_CreateContext(window); | ||
| 31 | } | ||
| 32 | |||
| 33 | ~SDLGLContext() { | ||
| 34 | SDL_GL_DeleteContext(context); | ||
| 35 | SDL_DestroyWindow(window); | ||
| 36 | } | ||
| 37 | |||
| 38 | void MakeCurrent() override { | ||
| 39 | SDL_GL_MakeCurrent(window, context); | ||
| 40 | } | ||
| 41 | |||
| 42 | void DoneCurrent() override { | ||
| 43 | SDL_GL_MakeCurrent(window, nullptr); | ||
| 44 | } | ||
| 45 | |||
| 46 | void SwapBuffers() override {} | ||
| 47 | |||
| 48 | private: | ||
| 49 | SDL_Window* window; | ||
| 50 | SDL_GLContext context; | ||
| 51 | }; | ||
| 52 | |||
| 22 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | 53 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { |
| 23 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); | 54 | TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); |
| 24 | InputCommon::GetMotionEmu()->Tilt(x, y); | 55 | InputCommon::GetMotionEmu()->Tilt(x, y); |
| @@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 153 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); | 184 | SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); |
| 154 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); | 185 | SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |
| 155 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); | 186 | SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); |
| 187 | SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); | ||
| 156 | 188 | ||
| 157 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, | 189 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, |
| 158 | Common::g_scm_branch, Common::g_scm_desc); | 190 | Common::g_scm_branch, Common::g_scm_desc); |
| @@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 171 | if (fullscreen) { | 203 | if (fullscreen) { |
| 172 | Fullscreen(); | 204 | Fullscreen(); |
| 173 | } | 205 | } |
| 174 | |||
| 175 | gl_context = SDL_GL_CreateContext(render_window); | 206 | gl_context = SDL_GL_CreateContext(render_window); |
| 176 | 207 | ||
| 177 | if (gl_context == nullptr) { | 208 | if (gl_context == nullptr) { |
| @@ -278,3 +309,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( | |||
| 278 | 309 | ||
| 279 | SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); | 310 | SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); |
| 280 | } | 311 | } |
| 312 | |||
| 313 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const { | ||
| 314 | return std::make_unique<SDLGLContext>(); | ||
| 315 | } | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index b0d4116cc..17e98227f 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -27,6 +27,8 @@ public: | |||
| 27 | /// Releases the GL context from the caller thread | 27 | /// Releases the GL context from the caller thread |
| 28 | void DoneCurrent() override; | 28 | void DoneCurrent() override; |
| 29 | 29 | ||
| 30 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 31 | |||
| 30 | /// Whether the window is still open, and a close request hasn't yet been sent | 32 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 31 | bool IsOpen() const; | 33 | bool IsOpen() const; |
| 32 | 34 | ||