diff options
Diffstat (limited to 'src')
91 files changed, 4100 insertions, 844 deletions
diff --git a/src/common/lz4_compression.h b/src/common/lz4_compression.h index fe2231a6c..4c16f6e03 100644 --- a/src/common/lz4_compression.h +++ b/src/common/lz4_compression.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <vector> #include "common/common_types.h" 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 { template <typename Func> ScopeExitHelper<Func> ScopeExit(Func&& func) { - return ScopeExitHelper<Func>(std::move(func)); + return ScopeExitHelper<Func>(std::forward<Func>(func)); } } // namespace detail 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 @@ #if defined(_MSC_VER) #include <cstdlib> -#elif defined(__linux__) -#include <byteswap.h> -#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || \ - defined(__NetBSD__) || defined(__OpenBSD__) -#include <sys/endian.h> #endif #include <cstring> #include "common/common_types.h" @@ -62,86 +57,49 @@ namespace Common { #ifdef _MSC_VER -inline u16 swap16(u16 _data) { - return _byteswap_ushort(_data); +[[nodiscard]] inline u16 swap16(u16 data) noexcept { + return _byteswap_ushort(data); } -inline u32 swap32(u32 _data) { - return _byteswap_ulong(_data); +[[nodiscard]] inline u32 swap32(u32 data) noexcept { + return _byteswap_ulong(data); } -inline u64 swap64(u64 _data) { - return _byteswap_uint64(_data); +[[nodiscard]] inline u64 swap64(u64 data) noexcept { + return _byteswap_uint64(data); } -#elif defined(ARCHITECTURE_ARM) && (__ARM_ARCH >= 6) -inline u16 swap16(u16 _data) { - u32 data = _data; - __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); - return (u16)data; -} -inline u32 swap32(u32 _data) { - __asm__("rev %0, %1\n" : "=l"(_data) : "l"(_data)); - return _data; -} -inline u64 swap64(u64 _data) { - return ((u64)swap32(_data) << 32) | swap32(_data >> 32); -} -#elif __linux__ -inline u16 swap16(u16 _data) { - return bswap_16(_data); -} -inline u32 swap32(u32 _data) { - return bswap_32(_data); -} -inline u64 swap64(u64 _data) { - return bswap_64(_data); -} -#elif __APPLE__ -inline __attribute__((always_inline)) u16 swap16(u16 _data) { - return (_data >> 8) | (_data << 8); -} -inline __attribute__((always_inline)) u32 swap32(u32 _data) { - return __builtin_bswap32(_data); -} -inline __attribute__((always_inline)) u64 swap64(u64 _data) { - return __builtin_bswap64(_data); -} -#elif defined(__Bitrig__) || defined(__OpenBSD__) +#elif defined(__clang__) || defined(__GNUC__) +#if defined(__Bitrig__) || defined(__OpenBSD__) // redefine swap16, swap32, swap64 as inline functions #undef swap16 #undef swap32 #undef swap64 -inline u16 swap16(u16 _data) { - return __swap16(_data); -} -inline u32 swap32(u32 _data) { - return __swap32(_data); -} -inline u64 swap64(u64 _data) { - return __swap64(_data); -} -#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) -inline u16 swap16(u16 _data) { - return bswap16(_data); +#endif +[[nodiscard]] inline u16 swap16(u16 data) noexcept { + return __builtin_bswap16(data); } -inline u32 swap32(u32 _data) { - return bswap32(_data); +[[nodiscard]] inline u32 swap32(u32 data) noexcept { + return __builtin_bswap32(data); } -inline u64 swap64(u64 _data) { - return bswap64(_data); +[[nodiscard]] inline u64 swap64(u64 data) noexcept { + return __builtin_bswap64(data); } #else -// Slow generic implementation. -inline u16 swap16(u16 data) { +// Generic implementation. +[[nodiscard]] inline u16 swap16(u16 data) noexcept { return (data >> 8) | (data << 8); } -inline u32 swap32(u32 data) { - return (swap16(data) << 16) | swap16(data >> 16); +[[nodiscard]] inline u32 swap32(u32 data) noexcept { + return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) | + ((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24); } -inline u64 swap64(u64 data) { - return ((u64)swap32(data) << 32) | swap32(data >> 32); +[[nodiscard]] inline u64 swap64(u64 data) noexcept { + return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) | + ((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) | + ((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) | + ((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56); } #endif -inline float swapf(float f) { +[[nodiscard]] inline float swapf(float f) noexcept { static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t."); u32 value; @@ -153,7 +111,7 @@ inline float swapf(float f) { return f; } -inline double swapd(double f) { +[[nodiscard]] inline double swapd(double f) noexcept { static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t."); u64 value; diff --git a/src/common/zstd_compression.h b/src/common/zstd_compression.h index e0a64b035..e9de941c8 100644 --- a/src/common/zstd_compression.h +++ b/src/common/zstd_compression.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <vector> #include "common/common_types.h" diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 49145911b..dc96e35d5 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -14,6 +14,7 @@ #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/svc.h" #include "core/hle/kernel/vm_manager.h" @@ -99,7 +100,7 @@ public: } void CallSVC(u32 swi) override { - Kernel::CallSVC(swi); + Kernel::CallSVC(parent.system, swi); } void AddTicks(u64 ticks) override { @@ -112,14 +113,14 @@ public: // Always execute at least one tick. amortized_ticks = std::max<u64>(amortized_ticks, 1); - parent.core_timing.AddTicks(amortized_ticks); + parent.system.CoreTiming().AddTicks(amortized_ticks); num_interpreted_instructions = 0; } u64 GetTicksRemaining() override { - return std::max(parent.core_timing.GetDowncount(), 0); + return std::max(parent.system.CoreTiming().GetDowncount(), 0); } u64 GetCNTPCT() override { - return Timing::CpuCyclesToClockCycles(parent.core_timing.GetTicks()); + return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); } ARM_Dynarmic& parent; @@ -129,7 +130,7 @@ public: }; std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { - auto* current_process = Core::CurrentProcess(); + auto* current_process = system.Kernel().CurrentProcess(); auto** const page_table = current_process->VMManager().page_table.pointers.data(); Dynarmic::A64::UserConfig config; @@ -171,10 +172,10 @@ void ARM_Dynarmic::Step() { cb->InterpreterFallback(jit->GetPC(), 1); } -ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, +ARM_Dynarmic::ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index) - : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing}, - core_index{core_index}, core_timing{core_timing}, + : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{system}, + core_index{core_index}, system{system}, exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} { ThreadContext ctx{}; inner_unicorn.SaveContext(ctx); diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index d867c2a50..c1db254e8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -12,19 +12,15 @@ #include "core/arm/exclusive_monitor.h" #include "core/arm/unicorn/arm_unicorn.h" -namespace Core::Timing { -class CoreTiming; -} - namespace Core { class ARM_Dynarmic_Callbacks; class DynarmicExclusiveMonitor; +class System; class ARM_Dynarmic final : public ARM_Interface { public: - ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, - std::size_t core_index); + ARM_Dynarmic(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); ~ARM_Dynarmic() override; void MapBackingMemory(VAddr address, std::size_t size, u8* memory, @@ -63,7 +59,7 @@ private: ARM_Unicorn inner_unicorn; std::size_t core_index; - Timing::CoreTiming& core_timing; + System& system; DynarmicExclusiveMonitor& exclusive_monitor; }; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 27309280c..4e07fe8b5 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -10,7 +10,6 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/svc.h" -#include "core/memory.h" namespace Core { @@ -49,20 +48,6 @@ static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_ } } -static void InterruptHook(uc_engine* uc, u32 intNo, void* user_data) { - u32 esr{}; - CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); - - auto ec = esr >> 26; - auto iss = esr & 0xFFFFFF; - - switch (ec) { - case 0x15: // SVC - Kernel::CallSVC(iss); - break; - } -} - static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value, void* user_data) { ARM_Interface::ThreadContext ctx{}; @@ -72,7 +57,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si return {}; } -ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} { +ARM_Unicorn::ARM_Unicorn(System& system) : system{system} { CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc)); auto fpv = 3 << 20; @@ -177,7 +162,7 @@ void ARM_Unicorn::Run() { if (GDBStub::IsServerEnabled()) { ExecuteInstructions(std::max(4000000, 0)); } else { - ExecuteInstructions(std::max(core_timing.GetDowncount(), 0)); + ExecuteInstructions(std::max(system.CoreTiming().GetDowncount(), 0)); } } @@ -190,7 +175,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64)); void ARM_Unicorn::ExecuteInstructions(int num_instructions) { MICROPROFILE_SCOPE(ARM_Jit_Unicorn); CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); - core_timing.AddTicks(num_instructions); + system.CoreTiming().AddTicks(num_instructions); if (GDBStub::IsServerEnabled()) { if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); @@ -273,4 +258,20 @@ void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) { last_bkpt_hit = true; } +void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) { + u32 esr{}; + CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr)); + + const auto ec = esr >> 26; + const auto iss = esr & 0xFFFFFF; + + auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data); + + switch (ec) { + case 0x15: // SVC + Kernel::CallSVC(arm_instance->system, iss); + break; + } +} + } // namespace Core diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index 1e44f0736..209fc16ad 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -9,15 +9,13 @@ #include "core/arm/arm_interface.h" #include "core/gdbstub/gdbstub.h" -namespace Core::Timing { -class CoreTiming; -} - namespace Core { +class System; + class ARM_Unicorn final : public ARM_Interface { public: - explicit ARM_Unicorn(Timing::CoreTiming& core_timing); + explicit ARM_Unicorn(System& system); ~ARM_Unicorn() override; void MapBackingMemory(VAddr address, std::size_t size, u8* memory, @@ -47,8 +45,10 @@ public: void RecordBreak(GDBStub::BreakpointAddress bkpt); private: + static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data); + uc_engine* uc{}; - Timing::CoreTiming& core_timing; + System& system; GDBStub::BreakpointAddress last_bkpt{}; bool last_bkpt_hit = false; }; diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index e75741db0..ba63c3e61 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -55,13 +55,13 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { if (Settings::values.use_cpu_jit) { #ifdef ARCHITECTURE_x86_64 - arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index); + arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); #else - arm_interface = std::make_unique<ARM_Unicorn>(); + arm_interface = std::make_unique<ARM_Unicorn>(system); LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); #endif } else { - arm_interface = std::make_unique<ARM_Unicorn>(core_timing); + arm_interface = std::make_unique<ARM_Unicorn>(system); } scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface); 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 @@ namespace Core::Frontend { /** + * Represents a graphics context that can be used for background computation or drawing. If the + * graphics backend doesn't require the context, then the implementation of these methods can be + * stubs + */ +class GraphicsContext { +public: + /// Makes the graphics context current for the caller thread + virtual void MakeCurrent() = 0; + + /// Releases (dunno if this is the "right" word) the context from the caller thread + virtual void DoneCurrent() = 0; + + /// Swap buffers to display the next frame + virtual void SwapBuffers() = 0; +}; + +/** * Abstraction class used to provide an interface between emulation code and the frontend * (e.g. SDL, QGLWidget, GLFW, etc...). * @@ -30,7 +47,7 @@ namespace Core::Frontend { * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please * re-read the upper points again and think about it if you don't see this. */ -class EmuWindow { +class EmuWindow : public GraphicsContext { public: /// Data structure to store emuwindow configuration struct WindowConfig { @@ -40,17 +57,21 @@ public: std::pair<unsigned, unsigned> min_client_area_size; }; - /// Swap buffers to display the next frame - virtual void SwapBuffers() = 0; - /// Polls window events virtual void PollEvents() = 0; - /// Makes the graphics context current for the caller thread - virtual void MakeCurrent() = 0; - - /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread - virtual void DoneCurrent() = 0; + /** + * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This + * context can be used from other threads for background graphics computation. If the frontend + * is using a graphics backend that doesn't need anything specific to run on a different thread, + * then it can use a stubbed implemenation for GraphicsContext. + * + * If the return value is null, then the core should assume that the frontend cannot provide a + * Shared Context + */ + virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const { + return nullptr; + } /** * Signal that a touch pressed event has occurred (e.g. mouse click pressed) diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 6cd607206..4921ad4f0 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -25,7 +25,7 @@ public: return name; } - static const HandleType HANDLE_TYPE = HandleType::ClientPort; + static constexpr HandleType HANDLE_TYPE = HandleType::ClientPort; HandleType GetHandleType() const override { return HANDLE_TYPE; } diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index b1f39aad7..09cdff588 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -29,7 +29,7 @@ public: return name; } - static const HandleType HANDLE_TYPE = HandleType::ClientSession; + static constexpr HandleType HANDLE_TYPE = HandleType::ClientSession; HandleType GetHandleType() const override { return HANDLE_TYPE; } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index f060f2a3b..dda52f4c0 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -85,7 +85,7 @@ public: return name; } - static const HandleType HANDLE_TYPE = HandleType::Process; + static constexpr HandleType HANDLE_TYPE = HandleType::Process; HandleType GetHandleType() const override { return HANDLE_TYPE; } diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 2eb9dcbb7..84215f572 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h @@ -31,7 +31,7 @@ public: return reset_type; } - static const HandleType HANDLE_TYPE = HandleType::ReadableEvent; + static constexpr HandleType HANDLE_TYPE = HandleType::ReadableEvent; HandleType GetHandleType() const override { return HANDLE_TYPE; } diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 70e09858a..2613a6bb5 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -41,7 +41,7 @@ public: return GetTypeName(); } - static const HandleType HANDLE_TYPE = HandleType::ResourceLimit; + static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit; HandleType GetHandleType() const override { return HANDLE_TYPE; } diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index fef573b71..dc88a1ebd 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -43,7 +43,7 @@ public: return name; } - static const HandleType HANDLE_TYPE = HandleType::ServerPort; + static constexpr HandleType HANDLE_TYPE = HandleType::ServerPort; HandleType GetHandleType() const override { return HANDLE_TYPE; } diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 09b835ff8..738df30f8 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -46,7 +46,7 @@ public: return name; } - static const HandleType HANDLE_TYPE = HandleType::ServerSession; + static constexpr HandleType HANDLE_TYPE = HandleType::ServerSession; HandleType GetHandleType() const override { return HANDLE_TYPE; } diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 37e18c443..c2b6155e1 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -76,7 +76,7 @@ public: return name; } - static const HandleType HANDLE_TYPE = HandleType::SharedMemory; + static constexpr HandleType HANDLE_TYPE = HandleType::SharedMemory; HandleType GetHandleType() const override { return HANDLE_TYPE; } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2fd07ab34..d48a2203a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -131,16 +131,15 @@ enum class ResourceLimitValueType { LimitValue, }; -ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type, - ResourceLimitValueType value_type) { +ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, + u32 resource_type, ResourceLimitValueType value_type) { const auto type = static_cast<ResourceType>(resource_type); if (!IsValidResourceType(type)) { LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); return ERR_INVALID_ENUM_VALUE; } - const auto& kernel = Core::System::GetInstance().Kernel(); - const auto* const current_process = kernel.CurrentProcess(); + const auto* const current_process = system.Kernel().CurrentProcess(); ASSERT(current_process != nullptr); const auto resource_limit_object = @@ -160,7 +159,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_ty } // Anonymous namespace /// Set the process heap to a given Size. It can both extend and shrink the heap. -static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { +static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) { LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB. @@ -175,7 +174,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { return ERR_INVALID_SIZE; } - auto& vm_manager = Core::System::GetInstance().Kernel().CurrentProcess()->VMManager(); + auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); const auto alloc_result = vm_manager.SetHeapSize(heap_size); if (alloc_result.Failed()) { return alloc_result.Code(); @@ -185,7 +184,7 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { return RESULT_SUCCESS; } -static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { +static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) { LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); if (!Common::Is4KBAligned(addr)) { @@ -217,7 +216,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { return ERR_INVALID_MEMORY_PERMISSIONS; } - auto* const current_process = Core::CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); auto& vm_manager = current_process->VMManager(); if (!vm_manager.IsWithinAddressSpace(addr, size)) { @@ -242,7 +241,8 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { return vm_manager.ReprotectRange(addr, size, converted_permissions); } -static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) { +static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, + u32 attribute) { LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, size, mask, attribute); @@ -280,7 +280,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr return ERR_INVALID_COMBINATION; } - auto& vm_manager = Core::CurrentProcess()->VMManager(); + auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); if (!vm_manager.IsWithinAddressSpace(address, size)) { LOG_ERROR(Kernel_SVC, "Given address (0x{:016X}) is outside the bounds of the address space.", address); @@ -291,11 +291,11 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr } /// Maps a memory range into a different range. -static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { +static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); - auto& vm_manager = Core::CurrentProcess()->VMManager(); + auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); if (result.IsError()) { @@ -306,11 +306,11 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { } /// Unmaps a region that was previously mapped with svcMapMemory -static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { +static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); - auto& vm_manager = Core::CurrentProcess()->VMManager(); + auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); if (result.IsError()) { @@ -321,7 +321,8 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { } /// Connect to an OS service given the port name, returns the handle to the port to out -static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) { +static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, + VAddr port_name_address) { if (!Memory::IsValidVirtualAddress(port_name_address)) { LOG_ERROR(Kernel_SVC, "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", @@ -340,8 +341,8 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); - auto& kernel = Core::System::GetInstance().Kernel(); - auto it = kernel.FindNamedPort(port_name); + auto& kernel = system.Kernel(); + const auto it = kernel.FindNamedPort(port_name); if (!kernel.IsValidNamedPort(it)) { LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); return ERR_NOT_FOUND; @@ -353,14 +354,14 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address CASCADE_RESULT(client_session, client_port->Connect()); // Return the client session - auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); CASCADE_RESULT(*out_handle, handle_table.Create(client_session)); return RESULT_SUCCESS; } /// Makes a blocking IPC call to an OS service. -static ResultCode SendSyncRequest(Handle handle) { - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); +static ResultCode SendSyncRequest(Core::System& system, Handle handle) { + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle); if (!session) { LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle); @@ -369,18 +370,18 @@ static ResultCode SendSyncRequest(Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); - Core::System::GetInstance().PrepareReschedule(); + system.PrepareReschedule(); // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server // responds and cause a reschedule. - return session->SendSyncRequest(GetCurrentThread()); + return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread()); } /// Get the ID for the specified thread. -static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { +static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle); @@ -392,10 +393,10 @@ static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) { } /// Gets the ID of the specified process or a specified thread's owning process. -static ResultCode GetProcessId(u64* process_id, Handle handle) { +static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) { LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const SharedPtr<Process> process = handle_table.Get<Process>(handle); if (process) { *process_id = process->GetProcessID(); @@ -437,8 +438,8 @@ static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thr }; /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 handle_count, - s64 nano_seconds) { +static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, + u64 handle_count, s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", handles_address, handle_count, nano_seconds); @@ -457,11 +458,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 return ERR_OUT_OF_RANGE; } - auto* const thread = GetCurrentThread(); + auto* const thread = system.CurrentScheduler().GetCurrentThread(); using ObjectPtr = Thread::ThreadWaitObjects::value_type; Thread::ThreadWaitObjects objects(handle_count); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); for (u64 i = 0; i < handle_count; ++i) { const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle)); @@ -507,16 +508,16 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 thread->WakeAfterDelay(nano_seconds); thread->SetWakeupCallback(DefaultThreadWakeupCallback); - Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); + system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); return RESULT_TIMEOUT; } /// Resumes a thread waiting on WaitSynchronization -static ResultCode CancelSynchronization(Handle thread_handle) { +static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", @@ -531,8 +532,8 @@ static ResultCode CancelSynchronization(Handle thread_handle) { } /// Attempts to locks a mutex, creating it if it does not already exist -static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, - Handle requesting_thread_handle) { +static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, + VAddr mutex_addr, Handle requesting_thread_handle) { LOG_TRACE(Kernel_SVC, "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " "requesting_current_thread_handle=0x{:08X}", @@ -549,13 +550,13 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, return ERR_INVALID_ADDRESS; } - auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle); } /// Unlock a mutex -static ResultCode ArbitrateUnlock(VAddr mutex_addr) { +static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); if (Memory::IsKernelVirtualAddress(mutex_addr)) { @@ -569,7 +570,7 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { return ERR_INVALID_ADDRESS; } - auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); return current_process->GetMutex().Release(mutex_addr); } @@ -592,7 +593,7 @@ struct BreakReason { }; /// Break program execution -static void Break(u32 reason, u64 info1, u64 info2) { +static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { BreakReason break_reason{reason}; bool has_dumped_buffer{}; @@ -670,22 +671,24 @@ static void Break(u32 reason, u64 info1, u64 info2) { Debug_Emulated, "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", reason, info1, info2); + handle_debug_buffer(info1, info2); - Core::System::GetInstance() - .ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID())) - .LogBacktrace(); + + auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); + const auto thread_processor_id = current_thread->GetProcessorID(); + system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); ASSERT(false); - Core::CurrentProcess()->PrepareForTermination(); + system.Kernel().CurrentProcess()->PrepareForTermination(); // Kill the current thread - GetCurrentThread()->Stop(); - Core::System::GetInstance().PrepareReschedule(); + current_thread->Stop(); + system.PrepareReschedule(); } } /// Used to output a message on a debug hardware unit - does nothing on a retail unit -static void OutputDebugString(VAddr address, u64 len) { +static void OutputDebugString([[maybe_unused]] Core::System& system, VAddr address, u64 len) { if (len == 0) { return; } @@ -696,7 +699,8 @@ static void OutputDebugString(VAddr address, u64 len) { } /// Gets system/memory information for the current process -static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) { +static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle, + u64 info_sub_id) { LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); @@ -754,7 +758,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) return ERR_INVALID_ENUM_VALUE; } - const auto& current_process_handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& current_process_handle_table = + system.Kernel().CurrentProcess()->GetHandleTable(); const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle)); if (!process) { return ERR_INVALID_HANDLE; @@ -844,7 +849,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) return ERR_INVALID_COMBINATION; } - Process* const current_process = Core::CurrentProcess(); + Process* const current_process = system.Kernel().CurrentProcess(); HandleTable& handle_table = current_process->GetHandleTable(); const auto resource_limit = current_process->GetResourceLimit(); if (!resource_limit) { @@ -875,7 +880,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) return ERR_INVALID_COMBINATION; } - *result = Core::CurrentProcess()->GetRandomEntropy(info_sub_id); + *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); return RESULT_SUCCESS; case GetInfoType::PrivilegedProcessId: @@ -892,15 +897,14 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) return ERR_INVALID_COMBINATION; } - const auto thread = - Core::CurrentProcess()->GetHandleTable().Get<Thread>(static_cast<Handle>(handle)); + const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>( + static_cast<Handle>(handle)); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", static_cast<Handle>(handle)); return ERR_INVALID_HANDLE; } - const auto& system = Core::System::GetInstance(); const auto& core_timing = system.CoreTiming(); const auto& scheduler = system.CurrentScheduler(); const auto* const current_thread = scheduler.GetCurrentThread(); @@ -927,13 +931,13 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) } /// Sets the thread activity -static ResultCode SetThreadActivity(Handle handle, u32 activity) { +static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); if (activity > static_cast<u32>(ThreadActivity::Paused)) { return ERR_INVALID_ENUM_VALUE; } - const auto* current_process = Core::CurrentProcess(); + const auto* current_process = system.Kernel().CurrentProcess(); const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); @@ -950,7 +954,7 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) { return ERR_INVALID_HANDLE; } - if (thread == GetCurrentThread()) { + if (thread == system.CurrentScheduler().GetCurrentThread()) { LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); return ERR_BUSY; } @@ -960,10 +964,10 @@ static ResultCode SetThreadActivity(Handle handle, u32 activity) { } /// Gets the thread context -static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { +static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) { LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); - const auto* current_process = Core::CurrentProcess(); + const auto* current_process = system.Kernel().CurrentProcess(); const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); @@ -980,7 +984,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { return ERR_INVALID_HANDLE; } - if (thread == GetCurrentThread()) { + if (thread == system.CurrentScheduler().GetCurrentThread()) { LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); return ERR_BUSY; } @@ -1001,10 +1005,10 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { } /// Gets the priority for the specified thread -static ResultCode GetThreadPriority(u32* priority, Handle handle) { +static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { LOG_TRACE(Kernel_SVC, "called"); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); @@ -1016,7 +1020,7 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) { } /// Sets the priority for the specified thread -static ResultCode SetThreadPriority(Handle handle, u32 priority) { +static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) { LOG_TRACE(Kernel_SVC, "called"); if (priority > THREADPRIO_LOWEST) { @@ -1027,7 +1031,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { return ERR_INVALID_THREAD_PRIORITY; } - const auto* const current_process = Core::CurrentProcess(); + const auto* const current_process = system.Kernel().CurrentProcess(); SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); if (!thread) { @@ -1037,18 +1041,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { thread->SetPriority(priority); - Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); + system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); return RESULT_SUCCESS; } /// Get which CPU core is executing the current thread -static u32 GetCurrentProcessorNumber() { +static u32 GetCurrentProcessorNumber(Core::System& system) { LOG_TRACE(Kernel_SVC, "called"); - return GetCurrentThread()->GetProcessorID(); + return system.CurrentScheduler().GetCurrentThread()->GetProcessorID(); } -static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, - u32 permissions) { +static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, + u64 size, u32 permissions) { LOG_TRACE(Kernel_SVC, "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", shared_memory_handle, addr, size, permissions); @@ -1082,7 +1086,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s return ERR_INVALID_MEMORY_PERMISSIONS; } - auto* const current_process = Core::CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); if (!shared_memory) { LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", @@ -1100,7 +1104,8 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare); } -static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { +static ResultCode UnmapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, + u64 size) { LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", shared_memory_handle, addr, size); @@ -1125,7 +1130,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 return ERR_INVALID_ADDRESS_STATE; } - auto* const current_process = Core::CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); if (!shared_memory) { LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", @@ -1143,10 +1148,11 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 return shared_memory->Unmap(*current_process, addr, size); } -static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, - Handle process_handle, VAddr address) { +static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, + VAddr page_info_address, Handle process_handle, + VAddr address) { LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); SharedPtr<Process> process = handle_table.Get<Process>(process_handle); if (!process) { LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", @@ -1172,20 +1178,156 @@ static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_ return RESULT_SUCCESS; } -static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address, - VAddr query_address) { +static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address, + VAddr page_info_address, VAddr query_address) { LOG_TRACE(Kernel_SVC, "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " "query_address=0x{:016X}", memory_info_address, page_info_address, query_address); - return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess, + return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess, query_address); } +static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, + u64 src_address, u64 size) { + LOG_DEBUG(Kernel_SVC, + "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " + "src_address=0x{:016X}, size=0x{:016X}", + process_handle, dst_address, src_address, size); + + if (!Common::Is4KBAligned(src_address)) { + LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", + src_address); + return ERR_INVALID_ADDRESS; + } + + if (!Common::Is4KBAligned(dst_address)) { + LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", + dst_address); + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || !Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); + return ERR_INVALID_SIZE; + } + + if (!IsValidAddressRange(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range overflows the address space (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!IsValidAddressRange(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range overflows the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + auto process = handle_table.Get<Process>(process_handle); + if (!process) { + LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", + process_handle); + return ERR_INVALID_HANDLE; + } + + auto& vm_manager = process->VMManager(); + if (!vm_manager.IsWithinAddressSpace(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range is not within the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_MEMORY_RANGE; + } + + return vm_manager.MapCodeMemory(dst_address, src_address, size); +} + +ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, + u64 src_address, u64 size) { + LOG_DEBUG(Kernel_SVC, + "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " + "size=0x{:016X}", + process_handle, dst_address, src_address, size); + + if (!Common::Is4KBAligned(dst_address)) { + LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", + dst_address); + return ERR_INVALID_ADDRESS; + } + + if (!Common::Is4KBAligned(src_address)) { + LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", + src_address); + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); + return ERR_INVALID_SIZE; + } + + if (!IsValidAddressRange(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range overflows the address space (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!IsValidAddressRange(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range overflows the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + auto process = handle_table.Get<Process>(process_handle); + if (!process) { + LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", + process_handle); + return ERR_INVALID_HANDLE; + } + + auto& vm_manager = process->VMManager(); + if (!vm_manager.IsWithinAddressSpace(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range is not within the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_MEMORY_RANGE; + } + + return vm_manager.UnmapCodeMemory(dst_address, src_address, size); +} + /// Exits the current process -static void ExitProcess() { - auto* current_process = Core::CurrentProcess(); +static void ExitProcess(Core::System& system) { + auto* current_process = system.Kernel().CurrentProcess(); LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, @@ -1194,20 +1336,20 @@ static void ExitProcess() { current_process->PrepareForTermination(); // Kill the current thread - GetCurrentThread()->Stop(); + system.CurrentScheduler().GetCurrentThread()->Stop(); - Core::System::GetInstance().PrepareReschedule(); + system.PrepareReschedule(); } /// Creates a new thread -static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, - u32 priority, s32 processor_id) { +static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, + VAddr stack_top, u32 priority, s32 processor_id) { LOG_TRACE(Kernel_SVC, "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", entry_point, arg, stack_top, priority, processor_id, *out_handle); - auto* const current_process = Core::CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); if (processor_id == THREADPROCESSORID_IDEAL) { // Set the target CPU to the one specified by the process. @@ -1239,7 +1381,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V } const std::string name = fmt::format("thread-{:X}", entry_point); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); CASCADE_RESULT(SharedPtr<Thread> thread, Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, *current_process)); @@ -1253,16 +1395,16 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V thread->SetGuestHandle(*new_guest_handle); *out_handle = *new_guest_handle; - Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); + system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); return RESULT_SUCCESS; } /// Starts the thread for the provided handle -static ResultCode StartThread(Handle thread_handle) { +static ResultCode StartThread(Core::System& system, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", @@ -1275,16 +1417,14 @@ static ResultCode StartThread(Handle thread_handle) { thread->ResumeFromWait(); if (thread->GetStatus() == ThreadStatus::Ready) { - Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); + system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); } return RESULT_SUCCESS; } /// Called when a thread exits -static void ExitThread() { - auto& system = Core::System::GetInstance(); - +static void ExitThread(Core::System& system) { LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); @@ -1294,7 +1434,7 @@ static void ExitThread() { } /// Sleep the current thread -static void SleepThread(s64 nanoseconds) { +static void SleepThread(Core::System& system, s64 nanoseconds) { LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); enum class SleepType : s64 { @@ -1303,7 +1443,6 @@ static void SleepThread(s64 nanoseconds) { YieldAndWaitForLoadBalancing = -2, }; - auto& system = Core::System::GetInstance(); auto& scheduler = system.CurrentScheduler(); auto* const current_thread = scheduler.GetCurrentThread(); @@ -1332,8 +1471,9 @@ static void SleepThread(s64 nanoseconds) { } /// Wait process wide key atomic -static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr, - Handle thread_handle, s64 nano_seconds) { +static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, + VAddr condition_variable_addr, Handle thread_handle, + s64 nano_seconds) { LOG_TRACE( Kernel_SVC, "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", @@ -1353,7 +1493,7 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var return ERR_INVALID_ADDRESS; } - auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); const auto& handle_table = current_process->GetHandleTable(); SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); ASSERT(thread); @@ -1363,7 +1503,7 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var return release_result; } - SharedPtr<Thread> current_thread = GetCurrentThread(); + SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread(); current_thread->SetCondVarWaitAddress(condition_variable_addr); current_thread->SetMutexWaitAddress(mutex_addr); current_thread->SetWaitHandle(thread_handle); @@ -1374,19 +1514,20 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var // Note: Deliberately don't attempt to inherit the lock owner's priority. - Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); + system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); return RESULT_SUCCESS; } /// Signal process wide key -static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { +static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, + s32 target) { LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", condition_variable_addr, target); - const auto RetrieveWaitingThreads = [](std::size_t core_index, - std::vector<SharedPtr<Thread>>& waiting_threads, - VAddr condvar_addr) { - const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); + const auto RetrieveWaitingThreads = [&system](std::size_t core_index, + std::vector<SharedPtr<Thread>>& waiting_threads, + VAddr condvar_addr) { + const auto& scheduler = system.Scheduler(core_index); const auto& thread_list = scheduler.GetThreadList(); for (const auto& thread : thread_list) { @@ -1425,9 +1566,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target // liberate Cond Var Thread. thread->SetCondVarWaitAddress(0); - std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); - - auto& monitor = Core::System::GetInstance().Monitor(); + const std::size_t current_core = system.CurrentCoreIndex(); + auto& monitor = system.Monitor(); // Atomically read the value of the mutex. u32 mutex_val = 0; @@ -1456,7 +1596,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target thread->SetLockOwner(nullptr); thread->SetMutexWaitAddress(0); thread->SetWaitHandle(0); - Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); + system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); } else { // Atomically signal that the mutex now has a waiting thread. do { @@ -1472,7 +1612,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target // The mutex is already owned by some other thread, make this thread wait on it. const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); auto owner = handle_table.Get<Thread>(owner_handle); ASSERT(owner); ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); @@ -1487,14 +1627,17 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target } // Wait for an address (via Address Arbiter) -static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout) { +static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, + s64 timeout) { LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, type, value, timeout); + // If the passed address is a kernel virtual address, return invalid memory state. if (Memory::IsKernelVirtualAddress(address)) { LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); return ERR_INVALID_ADDRESS_STATE; } + // If the address is not properly aligned to 4 bytes, return invalid address. if (!Common::IsWordAligned(address)) { LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); @@ -1502,20 +1645,22 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout } const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); - auto& address_arbiter = - Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); + auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); } // Signals to an address (via Address Arbiter) -static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to_wake) { +static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, + s32 num_to_wake) { LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", address, type, value, num_to_wake); + // If the passed address is a kernel virtual address, return invalid memory state. if (Memory::IsKernelVirtualAddress(address)) { LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); return ERR_INVALID_ADDRESS_STATE; } + // If the address is not properly aligned to 4 bytes, return invalid address. if (!Common::IsWordAligned(address)) { LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); @@ -1523,16 +1668,15 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to } const auto signal_type = static_cast<AddressArbiter::SignalType>(type); - auto& address_arbiter = - Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); + auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); } /// This returns the total CPU ticks elapsed since the CPU was powered-on -static u64 GetSystemTick() { +static u64 GetSystemTick(Core::System& system) { LOG_TRACE(Kernel_SVC, "called"); - auto& core_timing = Core::System::GetInstance().CoreTiming(); + auto& core_timing = system.CoreTiming(); const u64 result{core_timing.GetTicks()}; // Advance time to defeat dumb games that busy-wait for the frame to end. @@ -1542,18 +1686,18 @@ static u64 GetSystemTick() { } /// Close a handle -static ResultCode CloseHandle(Handle handle) { +static ResultCode CloseHandle(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle); - auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); return handle_table.Close(handle); } /// Clears the signaled state of an event or process. -static ResultCode ResetSignal(Handle handle) { +static ResultCode ResetSignal(Core::System& system, Handle handle) { LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); auto event = handle_table.Get<ReadableEvent>(handle); if (event) { @@ -1570,7 +1714,8 @@ static ResultCode ResetSignal(Handle handle) { } /// Creates a TransferMemory object -static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) { +static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size, + u32 permissions) { LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size, permissions); @@ -1598,7 +1743,7 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 return ERR_INVALID_MEMORY_PERMISSIONS; } - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); @@ -1611,7 +1756,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 return RESULT_SUCCESS; } -static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 permission_raw) { +static ResultCode MapTransferMemory(Core::System& system, Handle handle, VAddr address, u64 size, + u32 permission_raw) { LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}", handle, address, size, permission_raw); @@ -1645,7 +1791,7 @@ static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 return ERR_INVALID_STATE; } - const auto& kernel = Core::System::GetInstance().Kernel(); + const auto& kernel = system.Kernel(); const auto* const current_process = kernel.CurrentProcess(); const auto& handle_table = current_process->GetHandleTable(); @@ -1667,7 +1813,8 @@ static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 return transfer_memory->MapMemory(address, size, permissions); } -static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { +static ResultCode UnmapTransferMemory(Core::System& system, Handle handle, VAddr address, + u64 size) { LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle, address, size); @@ -1692,7 +1839,7 @@ static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { return ERR_INVALID_ADDRESS_STATE; } - const auto& kernel = Core::System::GetInstance().Kernel(); + const auto& kernel = system.Kernel(); const auto* const current_process = kernel.CurrentProcess(); const auto& handle_table = current_process->GetHandleTable(); @@ -1714,10 +1861,11 @@ static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { return transfer_memory->UnmapMemory(address, size); } -static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { +static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, + u64* mask) { LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", @@ -1731,11 +1879,12 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) return RESULT_SUCCESS; } -static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { +static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, + u64 mask) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle, mask, core); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", @@ -1780,8 +1929,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { return RESULT_SUCCESS; } -static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permissions, - u32 remote_permissions) { +static ResultCode CreateSharedMemory(Core::System& system, Handle* handle, u64 size, + u32 local_permissions, u32 remote_permissions) { LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, local_permissions, remote_permissions); if (size == 0) { @@ -1817,7 +1966,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss return ERR_INVALID_MEMORY_PERMISSIONS; } - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); auto process = kernel.CurrentProcess(); auto& handle_table = process->GetHandleTable(); auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms); @@ -1826,10 +1975,10 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss return RESULT_SUCCESS; } -static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) { +static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { LOG_DEBUG(Kernel_SVC, "called"); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); const auto [readable_event, writable_event] = WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent"); @@ -1854,10 +2003,10 @@ static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) { return RESULT_SUCCESS; } -static ResultCode ClearEvent(Handle handle) { +static ResultCode ClearEvent(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); auto writable_event = handle_table.Get<WritableEvent>(handle); if (writable_event) { @@ -1875,10 +2024,10 @@ static ResultCode ClearEvent(Handle handle) { return ERR_INVALID_HANDLE; } -static ResultCode SignalEvent(Handle handle) { +static ResultCode SignalEvent(Core::System& system, Handle handle) { LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle); - HandleTable& handle_table = Core::CurrentProcess()->GetHandleTable(); + HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); auto writable_event = handle_table.Get<WritableEvent>(handle); if (!writable_event) { @@ -1890,7 +2039,7 @@ static ResultCode SignalEvent(Handle handle) { return RESULT_SUCCESS; } -static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { +static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); // This function currently only allows retrieving a process' status. @@ -1898,7 +2047,7 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { Status, }; - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const auto process = handle_table.Get<Process>(process_handle); if (!process) { LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}", @@ -1916,10 +2065,10 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { return RESULT_SUCCESS; } -static ResultCode CreateResourceLimit(Handle* out_handle) { +static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) { LOG_DEBUG(Kernel_SVC, "called"); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); auto resource_limit = ResourceLimit::Create(kernel); auto* const current_process = kernel.CurrentProcess(); @@ -1934,11 +2083,11 @@ static ResultCode CreateResourceLimit(Handle* out_handle) { return RESULT_SUCCESS; } -static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit, - u32 resource_type) { +static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_value, + Handle resource_limit, u32 resource_type) { LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); - const auto limit_value = RetrieveResourceLimitValue(resource_limit, resource_type, + const auto limit_value = RetrieveResourceLimitValue(system, resource_limit, resource_type, ResourceLimitValueType::LimitValue); if (limit_value.Failed()) { return limit_value.Code(); @@ -1948,11 +2097,11 @@ static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_lim return RESULT_SUCCESS; } -static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit, - u32 resource_type) { +static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_value, + Handle resource_limit, u32 resource_type) { LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type); - const auto current_value = RetrieveResourceLimitValue(resource_limit, resource_type, + const auto current_value = RetrieveResourceLimitValue(system, resource_limit, resource_type, ResourceLimitValueType::CurrentValue); if (current_value.Failed()) { return current_value.Code(); @@ -1962,7 +2111,8 @@ static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_l return RESULT_SUCCESS; } -static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) { +static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit, + u32 resource_type, u64 value) { LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit, resource_type, value); @@ -1972,8 +2122,7 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource return ERR_INVALID_ENUM_VALUE; } - auto& kernel = Core::System::GetInstance().Kernel(); - auto* const current_process = kernel.CurrentProcess(); + auto* const current_process = system.Kernel().CurrentProcess(); ASSERT(current_process != nullptr); auto resource_limit_object = @@ -1997,8 +2146,8 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource return RESULT_SUCCESS; } -static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, - u32 out_process_ids_size) { +static ResultCode GetProcessList(Core::System& system, u32* out_num_processes, + VAddr out_process_ids, u32 out_process_ids_size) { LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", out_process_ids, out_process_ids_size); @@ -2010,7 +2159,7 @@ static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, return ERR_OUT_OF_RANGE; } - const auto& kernel = Core::System::GetInstance().Kernel(); + const auto& kernel = system.Kernel(); const auto& vm_manager = kernel.CurrentProcess()->VMManager(); const auto total_copy_size = out_process_ids_size * sizeof(u64); @@ -2034,8 +2183,8 @@ static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, return RESULT_SUCCESS; } -ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size, - Handle debug_handle) { +ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, + u32 out_thread_ids_size, Handle debug_handle) { // TODO: Handle this case when debug events are supported. UNIMPLEMENTED_IF(debug_handle != InvalidHandle); @@ -2049,7 +2198,7 @@ ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thr return ERR_OUT_OF_RANGE; } - const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); + const auto* const current_process = system.Kernel().CurrentProcess(); const auto& vm_manager = current_process->VMManager(); const auto total_copy_size = out_thread_ids_size * sizeof(u64); @@ -2076,7 +2225,7 @@ ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thr namespace { struct FunctionDef { - using Func = void(); + using Func = void(Core::System&); u32 id; Func* func; @@ -2204,8 +2353,8 @@ static const FunctionDef SVC_Table[] = { {0x74, nullptr, "MapProcessMemory"}, {0x75, nullptr, "UnmapProcessMemory"}, {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, - {0x77, nullptr, "MapProcessCodeMemory"}, - {0x78, nullptr, "UnmapProcessCodeMemory"}, + {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"}, + {0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, {0x79, nullptr, "CreateProcess"}, {0x7A, nullptr, "StartProcess"}, {0x7B, nullptr, "TerminateProcess"}, @@ -2225,7 +2374,7 @@ static const FunctionDef* GetSVCInfo(u32 func_num) { MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); -void CallSVC(u32 immediate) { +void CallSVC(Core::System& system, u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); // Lock the global kernel mutex when we enter the kernel HLE. @@ -2234,7 +2383,7 @@ void CallSVC(u32 immediate) { const FunctionDef* info = GetSVCInfo(immediate); if (info) { if (info->func) { - info->func(); + info->func(system); } else { LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name); } diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index c37ae0f98..c5539ac1c 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -6,8 +6,12 @@ #include "common/common_types.h" +namespace Core { +class System; +} + namespace Kernel { -void CallSVC(u32 immediate); +void CallSVC(Core::System& system, u32 immediate); } // namespace Kernel diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index b3733680f..865473c6f 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -11,278 +11,319 @@ namespace Kernel { -static inline u64 Param(int n) { - return Core::CurrentArmInterface().GetReg(n); +static inline u64 Param(const Core::System& system, int n) { + return system.CurrentArmInterface().GetReg(n); } /** * HLE a function return from the current ARM userland process - * @param res Result to return + * @param system System context + * @param result Result to return */ -static inline void FuncReturn(u64 res) { - Core::CurrentArmInterface().SetReg(0, res); +static inline void FuncReturn(Core::System& system, u64 result) { + system.CurrentArmInterface().SetReg(0, result); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type ResultCode -template <ResultCode func(u64)> -void SvcWrap() { - FuncReturn(func(Param(0)).raw); +template <ResultCode func(Core::System&, u64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0)).raw); } -template <ResultCode func(u32)> -void SvcWrap() { - FuncReturn(func(static_cast<u32>(Param(0))).raw); +template <ResultCode func(Core::System&, u32)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw); } -template <ResultCode func(u32, u32)> -void SvcWrap() { - FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw); +template <ResultCode func(Core::System&, u32, u32)> +void SvcWrap(Core::System& system) { + FuncReturn( + system, + func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); +} + +template <ResultCode func(Core::System&, u32, u64, u64, u64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), + Param(system, 2), Param(system, 3)) + .raw); } -template <ResultCode func(u32*)> -void SvcWrap() { +template <ResultCode func(Core::System&, u32*)> +void SvcWrap(Core::System& system) { u32 param = 0; - const u32 retval = func(¶m).raw; - Core::CurrentArmInterface().SetReg(1, param); - FuncReturn(retval); + const u32 retval = func(system, ¶m).raw; + system.CurrentArmInterface().SetReg(1, param); + FuncReturn(system, retval); } -template <ResultCode func(u32*, u32)> -void SvcWrap() { +template <ResultCode func(Core::System&, u32*, u32)> +void SvcWrap(Core::System& system) { u32 param_1 = 0; - u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u32*, u32*)> -void SvcWrap() { +template <ResultCode func(Core::System&, u32*, u32*)> +void SvcWrap(Core::System& system) { u32 param_1 = 0; u32 param_2 = 0; - const u32 retval = func(¶m_1, ¶m_2).raw; + const u32 retval = func(system, ¶m_1, ¶m_2).raw; - auto& arm_interface = Core::CurrentArmInterface(); + auto& arm_interface = system.CurrentArmInterface(); arm_interface.SetReg(1, param_1); arm_interface.SetReg(2, param_2); - FuncReturn(retval); + FuncReturn(system, retval); } -template <ResultCode func(u32*, u64)> -void SvcWrap() { +template <ResultCode func(Core::System&, u32*, u64)> +void SvcWrap(Core::System& system) { u32 param_1 = 0; - const u32 retval = func(¶m_1, Param(1)).raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u32*, u64, u32)> -void SvcWrap() { +template <ResultCode func(Core::System&, u32*, u64, u32)> +void SvcWrap(Core::System& system) { u32 param_1 = 0; - const u32 retval = func(¶m_1, Param(1), static_cast<u32>(Param(2))).raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = + func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u64*, u32)> -void SvcWrap() { +template <ResultCode func(Core::System&, u64*, u32)> +void SvcWrap(Core::System& system) { u64 param_1 = 0; - const u32 retval = func(¶m_1, static_cast<u32>(Param(1))).raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1))).raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u64, s32)> -void SvcWrap() { - FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw); +template <ResultCode func(Core::System&, u64, s32)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), static_cast<s32>(Param(system, 1))).raw); } -template <ResultCode func(u64, u32)> -void SvcWrap() { - FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw); +template <ResultCode func(Core::System&, u64, u32)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw); } -template <ResultCode func(u64*, u64)> -void SvcWrap() { +template <ResultCode func(Core::System&, u64*, u64)> +void SvcWrap(Core::System& system) { u64 param_1 = 0; - u32 retval = func(¶m_1, Param(1)).raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = func(system, ¶m_1, Param(system, 1)).raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u64*, u32, u32)> -void SvcWrap() { +template <ResultCode func(Core::System&, u64*, u32, u32)> +void SvcWrap(Core::System& system) { u64 param_1 = 0; - u32 retval = func(¶m_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = func(system, ¶m_1, static_cast<u32>(Param(system, 1)), + static_cast<u32>(Param(system, 2))) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u32, u64)> -void SvcWrap() { - FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw); +template <ResultCode func(Core::System&, u32, u64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw); } -template <ResultCode func(u32, u32, u64)> -void SvcWrap() { - FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw); +template <ResultCode func(Core::System&, u32, u32, u64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), + static_cast<u32>(Param(system, 1)), Param(system, 2)) + .raw); } -template <ResultCode func(u32, u32*, u64*)> -void SvcWrap() { +template <ResultCode func(Core::System&, u32, u32*, u64*)> +void SvcWrap(Core::System& system) { u32 param_1 = 0; u64 param_2 = 0; - ResultCode retval = func(static_cast<u32>(Param(2)), ¶m_1, ¶m_2); - Core::CurrentArmInterface().SetReg(1, param_1); - Core::CurrentArmInterface().SetReg(2, param_2); - FuncReturn(retval.raw); -} + const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); -template <ResultCode func(u64, u64, u32, u32)> -void SvcWrap() { - FuncReturn( - func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw); + system.CurrentArmInterface().SetReg(1, param_1); + system.CurrentArmInterface().SetReg(2, param_2); + FuncReturn(system, retval.raw); } -template <ResultCode func(u64, u64, u32, u64)> -void SvcWrap() { - FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw); +template <ResultCode func(Core::System&, u64, u64, u32, u32)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), Param(system, 1), + static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) + .raw); } -template <ResultCode func(u32, u64, u32)> -void SvcWrap() { - FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw); +template <ResultCode func(Core::System&, u64, u64, u32, u64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), Param(system, 1), + static_cast<u32>(Param(system, 2)), Param(system, 3)) + .raw); } -template <ResultCode func(u64, u64, u64)> -void SvcWrap() { - FuncReturn(func(Param(0), Param(1), Param(2)).raw); +template <ResultCode func(Core::System&, u32, u64, u32)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), + static_cast<u32>(Param(system, 2))) + .raw); } -template <ResultCode func(u64, u64, u32)> -void SvcWrap() { - FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw); +template <ResultCode func(Core::System&, u64, u64, u64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw); } -template <ResultCode func(u32, u64, u64, u32)> -void SvcWrap() { +template <ResultCode func(Core::System&, u64, u64, u32)> +void SvcWrap(Core::System& system) { FuncReturn( - func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw); + system, + func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw); } -template <ResultCode func(u32, u64, u64)> -void SvcWrap() { - FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw); +template <ResultCode func(Core::System&, u32, u64, u64, u32)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), + Param(system, 2), static_cast<u32>(Param(system, 3))) + .raw); } -template <ResultCode func(u32*, u64, u64, s64)> -void SvcWrap() { +template <ResultCode func(Core::System&, u32, u64, u64)> +void SvcWrap(Core::System& system) { + FuncReturn( + system, + func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw); +} + +template <ResultCode func(Core::System&, u32*, u64, u64, s64)> +void SvcWrap(Core::System& system) { u32 param_1 = 0; - ResultCode retval = - func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))); - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval.raw); + const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), + static_cast<s64>(Param(system, 3))) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u64, u64, u32, s64)> -void SvcWrap() { - FuncReturn( - func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw); +template <ResultCode func(Core::System&, u64, u64, u32, s64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), Param(system, 1), + static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) + .raw); } -template <ResultCode func(u64*, u64, u64, u64)> -void SvcWrap() { +template <ResultCode func(Core::System&, u64*, u64, u64, u64)> +void SvcWrap(Core::System& system) { u64 param_1 = 0; - u32 retval = func(¶m_1, Param(1), Param(2), Param(3)).raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = + func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3)).raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u32*, u64, u64, u64, u32, s32)> -void SvcWrap() { +template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)> +void SvcWrap(Core::System& system) { u32 param_1 = 0; - u32 retval = func(¶m_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)), - static_cast<s32>(Param(5))) - .raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), Param(system, 3), + static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5))) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u32*, u64, u64, u32)> -void SvcWrap() { +template <ResultCode func(Core::System&, u32*, u64, u64, u32)> +void SvcWrap(Core::System& system) { u32 param_1 = 0; - u32 retval = func(¶m_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = func(system, ¶m_1, Param(system, 1), Param(system, 2), + static_cast<u32>(Param(system, 3))) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(Handle*, u64, u32, u32)> -void SvcWrap() { +template <ResultCode func(Core::System&, Handle*, u64, u32, u32)> +void SvcWrap(Core::System& system) { u32 param_1 = 0; - u32 retval = - func(¶m_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw; - Core::CurrentArmInterface().SetReg(1, param_1); - FuncReturn(retval); + const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast<u32>(Param(system, 2)), + static_cast<u32>(Param(system, 3))) + .raw; + + system.CurrentArmInterface().SetReg(1, param_1); + FuncReturn(system, retval); } -template <ResultCode func(u64, u32, s32, s64)> -void SvcWrap() { - FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), - static_cast<s64>(Param(3))) - .raw); +template <ResultCode func(Core::System&, u64, u32, s32, s64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), + static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3))) + .raw); } -template <ResultCode func(u64, u32, s32, s32)> -void SvcWrap() { - FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)), - static_cast<s32>(Param(3))) - .raw); +template <ResultCode func(Core::System&, u64, u32, s32, s32)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)), + static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3))) + .raw); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type u32 -template <u32 func()> -void SvcWrap() { - FuncReturn(func()); +template <u32 func(Core::System&)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system)); } //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type u64 -template <u64 func()> -void SvcWrap() { - FuncReturn(func()); +template <u64 func(Core::System&)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system)); } //////////////////////////////////////////////////////////////////////////////////////////////////// /// Function wrappers that return type void -template <void func()> -void SvcWrap() { - func(); +template <void func(Core::System&)> +void SvcWrap(Core::System& system) { + func(system); } -template <void func(s64)> -void SvcWrap() { - func(static_cast<s64>(Param(0))); +template <void func(Core::System&, s64)> +void SvcWrap(Core::System& system) { + func(system, static_cast<s64>(Param(system, 0))); } -template <void func(u64, u64 len)> -void SvcWrap() { - func(Param(0), Param(1)); +template <void func(Core::System&, u64, u64)> +void SvcWrap(Core::System& system) { + func(system, Param(system, 0), Param(system, 1)); } -template <void func(u64, u64, u64)> -void SvcWrap() { - func(Param(0), Param(1), Param(2)); +template <void func(Core::System&, u64, u64, u64)> +void SvcWrap(Core::System& system) { + func(system, Param(system, 0), Param(system, 1), Param(system, 2)); } -template <void func(u32, u64, u64)> -void SvcWrap() { - func(static_cast<u32>(Param(0)), Param(1), Param(2)); +template <void func(Core::System&, u32, u64, u64)> +void SvcWrap(Core::System& system) { + func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)); } } // namespace Kernel 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) { UpdatePriority(); } -void Thread::BoostPriority(u32 priority) { - scheduler->SetThreadPriority(this, priority); - current_priority = priority; -} - void Thread::SetWaitSynchronizationResult(ResultCode result) { context.cpu_registers[0] = result.raw; } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 73e5d1bb4..32026d7f0 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -106,7 +106,7 @@ public: return "Thread"; } - static const HandleType HANDLE_TYPE = HandleType::Thread; + static constexpr HandleType HANDLE_TYPE = HandleType::Thread; HandleType GetHandleType() const override { return HANDLE_TYPE; } @@ -136,12 +136,6 @@ public: */ void SetPriority(u32 priority); - /** - * Temporarily boosts the thread's priority until the next time it is scheduled - * @param priority The new priority - */ - void BoostPriority(u32 priority); - /// Adds a thread to the list of threads that are waiting for a lock held by this thread. void AddMutexWaiter(SharedPtr<Thread> thread); diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index ec0a480ce..f0c0c12fc 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -302,6 +302,86 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) { return MakeResult<VAddr>(heap_region_base); } +ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { + constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; + const auto src_check_result = CheckRangeState( + src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All, + VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); + + if (src_check_result.Failed()) { + return src_check_result.Code(); + } + + const auto mirror_result = + MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode); + if (mirror_result.IsError()) { + return mirror_result; + } + + // Ensure we lock the source memory region. + const auto src_vma_result = CarveVMARange(src_address, size); + if (src_vma_result.Failed()) { + return src_vma_result.Code(); + } + auto src_vma_iter = *src_vma_result; + src_vma_iter->second.attribute = MemoryAttribute::Locked; + Reprotect(src_vma_iter, VMAPermission::Read); + + // The destination memory region is fine as is, however we need to make it read-only. + return ReprotectRange(dst_address, size, VMAPermission::Read); +} + +ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { + constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; + const auto src_check_result = CheckRangeState( + src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None, + VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute); + + if (src_check_result.Failed()) { + return src_check_result.Code(); + } + + // Yes, the kernel only checks the first page of the region. + const auto dst_check_result = + CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule, + MemoryState::FlagModule, VMAPermission::None, VMAPermission::None, + MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); + + if (dst_check_result.Failed()) { + return dst_check_result.Code(); + } + + const auto dst_memory_state = std::get<MemoryState>(*dst_check_result); + const auto dst_contiguous_check_result = CheckRangeState( + dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None, + VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); + + if (dst_contiguous_check_result.Failed()) { + return dst_contiguous_check_result.Code(); + } + + const auto unmap_result = UnmapRange(dst_address, size); + if (unmap_result.IsError()) { + return unmap_result; + } + + // With the mirrored portion unmapped, restore the original region's traits. + const auto src_vma_result = CarveVMARange(src_address, size); + if (src_vma_result.Failed()) { + return src_vma_result.Code(); + } + auto src_vma_iter = *src_vma_result; + src_vma_iter->second.state = MemoryState::Heap; + src_vma_iter->second.attribute = MemoryAttribute::None; + Reprotect(src_vma_iter, VMAPermission::ReadWrite); + + if (dst_memory_state == MemoryState::ModuleCode) { + Core::System::GetInstance().InvalidateCpuInstructionCaches(); + } + + return unmap_result; +} + MemoryInfo VMManager::QueryMemory(VAddr address) const { const auto vma = FindVMA(address); MemoryInfo memory_info{}; diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 6f484b7bf..288eb9450 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -43,6 +43,9 @@ enum class VMAPermission : u8 { ReadExecute = Read | Execute, WriteExecute = Write | Execute, ReadWriteExecute = Read | Write | Execute, + + // Used as a wildcard when checking permissions across memory ranges + All = 0xFF, }; constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { @@ -152,6 +155,9 @@ enum class MemoryState : u32 { FlagUncached = 1U << 24, FlagCodeMemory = 1U << 25, + // Wildcard used in range checking to indicate all states. + All = 0xFFFFFFFF, + // Convenience flag sets to reduce repetition IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, @@ -415,6 +421,49 @@ public: /// ResultVal<VAddr> SetHeapSize(u64 size); + /// Maps a region of memory as code memory. + /// + /// @param dst_address The base address of the region to create the aliasing memory region. + /// @param src_address The base address of the region to be aliased. + /// @param size The total amount of memory to map in bytes. + /// + /// @pre Both memory regions lie within the actual addressable address space. + /// + /// @post After this function finishes execution, assuming success, then the address range + /// [dst_address, dst_address+size) will alias the memory region, + /// [src_address, src_address+size). + /// <p> + /// What this also entails is as follows: + /// 1. The aliased region gains the Locked memory attribute. + /// 2. The aliased region becomes read-only. + /// 3. The aliasing region becomes read-only. + /// 4. The aliasing region is created with a memory state of MemoryState::CodeModule. + /// + ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); + + /// Unmaps a region of memory designated as code module memory. + /// + /// @param dst_address The base address of the memory region aliasing the source memory region. + /// @param src_address The base address of the memory region being aliased. + /// @param size The size of the memory region to unmap in bytes. + /// + /// @pre Both memory ranges lie within the actual addressable address space. + /// + /// @pre The memory region being unmapped has been previously been mapped + /// by a call to MapCodeMemory. + /// + /// @post After execution of the function, if successful. the aliasing memory region + /// will be unmapped and the aliased region will have various traits about it + /// restored to what they were prior to the original mapping call preceding + /// this function call. + /// <p> + /// What this also entails is as follows: + /// 1. The state of the memory region will now indicate a general heap region. + /// 2. All memory attributes for the memory region are cleared. + /// 3. Memory permissions for the region are restored to user read/write. + /// + ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); + /// Queries the memory manager for information about the given address. /// /// @param address The address to query the memory manager about for information. diff --git a/src/core/hle/kernel/writable_event.h b/src/core/hle/kernel/writable_event.h index c9068dd3d..d00c92a6b 100644 --- a/src/core/hle/kernel/writable_event.h +++ b/src/core/hle/kernel/writable_event.h @@ -37,7 +37,7 @@ public: return name; } - static const HandleType HANDLE_TYPE = HandleType::WritableEvent; + static constexpr HandleType HANDLE_TYPE = HandleType::WritableEvent; HandleType GetHandleType() const override { return HANDLE_TYPE; } diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 1f8ed265e..ba7d7acbd 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -137,6 +137,7 @@ private: class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { public: IManagerForApplication() : ServiceFramework("IManagerForApplication") { + // clang-format off static const FunctionInfo functions[] = { {0, &IManagerForApplication::CheckAvailability, "CheckAvailability"}, {1, &IManagerForApplication::GetAccountId, "GetAccountId"}, @@ -145,7 +146,10 @@ public: {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"}, {150, nullptr, "CreateAuthorizationRequest"}, {160, nullptr, "StoreOpenContext"}, + {170, nullptr, "LoadNetworkServiceLicenseKindAsync"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index 5e2030355..d66233cad 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -8,6 +8,7 @@ namespace Service::Account { ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") { + // clang-format off static const FunctionInfo functions[] = { {0, &ACC_SU::GetUserCount, "GetUserCount"}, {1, &ACC_SU::GetUserExistence, "GetUserExistence"}, @@ -19,6 +20,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, + {99, nullptr, "DebugActivateOpenContextRetention"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, nullptr, "GetBaasAccountManagerForSystemService"}, @@ -29,6 +31,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, + {130, nullptr, "ActivateOpenContextRetention"}, + {140, nullptr, "ListQualifiedUsers"}, {190, nullptr, "GetUserLastOpenedApplication"}, {191, nullptr, "ActivateOpenContextHolder"}, {200, nullptr, "BeginUserRegistration"}, @@ -48,6 +52,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index a4d705b45..182f7c7e5 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -8,6 +8,7 @@ namespace Service::Account { ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") { + // clang-format off static const FunctionInfo functions[] = { {0, &ACC_U0::GetUserCount, "GetUserCount"}, {1, &ACC_U0::GetUserExistence, "GetUserExistence"}, @@ -19,6 +20,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, + {99, nullptr, "DebugActivateOpenContextRetention"}, {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"}, {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, {102, nullptr, "AuthenticateApplicationAsync"}, @@ -27,7 +29,13 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {111, nullptr, "ClearSaveDataThumbnail"}, {120, nullptr, "CreateGuestLoginRequest"}, {130, nullptr, "LoadOpenContext"}, + {131, nullptr, "ListOpenContextStoredUsers"}, + {140, nullptr, "InitializeApplicationInfo"}, + {141, nullptr, "ListQualifiedUsers"}, + {150, nullptr, "IsUserAccountSwitchLocked"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 8fffc93b5..2dd17d935 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -8,6 +8,7 @@ namespace Service::Account { ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") { + // clang-format off static const FunctionInfo functions[] = { {0, &ACC_U1::GetUserCount, "GetUserCount"}, {1, &ACC_U1::GetUserExistence, "GetUserExistence"}, @@ -19,6 +20,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"}, {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, {60, nullptr, "ListOpenContextStoredUsers"}, + {99, nullptr, "DebugActivateOpenContextRetention"}, {100, nullptr, "GetUserRegistrationNotifier"}, {101, nullptr, "GetUserStateChangeNotifier"}, {102, nullptr, "GetBaasAccountManagerForSystemService"}, @@ -29,12 +31,16 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {111, nullptr, "ClearSaveDataThumbnail"}, {112, nullptr, "LoadSaveDataThumbnail"}, {113, nullptr, "GetSaveDataThumbnailExistence"}, + {130, nullptr, "ActivateOpenContextRetention"}, + {140, nullptr, "ListQualifiedUsers"}, {190, nullptr, "GetUserLastOpenedApplication"}, {191, nullptr, "ActivateOpenContextHolder"}, {997, nullptr, "DebugInvalidateTokenCacheForUser"}, {998, nullptr, "DebugSetUserStateClose"}, {999, nullptr, "DebugSetUserStateOpen"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 85271d418..1aa4ce1ac 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -224,6 +224,7 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { {20, nullptr, "InvalidateTransitionLayer"}, {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, {40, nullptr, "GetAppletResourceUsageInfo"}, + {41, nullptr, "SetCpuBoostModeForApplet"}, }; // clang-format on @@ -256,6 +257,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, {41, nullptr, "IsSystemBufferSharingEnabled"}, {42, nullptr, "GetSystemSharedLayerHandle"}, + {43, nullptr, "GetSystemSharedBufferHandle"}, {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, {51, nullptr, "ApproveToDisplay"}, {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, @@ -269,9 +271,11 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {68, nullptr, "SetAutoSleepDisabled"}, {69, nullptr, "IsAutoSleepDisabled"}, {70, nullptr, "ReportMultimediaError"}, + {71, nullptr, "GetCurrentIlluminanceEx"}, {80, nullptr, "SetWirelessPriorityMode"}, {90, nullptr, "GetAccumulatedSuspendedTickValue"}, {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, + {100, nullptr, "SetAlbumImageTakenNotificationEnabled"}, {1000, nullptr, "GetDebugStorageChannel"}, }; // clang-format on @@ -516,11 +520,20 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q {50, nullptr, "IsVrModeEnabled"}, {51, nullptr, "SetVrModeEnabled"}, {52, nullptr, "SwitchLcdBacklight"}, + {53, nullptr, "BeginVrModeEx"}, + {54, nullptr, "EndVrModeEx"}, {55, nullptr, "IsInControllerFirmwareUpdateSection"}, {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, {62, nullptr, "GetHdcpAuthenticationState"}, {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, + {64, nullptr, "SetTvPowerStateMatchingMode"}, + {65, nullptr, "GetApplicationIdByContentActionName"}, + {66, nullptr, "SetCpuBoostMode"}, + {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, + {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, + {91, nullptr, "GetCurrentPerformanceConfiguration"}, + {200, nullptr, "GetOperationModeSystemInfo"}, }; // clang-format on @@ -960,6 +973,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, {12, nullptr, "CreateApplicationAndRequestToStart"}, {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, + {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"}, + {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"}, {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, @@ -1233,6 +1248,7 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat {2, nullptr, "StartSleepSequence"}, {3, nullptr, "StartShutdownSequence"}, {4, nullptr, "StartRebootSequence"}, + {9, nullptr, "IsAutoPowerDownRequested"}, {10, nullptr, "LoadAndApplyIdlePolicySettings"}, {11, nullptr, "NotifyCecSettingsChanged"}, {12, nullptr, "SetDefaultHomeButtonLongPressTime"}, diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index b888f861d..488add8e7 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -16,6 +16,7 @@ public: std::shared_ptr<AppletMessageQueue> msg_queue) : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), msg_queue(std::move(msg_queue)) { + // clang-format off static const FunctionInfo functions[] = { {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, @@ -25,8 +26,11 @@ public: {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"}, {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, {20, &ILibraryAppletProxy::GetApplicationFunctions, "GetApplicationFunctions"}, + {21, nullptr, "GetAppletCommonFunctions"}, {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, }; + // clang-format on + RegisterHandlers(functions); } @@ -113,6 +117,7 @@ public: std::shared_ptr<AppletMessageQueue> msg_queue) : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), msg_queue(std::move(msg_queue)) { + // clang-format off static const FunctionInfo functions[] = { {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, @@ -124,8 +129,11 @@ public: {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"}, + {23, nullptr, "GetAppletCommonFunctions"}, {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 2d768d9fc..51d8c26b4 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -50,6 +50,7 @@ static std::vector<u64> AccumulateAOCTitleIDs() { } AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CountAddOnContentByApplicationId"}, {1, nullptr, "ListAddOnContentByApplicationId"}, @@ -60,7 +61,10 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs {6, nullptr, "PrepareAddOnContentByApplicationId"}, {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"}, {8, &AOC_U::GetAddOnContentListChangedEvent, "GetAddOnContentListChangedEvent"}, + {100, nullptr, "CreateEcPurchasedEventManager"}, }; + // clang-format on + RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index fcacbab72..d058c0245 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp @@ -87,6 +87,8 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { {3, nullptr, "GetLastThrottlingState"}, {4, nullptr, "ClearLastThrottlingState"}, {5, nullptr, "LoadAndApplySettings"}, + {6, nullptr, "SetCpuBoostMode"}, + {7, nullptr, "GetCurrentPerformanceConfiguration"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index e5daefdde..d7f1d348d 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -25,6 +25,7 @@ public: {11, nullptr, "GetAudioInBufferCount"}, {12, nullptr, "SetAudioInDeviceGain"}, {13, nullptr, "GetAudioInDeviceGain"}, + {14, nullptr, "FlushAudioInBuffers"}, }; // clang-format on diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 39acb7b23..12875fb42 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -44,7 +44,7 @@ public: std::string&& unique_name) : ServiceFramework("IAudioOut"), audio_core(audio_core), device_name(std::move(device_name)), audio_params(audio_params) { - + // clang-format off static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, @@ -58,7 +58,10 @@ public: {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, {10, nullptr, "GetAudioOutPlayedSampleCount"}, {11, nullptr, "FlushAudioOutBuffers"}, + {12, nullptr, "SetAudioOutVolume"}, + {13, nullptr, "GetAudioOutVolume"}, }; + // clang-format on RegisterHandlers(functions); // This is the event handle used to check if the audio buffer was released diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 59ef603e1..974ff8e1a 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -154,7 +154,8 @@ public: {96, nullptr, "GetLeHidEventInfo"}, {97, nullptr, "RegisterBleHidEvent"}, {98, nullptr, "SetLeScanParameter"}, - {256, nullptr, "GetIsManufacturingMode"} + {256, nullptr, "GetIsManufacturingMode"}, + {257, nullptr, "EmulateBluetoothCrash"}, }; // clang-format on diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index ae7b0720b..907f464ab 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp @@ -15,32 +15,41 @@ public: explicit CAPS_A() : ServiceFramework{"caps:a"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "Unknown3"}, - {3, nullptr, "Unknown4"}, - {4, nullptr, "Unknown5"}, - {5, nullptr, "Unknown6"}, - {6, nullptr, "Unknown7"}, - {7, nullptr, "Unknown8"}, - {8, nullptr, "Unknown9"}, - {9, nullptr, "Unknown10"}, - {10, nullptr, "Unknown11"}, - {11, nullptr, "Unknown12"}, - {12, nullptr, "Unknown13"}, - {13, nullptr, "Unknown14"}, - {14, nullptr, "Unknown15"}, - {301, nullptr, "Unknown16"}, - {401, nullptr, "Unknown17"}, - {501, nullptr, "Unknown18"}, - {1001, nullptr, "Unknown19"}, - {1002, nullptr, "Unknown20"}, - {8001, nullptr, "Unknown21"}, - {8002, nullptr, "Unknown22"}, - {8011, nullptr, "Unknown23"}, - {8012, nullptr, "Unknown24"}, - {8021, nullptr, "Unknown25"}, - {10011, nullptr, "Unknown26"}, + {0, nullptr, "GetAlbumFileCount"}, + {1, nullptr, "GetAlbumFileList"}, + {2, nullptr, "LoadAlbumFile"}, + {3, nullptr, "DeleteAlbumFile"}, + {4, nullptr, "StorageCopyAlbumFile"}, + {5, nullptr, "IsAlbumMounted"}, + {6, nullptr, "GetAlbumUsage"}, + {7, nullptr, "GetAlbumFileSize"}, + {8, nullptr, "LoadAlbumFileThumbnail"}, + {9, nullptr, "LoadAlbumScreenShotImage"}, + {10, nullptr, "LoadAlbumScreenShotThumbnailImage"}, + {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"}, + {12, nullptr, "Unknown12"}, + {13, nullptr, "Unknown13"}, + {14, nullptr, "Unknown14"}, + {15, nullptr, "Unknown15"}, + {16, nullptr, "Unknown16"}, + {17, nullptr, "Unknown17"}, + {18, nullptr, "Unknown18"}, + {202, nullptr, "SaveEditedScreenShot"}, + {301, nullptr, "GetLastThumbnail"}, + {401, nullptr, "GetAutoSavingStorage"}, + {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, + {1001, nullptr, "Unknown1001"}, + {1002, nullptr, "Unknown1002"}, + {1003, nullptr, "Unknown1003"}, + {8001, nullptr, "ForceAlbumUnmounted"}, + {8002, nullptr, "ResetAlbumMountStatus"}, + {8011, nullptr, "RefreshAlbumCache"}, + {8012, nullptr, "GetAlbumCache"}, + {8013, nullptr, "Unknown8013"}, + {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"}, + {10011, nullptr, "SetInternalErrorConversionEnabled"}, + {50000, nullptr, "Unknown50000"}, + {60002, nullptr, "Unknown60002"}, }; // clang-format on @@ -53,16 +62,17 @@ public: explicit CAPS_C() : ServiceFramework{"caps:c"} { // clang-format off static const FunctionInfo functions[] = { - {2001, nullptr, "Unknown1"}, - {2002, nullptr, "Unknown2"}, - {2011, nullptr, "Unknown3"}, - {2012, nullptr, "Unknown4"}, - {2013, nullptr, "Unknown5"}, - {2014, nullptr, "Unknown6"}, - {2101, nullptr, "Unknown7"}, - {2102, nullptr, "Unknown8"}, - {2201, nullptr, "Unknown9"}, - {2301, nullptr, "Unknown10"}, + {33, nullptr, "Unknown33"}, + {2001, nullptr, "Unknown2001"}, + {2002, nullptr, "Unknown2002"}, + {2011, nullptr, "Unknown2011"}, + {2012, nullptr, "Unknown2012"}, + {2013, nullptr, "Unknown2013"}, + {2014, nullptr, "Unknown2014"}, + {2101, nullptr, "Unknown2101"}, + {2102, nullptr, "Unknown2102"}, + {2201, nullptr, "Unknown2201"}, + {2301, nullptr, "Unknown2301"}, }; // clang-format on @@ -127,11 +137,18 @@ public: explicit CAPS_U() : ServiceFramework{"caps:u"} { // clang-format off static const FunctionInfo functions[] = { + {32, nullptr, "SetShimLibraryVersion"}, {102, nullptr, "GetAlbumFileListByAruid"}, {103, nullptr, "DeleteAlbumFileByAruid"}, {104, nullptr, "GetAlbumFileSizeByAruid"}, + {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, + {130, nullptr, "PrecheckToCreateContentsByAruid"}, + {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, + {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, + {142, nullptr, "GetAlbumFileList3AaeAruid"}, + {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, {60002, nullptr, "OpenAccessorSessionForApplication"}, }; // clang-format on diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 657baddb8..e7df8fd98 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -115,11 +115,12 @@ private: void Read(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const u64 unk = rp.Pop<u64>(); + const u64 option = rp.Pop<u64>(); const s64 offset = rp.Pop<s64>(); const s64 length = rp.Pop<s64>(); - LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); + LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, + length); // Error checking if (length < 0) { @@ -148,11 +149,12 @@ private: void Write(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const u64 unk = rp.Pop<u64>(); + const u64 option = rp.Pop<u64>(); const s64 offset = rp.Pop<s64>(); const s64 length = rp.Pop<s64>(); - LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); + LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, + length); // Error checking if (length < 0) { @@ -250,10 +252,7 @@ private: u64 next_entry_index = 0; void Read(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 unk = rp.Pop<u64>(); - - LOG_DEBUG(Service_FS, "called, unk=0x{:X}", unk); + LOG_DEBUG(Service_FS, "called."); // Calculate how many entries we can fit in the output buffer const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); @@ -665,10 +664,13 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {100, nullptr, "OpenImageDirectoryFileSystem"}, {110, nullptr, "OpenContentStorageFileSystem"}, {120, nullptr, "OpenCloudBackupWorkStorageFileSystem"}, + {130, nullptr, "OpenCustomStorageFileSystem"}, {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, {201, nullptr, "OpenDataStorageByProgramId"}, {202, &FSP_SRV::OpenDataStorageByDataId, "OpenDataStorageByDataId"}, {203, &FSP_SRV::OpenPatchDataStorageByCurrentProcess, "OpenPatchDataStorageByCurrentProcess"}, + {204, nullptr, "OpenDataFileSystemByProgramIndex"}, + {205, nullptr, "OpenDataStorageByProgramIndex"}, {400, nullptr, "OpenDeviceOperator"}, {500, nullptr, "OpenSdCardDetectionEventNotifier"}, {501, nullptr, "OpenGameCardDetectionEventNotifier"}, @@ -692,6 +694,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, {616, nullptr, "GetSaveDataCommitId"}, + {617, nullptr, "UnregisterExternalKey"}, {620, nullptr, "SetSdCardEncryptionSeed"}, {630, nullptr, "SetSdCardAccessibility"}, {631, nullptr, "IsSdCardAccessible"}, @@ -702,6 +705,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {710, nullptr, "ResolveAccessFailure"}, {720, nullptr, "AbandonAccessFailure"}, {800, nullptr, "GetAndClearFileSystemProxyErrorInfo"}, + {810, nullptr, "RegisterProgramIndexMapInfo"}, {1000, nullptr, "SetBisRootForHost"}, {1001, nullptr, "SetSaveDataSize"}, {1002, nullptr, "SetSaveDataRootPath"}, @@ -712,6 +716,8 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {1007, nullptr, "RegisterUpdatePartition"}, {1008, nullptr, "OpenRegisteredUpdatePartition"}, {1009, nullptr, "GetAndClearMemoryReportInfo"}, + {1010, nullptr, "SetDataStorageRedirectTarget"}, + {1011, nullptr, "OutputAccessLogToSdCard2"}, {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, {1200, nullptr, "OpenMultiCommitManager"}, diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index d9225d624..5100e376c 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -12,6 +12,7 @@ namespace Service::Friend { class IFriendService final : public ServiceFramework<IFriendService> { public: IFriendService() : ServiceFramework("IFriendService") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetCompletionEvent"}, {1, nullptr, "Cancel"}, @@ -24,8 +25,7 @@ public: {10400, nullptr, "GetBlockedUserListIds"}, {10500, nullptr, "GetProfileList"}, {10600, nullptr, "DeclareOpenOnlinePlaySession"}, - {10601, &IFriendService::DeclareCloseOnlinePlaySession, - "DeclareCloseOnlinePlaySession"}, + {10601, &IFriendService::DeclareCloseOnlinePlaySession, "DeclareCloseOnlinePlaySession"}, {10610, &IFriendService::UpdateUserPresence, "UpdateUserPresence"}, {10700, nullptr, "GetPlayHistoryRegistrationKey"}, {10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"}, @@ -88,6 +88,7 @@ public: {30830, nullptr, "ClearPlayLog"}, {49900, nullptr, "DeleteNetworkServiceAccountCache"}, }; + // clang-format on RegisterHandlers(functions); } diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 63b55758b..a4ad95d96 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -210,6 +210,7 @@ Hid::Hid() : ServiceFramework("hid") { {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, + {134, nullptr, "SetNpadAnalogStickUseCenterClamp"}, {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, {201, &Hid::SendVibrationValue, "SendVibrationValue"}, {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, @@ -221,6 +222,7 @@ Hid::Hid() : ServiceFramework("hid") { {208, nullptr, "GetActualVibrationGcErmCommand"}, {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, + {211, nullptr, "IsVibrationDeviceMounted"}, {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, {302, nullptr, "StopConsoleSixAxisSensor"}, @@ -265,6 +267,7 @@ Hid::Hid() : ServiceFramework("hid") { {523, nullptr, "SetIsPalmaPairedConnectable"}, {524, nullptr, "PairPalma"}, {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, + {526, nullptr, "CancelWritePalmaWaveEntry"}, {1000, nullptr, "SetNpadCommunicationMode"}, {1001, nullptr, "GetNpadCommunicationMode"}, }; @@ -797,12 +800,22 @@ public: {232, nullptr, "EnableShipmentMode"}, {233, nullptr, "ClearPairingInfo"}, {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, + {235, nullptr, "EnableAnalogStickPower"}, {301, nullptr, "GetAbstractedPadHandles"}, {302, nullptr, "GetAbstractedPadState"}, {303, nullptr, "GetAbstractedPadsState"}, {321, nullptr, "SetAutoPilotVirtualPadState"}, {322, nullptr, "UnsetAutoPilotVirtualPadState"}, {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, + {324, nullptr, "AttachHdlsWorkBuffer"}, + {325, nullptr, "ReleaseHdlsWorkBuffer"}, + {326, nullptr, "DumpHdlsNpadAssignmentState"}, + {327, nullptr, "DumpHdlsStates"}, + {328, nullptr, "ApplyHdlsNpadAssignmentState"}, + {329, nullptr, "ApplyHdlsStateList"}, + {330, nullptr, "AttachHdlsVirtualDevice"}, + {331, nullptr, "DetachHdlsVirtualDevice"}, + {332, nullptr, "SetHdlsState"}, {350, nullptr, "AddRegisteredDevice"}, {400, nullptr, "DisableExternalMcuOnNxDevice"}, {401, nullptr, "DisableRailDeviceFiltering"}, @@ -825,6 +838,7 @@ public: {131, nullptr, "ActivateSleepButton"}, {141, nullptr, "AcquireCaptureButtonEventHandle"}, {151, nullptr, "ActivateCaptureButton"}, + {161, nullptr, "GetPlatformConfig"}, {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, {211, nullptr, "GetNpadsWithNfc"}, {212, nullptr, "AcquireNfcActivateEventHandle"}, @@ -894,6 +908,7 @@ public: {827, nullptr, "IsAnalogStickButtonPressed"}, {828, nullptr, "IsAnalogStickInReleasePosition"}, {829, nullptr, "IsAnalogStickInCircumference"}, + {830, nullptr, "SetNotificationLedPattern"}, {850, nullptr, "IsUsbFullKeyControllerEnabled"}, {851, nullptr, "EnableUsbFullKeyController"}, {852, nullptr, "IsUsbConnected"}, diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index e250595e3..ed5059047 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -52,9 +52,11 @@ public: } }; -class ILocalCommunicationService final : public ServiceFramework<ILocalCommunicationService> { +class ISystemLocalCommunicationService final + : public ServiceFramework<ISystemLocalCommunicationService> { public: - explicit ILocalCommunicationService(const char* name) : ServiceFramework{name} { + explicit ISystemLocalCommunicationService() + : ServiceFramework{"ISystemLocalCommunicationService"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetState"}, @@ -84,6 +86,50 @@ public: {304, nullptr, "Disconnect"}, {400, nullptr, "InitializeSystem"}, {401, nullptr, "FinalizeSystem"}, + {402, nullptr, "SetOperationMode"}, + {403, nullptr, "InitializeSystem2"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IUserLocalCommunicationService final + : public ServiceFramework<IUserLocalCommunicationService> { +public: + explicit IUserLocalCommunicationService() : ServiceFramework{"IUserLocalCommunicationService"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetState"}, + {1, nullptr, "GetNetworkInfo"}, + {2, nullptr, "GetIpv4Address"}, + {3, nullptr, "GetDisconnectReason"}, + {4, nullptr, "GetSecurityParameter"}, + {5, nullptr, "GetNetworkConfig"}, + {100, nullptr, "AttachStateChangeEvent"}, + {101, nullptr, "GetNetworkInfoLatestUpdate"}, + {102, nullptr, "Scan"}, + {103, nullptr, "ScanPrivate"}, + {104, nullptr, "SetWirelessControllerRestriction"}, + {200, nullptr, "OpenAccessPoint"}, + {201, nullptr, "CloseAccessPoint"}, + {202, nullptr, "CreateNetwork"}, + {203, nullptr, "CreateNetworkPrivate"}, + {204, nullptr, "DestroyNetwork"}, + {205, nullptr, "Reject"}, + {206, nullptr, "SetAdvertiseData"}, + {207, nullptr, "SetStationAcceptPolicy"}, + {208, nullptr, "AddAcceptFilterEntry"}, + {209, nullptr, "ClearAcceptFilter"}, + {300, nullptr, "OpenStation"}, + {301, nullptr, "CloseStation"}, + {302, nullptr, "Connect"}, + {303, nullptr, "ConnectPrivate"}, + {304, nullptr, "Disconnect"}, + {400, nullptr, "Initialize"}, + {401, nullptr, "Finalize"}, + {402, nullptr, "SetOperationMode"}, }; // clang-format on @@ -108,7 +154,7 @@ public: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILocalCommunicationService>("ISystemLocalCommunicationService"); + rb.PushIpcInterface<ISystemLocalCommunicationService>(); } }; @@ -129,7 +175,7 @@ public: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILocalCommunicationService>("IUserLocalCommunicationService"); + rb.PushIpcInterface<IUserLocalCommunicationService>(); } }; diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index d65693fc7..5af925515 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -86,6 +86,7 @@ public: {2, &RelocatableObject::LoadNrr, "LoadNrr"}, {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, {4, &RelocatableObject::Initialize, "Initialize"}, + {10, nullptr, "LoadNrrEx"}, }; // clang-format on @@ -93,12 +94,18 @@ public: } void LoadNrr(Kernel::HLERequestContext& ctx) { + struct Parameters { + u64_le process_id; + u64_le nrr_address; + u64_le nrr_size; + }; + IPC::RequestParser rp{ctx}; - rp.Skip(2, false); - const VAddr nrr_addr{rp.Pop<VAddr>()}; - const u64 nrr_size{rp.Pop<u64>()}; - LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr, - nrr_size); + const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>(); + + LOG_DEBUG(Service_LDR, + "called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}", + process_id, nrr_address, nrr_size); if (!initialized) { LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); @@ -116,24 +123,26 @@ public: } // NRR Address does not fall on 0x1000 byte boundary - if (!Common::Is4KBAligned(nrr_addr)) { - LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); + if (!Common::Is4KBAligned(nrr_address)) { + LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", + nrr_address); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_ALIGNMENT); return; } // NRR Size is zero or causes overflow - if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) { + if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 || + !Common::Is4KBAligned(nrr_size)) { LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", - nrr_addr, nrr_size); + nrr_address, nrr_size); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_SIZE); return; } // Read NRR data from memory std::vector<u8> nrr_data(nrr_size); - Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size); + Memory::ReadBlock(nrr_address, nrr_data.data(), nrr_size); NRRHeader header; std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); @@ -174,7 +183,7 @@ public: hashes.emplace_back(hash); } - nrr.insert_or_assign(nrr_addr, std::move(hashes)); + nrr.insert_or_assign(nrr_address, std::move(hashes)); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -188,23 +197,30 @@ public: return; } + struct Parameters { + u64_le process_id; + u64_le nrr_address; + }; + IPC::RequestParser rp{ctx}; - rp.Skip(2, false); - const auto nrr_addr{rp.Pop<VAddr>()}; - LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr); + const auto [process_id, nrr_address] = rp.PopRaw<Parameters>(); + + LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id, + nrr_address); - if (!Common::Is4KBAligned(nrr_addr)) { - LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); + if (!Common::Is4KBAligned(nrr_address)) { + LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", + nrr_address); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_ALIGNMENT); return; } - const auto iter = nrr.find(nrr_addr); + const auto iter = nrr.find(nrr_address); if (iter == nrr.end()) { LOG_ERROR(Service_LDR, "Attempting to unload NRR which has not been loaded! (addr={:016X})", - nrr_addr); + nrr_address); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_NRR_ADDRESS); return; @@ -216,16 +232,22 @@ public: } void LoadNro(Kernel::HLERequestContext& ctx) { + struct Parameters { + u64_le process_id; + u64_le image_address; + u64_le image_size; + u64_le bss_address; + u64_le bss_size; + }; + IPC::RequestParser rp{ctx}; - rp.Skip(2, false); - const VAddr nro_addr{rp.Pop<VAddr>()}; - const u64 nro_size{rp.Pop<u64>()}; - const VAddr bss_addr{rp.Pop<VAddr>()}; - const u64 bss_size{rp.Pop<u64>()}; - LOG_DEBUG( - Service_LDR, - "called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}", - nro_addr, nro_size, bss_addr, bss_size); + const auto [process_id, nro_address, nro_size, bss_address, bss_size] = + rp.PopRaw<Parameters>(); + + LOG_DEBUG(Service_LDR, + "called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, " + "bss_size={:016X}", + process_id, nro_address, nro_size, bss_address, bss_size); if (!initialized) { LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); @@ -243,8 +265,9 @@ public: } // NRO Address does not fall on 0x1000 byte boundary - if (!Common::Is4KBAligned(nro_addr)) { - LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr); + if (!Common::Is4KBAligned(nro_address)) { + LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", + nro_address); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_ALIGNMENT); return; @@ -252,15 +275,15 @@ public: // NRO Size or BSS Size is zero or causes overflow const auto nro_size_valid = - nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size); - const auto bss_size_valid = - nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr); + nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size); + const auto bss_size_valid = nro_size + bss_size >= nro_size && + (bss_size == 0 || bss_address + bss_size > bss_address); if (!nro_size_valid || !bss_size_valid) { LOG_ERROR(Service_LDR, "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, " "bss_address={:016X}, bss_size={:016X})", - nro_addr, nro_size, bss_addr, bss_size); + nro_address, nro_size, bss_address, bss_size); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_SIZE); return; @@ -268,7 +291,7 @@ public: // Read NRO data from memory std::vector<u8> nro_data(nro_size); - Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); + Memory::ReadBlock(nro_address, nro_data.data(), nro_size); SHA256Hash hash{}; mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0); @@ -318,17 +341,18 @@ public: return; } - ASSERT(vm_manager - .MirrorMemory(*map_address, nro_addr, nro_size, Kernel::MemoryState::ModuleCode) - .IsSuccess()); - ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess()); + ASSERT( + vm_manager + .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) + .IsSuccess()); + ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); if (bss_size > 0) { ASSERT(vm_manager - .MirrorMemory(*map_address + nro_size, bss_addr, bss_size, + .MirrorMemory(*map_address + nro_size, bss_address, bss_size, Kernel::MemoryState::ModuleCode) .IsSuccess()); - ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess()); + ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess()); } vm_manager.ReprotectRange(*map_address, header.text_size, @@ -348,13 +372,6 @@ public: } void UnloadNro(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - rp.Skip(2, false); - const VAddr mapped_addr{rp.PopRaw<VAddr>()}; - const VAddr heap_addr{rp.PopRaw<VAddr>()}; - LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr, - heap_addr); - if (!initialized) { LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); IPC::ResponseBuilder rb{ctx, 2}; @@ -362,22 +379,30 @@ public: return; } - if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) { - LOG_ERROR(Service_LDR, - "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, " - "bss_addr={:016X})!", - mapped_addr, heap_addr); + struct Parameters { + u64_le process_id; + u64_le nro_address; + }; + + IPC::RequestParser rp{ctx}; + const auto [process_id, nro_address] = rp.PopRaw<Parameters>(); + LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id, + nro_address); + + if (!Common::Is4KBAligned(nro_address)) { + LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})", + nro_address); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_ALIGNMENT); return; } - const auto iter = nro.find(mapped_addr); + const auto iter = nro.find(nro_address); if (iter == nro.end()) { LOG_ERROR(Service_LDR, - "The NRO attempting to unmap was not mapped or has an invalid address " - "(actual {:016X})!", - mapped_addr); + "The NRO attempting to be unmapped was not mapped or has an invalid address " + "(nro_address=0x{:016X})!", + nro_address); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_INVALID_NRO_ADDRESS); return; @@ -386,10 +411,7 @@ public: auto& vm_manager = Core::CurrentProcess()->VMManager(); const auto& nro_size = iter->second.size; - ASSERT(vm_manager - .MirrorMemory(heap_addr, mapped_addr, nro_size, Kernel::MemoryState::ModuleCode) - .IsSuccess()); - ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess()); + ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); Core::System::GetInstance().InvalidateCpuInstructionCaches(); @@ -459,11 +481,10 @@ private: std::map<VAddr, NROInfo> nro; std::map<VAddr, std::vector<SHA256Hash>> nrr; - bool IsValidNROHash(const SHA256Hash& hash) { - return std::any_of( - nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) { - return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); - }); + bool IsValidNROHash(const SHA256Hash& hash) const { + return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { + return std::find(p.second.begin(), p.second.end(), hash) != p.second.end(); + }); } static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 60479bb45..f92571008 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -15,12 +15,16 @@ namespace Service::NIFM { class IScanRequest final : public ServiceFramework<IScanRequest> { public: explicit IScanRequest() : ServiceFramework("IScanRequest") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Submit"}, {1, nullptr, "IsProcessing"}, {2, nullptr, "GetResult"}, {3, nullptr, "GetSystemEventReadableHandle"}, + {4, nullptr, "SetChannels"}, }; + // clang-format on + RegisterHandlers(functions); } }; diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp index ccb6f9da9..8751522ca 100644 --- a/src/core/hle/service/npns/npns.cpp +++ b/src/core/hle/service/npns/npns.cpp @@ -45,7 +45,7 @@ public: {114, nullptr, "AttachJid"}, {115, nullptr, "DetachJid"}, {201, nullptr, "RequestChangeStateForceTimed"}, - {102, nullptr, "RequestChangeStateForceAsync"}, + {202, nullptr, "RequestChangeStateForceAsync"}, }; // clang-format on @@ -73,6 +73,7 @@ public: {103, nullptr, "GetState"}, {104, nullptr, "GetStatistics"}, {111, nullptr, "GetJid"}, + {120, nullptr, "CreateNotificationReceiver"}, }; // clang-format on 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 @@ #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" #include "core/perf_stats.h" +#include "core/settings.h" #include "video_core/renderer_base.h" namespace Service::NVFlinger { -constexpr std::size_t SCREEN_REFRESH_RATE = 60; -constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); +constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); +constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30); NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { displays.emplace_back(0, "Default"); @@ -36,13 +37,15 @@ NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_t displays.emplace_back(4, "Null"); // Schedule the screen composition events - composition_event = - core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { + const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks; + + composition_event = core_timing.RegisterEvent( + "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) { Compose(); - this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event); + this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event); }); - core_timing.ScheduleEvent(frame_ticks, composition_event); + core_timing.ScheduleEvent(ticks, composition_event); } NVFlinger::~NVFlinger() { @@ -62,6 +65,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { const auto itr = std::find_if(displays.begin(), displays.end(), [&](const VI::Display& display) { return display.GetName() == name; }); + if (itr == displays.end()) { return {}; } diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp index 6081f41e1..c75b4ee34 100644 --- a/src/core/hle/service/pctl/module.cpp +++ b/src/core/hle/service/pctl/module.cpp @@ -12,10 +12,10 @@ namespace Service::PCTL { class IParentalControlService final : public ServiceFramework<IParentalControlService> { public: IParentalControlService() : ServiceFramework("IParentalControlService") { + // clang-format off static const FunctionInfo functions[] = { {1, &IParentalControlService::Initialize, "Initialize"}, - {1001, &IParentalControlService::CheckFreeCommunicationPermission, - "CheckFreeCommunicationPermission"}, + {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"}, {1002, nullptr, "ConfirmLaunchApplicationPermission"}, {1003, nullptr, "ConfirmResumeApplicationPermission"}, {1004, nullptr, "ConfirmSnsPostPermission"}, @@ -30,6 +30,7 @@ public: {1013, nullptr, "ConfirmStereoVisionPermission"}, {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, {1015, nullptr, "ConfirmPlayableApplicationVideo"}, + {1016, nullptr, "ConfirmShowNewsPermission"}, {1031, nullptr, "IsRestrictionEnabled"}, {1032, nullptr, "GetSafetyLevel"}, {1033, nullptr, "SetSafetyLevel"}, @@ -45,6 +46,7 @@ public: {1045, nullptr, "UpdateFreeCommunicationApplicationList"}, {1046, nullptr, "DisableFeaturesForReset"}, {1047, nullptr, "NotifyApplicationDownloadStarted"}, + {1048, nullptr, "NotifyNetworkProfileCreated"}, {1061, nullptr, "ConfirmStereoVisionRestrictionConfigurable"}, {1062, nullptr, "GetStereoVisionRestriction"}, {1063, nullptr, "SetStereoVisionRestriction"}, @@ -63,6 +65,7 @@ public: {1411, nullptr, "GetPairingAccountInfo"}, {1421, nullptr, "GetAccountNickname"}, {1424, nullptr, "GetAccountState"}, + {1425, nullptr, "RequestPostEvents"}, {1432, nullptr, "GetSynchronizationEvent"}, {1451, nullptr, "StartPlayTimer"}, {1452, nullptr, "StopPlayTimer"}, diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index 6b27dc4a3..ebcc41a43 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -42,15 +42,18 @@ private: class DebugMonitor final : public ServiceFramework<DebugMonitor> { public: explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} { + // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "IsDebugMode"}, - {1, nullptr, "GetDebugProcesses"}, - {2, nullptr, "StartDebugProcess"}, - {3, nullptr, "GetTitlePid"}, - {4, nullptr, "EnableDebugForTitleId"}, - {5, nullptr, "GetApplicationPid"}, - {6, nullptr, "EnableDebugForApplication"}, + {0, nullptr, "GetDebugProcesses"}, + {1, nullptr, "StartDebugProcess"}, + {2, nullptr, "GetTitlePid"}, + {3, nullptr, "EnableDebugForTitleId"}, + {4, nullptr, "GetApplicationPid"}, + {5, nullptr, "EnableDebugForApplication"}, + {6, nullptr, "DisableDebug"}, }; + // clang-format on + RegisterHandlers(functions); } }; @@ -68,6 +71,7 @@ public: class Shell final : public ServiceFramework<Shell> { public: explicit Shell() : ServiceFramework{"pm:shell"} { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "LaunchProcess"}, {1, nullptr, "TerminateProcessByPid"}, @@ -77,7 +81,10 @@ public: {5, nullptr, "NotifyBootFinished"}, {6, nullptr, "GetApplicationPid"}, {7, nullptr, "BoostSystemMemoryResourceLimit"}, + {8, nullptr, "EnableAdditionalSystemThreads"}, }; + // clang-format on + RegisterHandlers(functions); } }; diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 1afc43f75..4ecb6bcef 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -116,6 +116,7 @@ void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { } SET::SET() : ServiceFramework("set") { + // clang-format off static const FunctionInfo functions[] = { {0, &SET::GetLanguageCode, "GetLanguageCode"}, {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, @@ -126,7 +127,10 @@ SET::SET() : ServiceFramework("set") { {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, {7, nullptr, "GetKeyCodeMap"}, {8, nullptr, "GetQuestFlag"}, + {9, nullptr, "GetKeyCodeMap2"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp index 34654bb07..5981c575c 100644 --- a/src/core/hle/service/set/set_cal.cpp +++ b/src/core/hle/service/set/set_cal.cpp @@ -40,7 +40,7 @@ SET_CAL::SET_CAL() : ServiceFramework("set:cal") { {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, - {33, nullptr, "GetBatteryVersion"}, + {41, nullptr, "GetBatteryVersion"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index ecee554bf..98d0cfdfd 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -104,6 +104,7 @@ void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) { } SET_SYS::SET_SYS() : ServiceFramework("set:sys") { + // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "SetLanguageCode"}, {1, nullptr, "SetNetworkSettings"}, @@ -252,7 +253,33 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") { {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, {149, nullptr, "GetRebootlessSystemUpdateVersion"}, + {150, nullptr, "GetDeviceTimeZoneLocationUpdatedTime"}, + {151, nullptr, "SetDeviceTimeZoneLocationUpdatedTime"}, + {152, nullptr, "GetUserSystemClockAutomaticCorrectionUpdatedTime"}, + {153, nullptr, "SetUserSystemClockAutomaticCorrectionUpdatedTime"}, + {154, nullptr, "GetAccountOnlineStorageSettings"}, + {155, nullptr, "SetAccountOnlineStorageSettings"}, + {156, nullptr, "GetPctlReadyFlag"}, + {157, nullptr, "SetPctlReadyFlag"}, + {162, nullptr, "GetPtmBatteryVersion"}, + {163, nullptr, "SetPtmBatteryVersion"}, + {164, nullptr, "GetUsb30HostEnableFlag"}, + {165, nullptr, "SetUsb30HostEnableFlag"}, + {166, nullptr, "GetUsb30DeviceEnableFlag"}, + {167, nullptr, "SetUsb30DeviceEnableFlag"}, + {168, nullptr, "GetThemeId"}, + {169, nullptr, "SetThemeId"}, + {170, nullptr, "GetChineseTraditionalInputMethod"}, + {171, nullptr, "SetChineseTraditionalInputMethod"}, + {172, nullptr, "GetPtmCycleCountReliability"}, + {173, nullptr, "SetPtmCycleCountReliability"}, + {175, nullptr, "GetThemeSettings"}, + {176, nullptr, "SetThemeSettings"}, + {177, nullptr, "GetThemeKey"}, + {178, nullptr, "SetThemeKey"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 4342f3b2d..884ad173b 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -73,6 +73,7 @@ void BSD::Close(Kernel::HLERequestContext& ctx) { } BSD::BSD(const char* name) : ServiceFramework(name) { + // clang-format off static const FunctionInfo functions[] = { {0, &BSD::RegisterClient, "RegisterClient"}, {1, &BSD::StartMonitoring, "StartMonitoring"}, @@ -105,7 +106,11 @@ BSD::BSD(const char* name) : ServiceFramework(name) { {28, nullptr, "GetResourceStatistics"}, {29, nullptr, "RecvMMsg"}, {30, nullptr, "SendMMsg"}, + {31, nullptr, "EventFd"}, + {32, nullptr, "RegisterResourceStatisticsName"}, }; + // clang-format on + RegisterHandlers(functions); } diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index f7f87a958..65040c077 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -103,6 +103,8 @@ public: {4, nullptr, "DebugIoctl"}, {5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"}, {6, nullptr, "FlushSessionCache"}, + {7, nullptr, "SetDebugOption"}, + {8, nullptr, "GetDebugOption"}, }; // clang-format on diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index b3a196f65..8d122ae33 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp @@ -8,6 +8,7 @@ namespace Service::Time { Time::Time(std::shared_ptr<Module> time, const char* name) : Module::Interface(std::move(time), name) { + // clang-format off static const FunctionInfo functions[] = { {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, @@ -15,18 +16,23 @@ Time::Time(std::shared_ptr<Module> time, const char* name) {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, {5, nullptr, "GetEphemeralNetworkSystemClock"}, + {20, nullptr, "GetSharedMemoryNativeHandle"}, + {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, + {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, {50, nullptr, "SetStandardSteadyClockInternalOffset"}, {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, {102, nullptr, "GetStandardUserSystemClockInitialYear"}, {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, + {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, - {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, - "CalculateStandardUserSystemClockDifferenceByUser"}, + {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, {501, nullptr, "CalculateSpanBetween"}, }; + // clang-format on + RegisterHandlers(functions); } 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 { bool use_disk_shader_cache; bool use_accurate_gpu_emulation; bool use_asynchronous_gpu_emulation; + bool force_30fps_mode; float bg_red; float bg_green; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 242a0d1cd..114bed20d 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -106,6 +106,8 @@ add_library(video_core STATIC textures/decoders.cpp textures/decoders.h textures/texture.h + texture_cache.cpp + texture_cache.h video_core.cpp video_core.h ) @@ -127,12 +129,14 @@ if (ENABLE_VULKAN) renderer_vulkan/vk_sampler_cache.h renderer_vulkan/vk_scheduler.cpp renderer_vulkan/vk_scheduler.h + renderer_vulkan/vk_shader_decompiler.cpp + renderer_vulkan/vk_shader_decompiler.h renderer_vulkan/vk_stream_buffer.cpp renderer_vulkan/vk_stream_buffer.h renderer_vulkan/vk_swapchain.cpp renderer_vulkan/vk_swapchain.h) - target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) + target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) target_compile_definitions(video_core PRIVATE HAS_VULKAN) endif() @@ -140,3 +144,6 @@ create_target_directory_groups(video_core) target_link_libraries(video_core PUBLIC common core) target_link_libraries(video_core PRIVATE glad) +if (ENABLE_VULKAN) + target_link_libraries(video_core PRIVATE sirit) +endif() diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 2e1e96c81..acf475289 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -792,6 +792,12 @@ union Instruction { } ldg; union { + BitField<48, 3, UniformType> type; + BitField<46, 2, u64> cache_mode; + BitField<20, 24, s64> immediate_offset; + } stg; + + union { BitField<0, 3, u64> pred0; BitField<3, 3, u64> pred3; BitField<7, 1, u64> abs_a; diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp index 8d9ee81f1..ea4a593af 100644 --- a/src/video_core/renderer_opengl/gl_global_cache.cpp +++ b/src/video_core/renderer_opengl/gl_global_cache.cpp @@ -14,28 +14,28 @@ namespace OpenGL { -CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr) - : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, size{size} { +CachedGlobalRegion::CachedGlobalRegion(VAddr cpu_addr, u8* host_ptr, u32 size, u32 max_size) + : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, host_ptr{host_ptr}, size{size}, + max_size{max_size} { buffer.Create(); - // Bind and unbind the buffer so it gets allocated by the driver - glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); LabelGLObject(GL_BUFFER, buffer.handle, cpu_addr, "GlobalMemory"); } -void CachedGlobalRegion::Reload(u32 size_) { - constexpr auto max_size = static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize); +CachedGlobalRegion::~CachedGlobalRegion() = default; +void CachedGlobalRegion::Reload(u32 size_) { size = size_; if (size > max_size) { size = max_size; - LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the expected size {}!", size_, + LOG_CRITICAL(HW_GPU, "Global region size {} exceeded the supported size {}!", size_, max_size); } + glNamedBufferData(buffer.handle, size, host_ptr, GL_STREAM_DRAW); +} - // TODO(Rodrigo): Get rid of Memory::GetPointer with a staging buffer - glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer.handle); - glBufferData(GL_SHADER_STORAGE_BUFFER, size, GetHostPtr(), GL_DYNAMIC_DRAW); +void CachedGlobalRegion::Flush() { + LOG_DEBUG(Render_OpenGL, "Flushing {} bytes to CPU memory address 0x{:16}", size, cpu_addr); + glGetNamedBufferSubData(buffer.handle, 0, static_cast<GLsizeiptr>(size), host_ptr); } GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const { @@ -46,14 +46,16 @@ GlobalRegion GlobalRegionCacheOpenGL::TryGetReservedGlobalRegion(CacheAddr addr, return search->second; } -GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u32 size, - u8* host_ptr) { +GlobalRegion GlobalRegionCacheOpenGL::GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr, + u32 size) { GlobalRegion region{TryGetReservedGlobalRegion(ToCacheAddr(host_ptr), size)}; if (!region) { // No reserved surface available, create a new one and reserve it auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; - const auto cpu_addr = *memory_manager.GpuToCpuAddress(addr); - region = std::make_shared<CachedGlobalRegion>(cpu_addr, size, host_ptr); + const auto cpu_addr{memory_manager.GpuToCpuAddress(addr)}; + ASSERT(cpu_addr); + + region = std::make_shared<CachedGlobalRegion>(*cpu_addr, host_ptr, size, max_ssbo_size); ReserveGlobalRegion(region); } region->Reload(size); @@ -65,7 +67,11 @@ void GlobalRegionCacheOpenGL::ReserveGlobalRegion(GlobalRegion region) { } GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer) - : RasterizerCache{rasterizer} {} + : RasterizerCache{rasterizer} { + GLint max_ssbo_size_; + glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &max_ssbo_size_); + max_ssbo_size = static_cast<u32>(max_ssbo_size_); +} GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( const GLShader::GlobalMemoryEntry& global_region, @@ -73,7 +79,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( auto& gpu{Core::System::GetInstance().GPU()}; auto& memory_manager{gpu.MemoryManager()}; - const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<u64>(stage)]}; + const auto cbufs{gpu.Maxwell3D().state.shader_stages[static_cast<std::size_t>(stage)]}; const auto addr{cbufs.const_buffers[global_region.GetCbufIndex()].address + global_region.GetCbufOffset()}; const auto actual_addr{memory_manager.Read<u64>(addr)}; @@ -85,7 +91,7 @@ GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( if (!region) { // No global region found - create a new one - region = GetUncachedGlobalRegion(actual_addr, size, host_ptr); + region = GetUncachedGlobalRegion(actual_addr, host_ptr, size); Register(region); } diff --git a/src/video_core/renderer_opengl/gl_global_cache.h b/src/video_core/renderer_opengl/gl_global_cache.h index 5a21ab66f..196e6e278 100644 --- a/src/video_core/renderer_opengl/gl_global_cache.h +++ b/src/video_core/renderer_opengl/gl_global_cache.h @@ -19,7 +19,7 @@ namespace OpenGL { namespace GLShader { class GlobalMemoryEntry; -} // namespace GLShader +} class RasterizerOpenGL; class CachedGlobalRegion; @@ -27,7 +27,8 @@ using GlobalRegion = std::shared_ptr<CachedGlobalRegion>; class CachedGlobalRegion final : public RasterizerCacheObject { public: - explicit CachedGlobalRegion(VAddr cpu_addr, u32 size, u8* host_ptr); + explicit CachedGlobalRegion(VAddr cpu_addr, u8* host_ptr, u32 size, u32 max_size); + ~CachedGlobalRegion(); VAddr GetCpuAddr() const override { return cpu_addr; @@ -45,14 +46,14 @@ public: /// Reloads the global region from guest memory void Reload(u32 size_); - // TODO(Rodrigo): When global memory is written (STG), implement flushing - void Flush() override { - UNIMPLEMENTED(); - } + void Flush() override; private: VAddr cpu_addr{}; + u8* host_ptr{}; u32 size{}; + u32 max_size{}; + OGLBuffer buffer; }; @@ -66,10 +67,11 @@ public: private: GlobalRegion TryGetReservedGlobalRegion(CacheAddr addr, u32 size) const; - GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u32 size, u8* host_ptr); + GlobalRegion GetUncachedGlobalRegion(GPUVAddr addr, u8* host_ptr, u32 size); void ReserveGlobalRegion(GlobalRegion region); std::unordered_map<CacheAddr, GlobalRegion> reserve; + u32 max_ssbo_size{}; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 8cc21aa49..0b1fe3494 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -756,6 +756,7 @@ void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) { return; } res_cache.FlushRegion(addr, size); + global_cache.FlushRegion(addr, size); } void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { @@ -953,6 +954,9 @@ void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::Shade for (std::size_t bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { const auto& entry{entries[bindpoint]}; const auto& region{global_cache.GetGlobalRegion(entry, stage)}; + if (entry.IsWritten()) { + region->MarkAsModified(true, global_cache); + } bind_ssbo_pushbuffer.Push(region->GetBufferHandle(), 0, static_cast<GLsizeiptr>(region->GetSizeInBytes())); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index e4c64ae71..d4c2cf80e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -71,10 +71,6 @@ public: static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); - static constexpr std::size_t MaxGlobalMemorySize = 0x10000; - static_assert(MaxGlobalMemorySize % sizeof(float) == 0, - "The maximum size of a global memory must be a multiple of the size of float"); - private: class SamplerInfo { public: diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index aa6da1944..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, params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), params.srgb_conversion); - if (params.pixel_format == PixelFormat::R16U && config.tsc.depth_compare_enabled) { + if (config.tsc.depth_compare_enabled) { // Some titles create a 'R16U' (normalized 16-bit) texture with depth_compare enabled, // then attempt to sample from it via a shadow sampler. Convert format to Z16 (which also // causes GetFormatType to properly return 'Depth' below). - params.pixel_format = PixelFormat::Z16; + if (GetFormatType(params.pixel_format) == SurfaceType::ColorTexture) { + switch (params.pixel_format) { + case PixelFormat::R16S: + case PixelFormat::R16U: + case PixelFormat::R16F: + params.pixel_format = PixelFormat::Z16; + break; + case PixelFormat::R32F: + params.pixel_format = PixelFormat::Z32F; + break; + default: + LOG_WARNING(HW_GPU, "Color texture format being used with depth compare: {}", + static_cast<u32>(params.pixel_format)); + break; + } + } } params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); @@ -266,6 +281,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, params.component_type = ComponentTypeFromRenderTarget(config.format); params.type = GetFormatType(params.pixel_format); params.width = config.width; + params.pitch = config.pitch; params.height = config.height; params.unaligned_height = config.height; params.target = SurfaceTarget::Texture2D; @@ -1175,10 +1191,16 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, return new_surface; } + const bool old_compressed = + GetFormatTuple(old_params.pixel_format, old_params.component_type).compressed; + const bool new_compressed = + GetFormatTuple(new_params.pixel_format, new_params.component_type).compressed; + const bool compatible_formats = + GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format) && + !(old_compressed || new_compressed); // For compatible surfaces, we can just do fast glCopyImageSubData based copy - if (old_params.target == new_params.target && old_params.type == new_params.type && - old_params.depth == new_params.depth && old_params.depth == 1 && - GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) { + if (old_params.target == new_params.target && old_params.depth == new_params.depth && + old_params.depth == 1 && compatible_formats) { FastCopySurface(old_surface, new_surface); return new_surface; } @@ -1193,7 +1215,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, case SurfaceTarget::TextureCubemap: case SurfaceTarget::Texture2DArray: case SurfaceTarget::TextureCubeArray: - if (old_params.pixel_format == new_params.pixel_format) + if (compatible_formats) FastLayeredCopySurface(old_surface, new_surface); else { AccurateCopySurface(old_surface, new_surface); diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 28e490b3c..445048daf 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -45,8 +45,6 @@ using TextureIR = std::variant<TextureAoffi, TextureArgument>; enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; constexpr u32 MAX_CONSTBUFFER_ELEMENTS = static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float)); -constexpr u32 MAX_GLOBALMEMORY_ELEMENTS = - static_cast<u32>(RasterizerOpenGL::MaxGlobalMemorySize) / sizeof(float); class ShaderWriter { public: @@ -208,8 +206,10 @@ public: for (const auto& sampler : ir.GetSamplers()) { entries.samplers.emplace_back(sampler); } - for (const auto& gmem : ir.GetGlobalMemoryBases()) { - entries.global_memory_entries.emplace_back(gmem.cbuf_index, gmem.cbuf_offset); + for (const auto& gmem_pair : ir.GetGlobalMemory()) { + const auto& [base, usage] = gmem_pair; + entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset, + usage.is_read, usage.is_written); } entries.clip_distances = ir.GetClipDistances(); entries.shader_length = ir.GetLength(); @@ -380,12 +380,22 @@ private: } void DeclareGlobalMemory() { - for (const auto& entry : ir.GetGlobalMemoryBases()) { + for (const auto& gmem : ir.GetGlobalMemory()) { + const auto& [base, usage] = gmem; + + // Since we don't know how the shader will use the shader, hint the driver to disable as + // much optimizations as possible + std::string qualifier = "coherent volatile"; + if (usage.is_read && !usage.is_written) + qualifier += " readonly"; + else if (usage.is_written && !usage.is_read) + qualifier += " writeonly"; + const std::string binding = - fmt::format("GMEM_BINDING_{}_{}", entry.cbuf_index, entry.cbuf_offset); - code.AddLine("layout (std430, binding = " + binding + ") buffer " + - GetGlobalMemoryBlock(entry) + " {"); - code.AddLine(" float " + GetGlobalMemory(entry) + "[MAX_GLOBALMEMORY_ELEMENTS];"); + fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset); + code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " + + GetGlobalMemoryBlock(base) + " {"); + code.AddLine(" float " + GetGlobalMemory(base) + "[];"); code.AddLine("};"); code.AddNewLine(); } @@ -868,6 +878,12 @@ private: } else if (const auto lmem = std::get_if<LmemNode>(dest)) { target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; + } else if (const auto gmem = std::get_if<GmemNode>(dest)) { + const std::string real = Visit(gmem->GetRealAddress()); + const std::string base = Visit(gmem->GetBaseAddress()); + const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; + target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); + } else { UNREACHABLE_MSG("Assign called without a proper target"); } @@ -1621,9 +1637,7 @@ private: std::string GetCommonDeclarations() { const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); - const auto gmem = std::to_string(MAX_GLOBALMEMORY_ELEMENTS); return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + - "#define MAX_GLOBALMEMORY_ELEMENTS " + gmem + "\n" + "#define ftoi floatBitsToInt\n" "#define ftou floatBitsToUint\n" "#define itof intBitsToFloat\n" diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index 4e04ab2f8..55b3d4d7b 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h @@ -39,8 +39,9 @@ private: class GlobalMemoryEntry { public: - explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset) - : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {} + explicit GlobalMemoryEntry(u32 cbuf_index, u32 cbuf_offset, bool is_read, bool is_written) + : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset}, is_read{is_read}, is_written{ + is_written} {} u32 GetCbufIndex() const { return cbuf_index; @@ -50,9 +51,19 @@ public: return cbuf_offset; } + bool IsRead() const { + return is_read; + } + + bool IsWritten() const { + return is_written; + } + private: u32 cbuf_index{}; u32 cbuf_offset{}; + bool is_read{}; + bool is_written{}; }; struct ShaderEntries { diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index 8a43eb157..d5890a375 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -337,11 +337,16 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn for (u32 i = 0; i < global_memory_count; ++i) { u32 cbuf_index{}; u32 cbuf_offset{}; + u8 is_read{}; + u8 is_written{}; if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || - file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32)) { + file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32) || + file.ReadBytes(&is_read, sizeof(u8)) != sizeof(u8) || + file.ReadBytes(&is_written, sizeof(u8)) != sizeof(u8)) { return {}; } - entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset); + entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0, + is_written != 0); } for (auto& clip_distance : entry.entries.clip_distances) { @@ -397,7 +402,9 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 uniqu return false; for (const auto& gmem : entries.global_memory_entries) { if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 || - file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1) { + file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1 || + file.WriteObject(static_cast<u8>(gmem.IsRead() ? 1 : 0)) != 1 || + file.WriteObject(static_cast<u8>(gmem.IsWritten() ? 1 : 0)) != 1) { return false; } } 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/common_types.h" +#include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_shader_manager.h" namespace OpenGL::GLShader { using Tegra::Engines::Maxwell3D; +ProgramManager::ProgramManager() { + pipeline.Create(); +} + +ProgramManager::~ProgramManager() = default; + +void ProgramManager::ApplyTo(OpenGLState& state) { + UpdatePipeline(); + state.draw.shader_program = 0; + state.draw.program_pipeline = pipeline.handle; +} + +void ProgramManager::UpdatePipeline() { + // Avoid updating the pipeline when values have no changed + if (old_state == current_state) { + return; + } + + // Workaround for AMD bug + constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | + GL_FRAGMENT_SHADER_BIT}; + glUseProgramStages(pipeline.handle, all_used_stages, 0); + + glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader); + glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, current_state.geometry_shader); + glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current_state.fragment_shader); + + old_state = current_state; +} + void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) { const auto& regs = maxwell.regs; const auto& state = maxwell.state; @@ -16,7 +48,7 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shade viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f; - u32 func = static_cast<u32>(regs.alpha_test_func); + auto func{static_cast<u32>(regs.alpha_test_func)}; // Normalize the gl variants of opCompare to be the same as the normal variants const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never); 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 @@ #pragma once +#include <cstddef> + #include <glad/glad.h> #include "video_core/renderer_opengl/gl_resource_manager.h" @@ -38,55 +40,48 @@ static_assert(sizeof(MaxwellUniformData) < 16384, class ProgramManager { public: - ProgramManager() { - pipeline.Create(); - } + explicit ProgramManager(); + ~ProgramManager(); + + void ApplyTo(OpenGLState& state); void UseProgrammableVertexShader(GLuint program) { - vs = program; + current_state.vertex_shader = program; } void UseProgrammableGeometryShader(GLuint program) { - gs = program; + current_state.geometry_shader = program; } void UseProgrammableFragmentShader(GLuint program) { - fs = program; + current_state.fragment_shader = program; } void UseTrivialGeometryShader() { - gs = 0; - } - - void ApplyTo(OpenGLState& state) { - UpdatePipeline(); - state.draw.shader_program = 0; - state.draw.program_pipeline = pipeline.handle; + current_state.geometry_shader = 0; } private: - void UpdatePipeline() { - // Avoid updating the pipeline when values have no changed - if (old_vs == vs && old_fs == fs && old_gs == gs) - return; - // Workaround for AMD bug - glUseProgramStages(pipeline.handle, - GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, - 0); - - glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vs); - glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, gs); - glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs); - - // Update the old values - old_vs = vs; - old_fs = fs; - old_gs = gs; - } + struct PipelineState { + bool operator==(const PipelineState& rhs) const { + return vertex_shader == rhs.vertex_shader && fragment_shader == rhs.fragment_shader && + geometry_shader == rhs.geometry_shader; + } + + bool operator!=(const PipelineState& rhs) const { + return !operator==(rhs); + } + + GLuint vertex_shader{}; + GLuint fragment_shader{}; + GLuint geometry_shader{}; + }; + + void UpdatePipeline(); OGLPipeline pipeline; - GLuint vs{}, fs{}, gs{}; - GLuint old_vs{}, old_fs{}, old_gs{}; + PipelineState current_state; + PipelineState old_state; }; } // namespace OpenGL::GLShader diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp new file mode 100644 index 000000000..25500f9a3 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -0,0 +1,1381 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <functional> +#include <map> +#include <set> + +#include <fmt/format.h> + +#include <sirit/sirit.h> + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/engines/shader_bytecode.h" +#include "video_core/engines/shader_header.h" +#include "video_core/renderer_vulkan/vk_shader_decompiler.h" +#include "video_core/shader/shader_ir.h" + +namespace Vulkan::VKShader { + +using Sirit::Id; +using Tegra::Shader::Attribute; +using Tegra::Shader::AttributeUse; +using Tegra::Shader::Register; +using namespace VideoCommon::Shader; + +using Maxwell = Tegra::Engines::Maxwell3D::Regs; +using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage; +using Operation = const OperationNode&; + +// TODO(Rodrigo): Use rasterizer's value +constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 0x1000; +constexpr u32 STAGE_BINDING_STRIDE = 0x100; + +enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat }; + +struct SamplerImage { + Id image_type; + Id sampled_image_type; + Id sampler; +}; + +namespace { + +spv::Dim GetSamplerDim(const Sampler& sampler) { + switch (sampler.GetType()) { + case Tegra::Shader::TextureType::Texture1D: + return spv::Dim::Dim1D; + case Tegra::Shader::TextureType::Texture2D: + return spv::Dim::Dim2D; + case Tegra::Shader::TextureType::Texture3D: + return spv::Dim::Dim3D; + case Tegra::Shader::TextureType::TextureCube: + return spv::Dim::Cube; + default: + UNIMPLEMENTED_MSG("Unimplemented sampler type={}", static_cast<u32>(sampler.GetType())); + return spv::Dim::Dim2D; + } +} + +/// Returns true if an attribute index is one of the 32 generic attributes +constexpr bool IsGenericAttribute(Attribute::Index attribute) { + return attribute >= Attribute::Index::Attribute_0 && + attribute <= Attribute::Index::Attribute_31; +} + +/// Returns the location of a generic attribute +constexpr u32 GetGenericAttributeLocation(Attribute::Index attribute) { + ASSERT(IsGenericAttribute(attribute)); + return static_cast<u32>(attribute) - static_cast<u32>(Attribute::Index::Attribute_0); +} + +/// Returns true if an object has to be treated as precise +bool IsPrecise(Operation operand) { + const auto& meta = operand.GetMeta(); + + if (std::holds_alternative<MetaArithmetic>(meta)) { + return std::get<MetaArithmetic>(meta).precise; + } + if (std::holds_alternative<MetaHalfArithmetic>(meta)) { + return std::get<MetaHalfArithmetic>(meta).precise; + } + return false; +} + +} // namespace + +class SPIRVDecompiler : public Sirit::Module { +public: + explicit SPIRVDecompiler(const ShaderIR& ir, ShaderStage stage) + : Module(0x00010300), ir{ir}, stage{stage}, header{ir.GetHeader()} { + AddCapability(spv::Capability::Shader); + AddExtension("SPV_KHR_storage_buffer_storage_class"); + AddExtension("SPV_KHR_variable_pointers"); + } + + void Decompile() { + AllocateBindings(); + AllocateLabels(); + + DeclareVertex(); + DeclareGeometry(); + DeclareFragment(); + DeclareRegisters(); + DeclarePredicates(); + DeclareLocalMemory(); + DeclareInternalFlags(); + DeclareInputAttributes(); + DeclareOutputAttributes(); + DeclareConstantBuffers(); + DeclareGlobalBuffers(); + DeclareSamplers(); + + execute_function = + Emit(OpFunction(t_void, spv::FunctionControlMask::Inline, TypeFunction(t_void))); + Emit(OpLabel()); + + const u32 first_address = ir.GetBasicBlocks().begin()->first; + const Id loop_label = OpLabel("loop"); + const Id merge_label = OpLabel("merge"); + const Id dummy_label = OpLabel(); + const Id jump_label = OpLabel(); + continue_label = OpLabel("continue"); + + std::vector<Sirit::Literal> literals; + std::vector<Id> branch_labels; + for (const auto& pair : labels) { + const auto [literal, label] = pair; + literals.push_back(literal); + branch_labels.push_back(label); + } + + // TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely + // that shaders will use 20 nested SSYs and PBKs. + constexpr u32 FLOW_STACK_SIZE = 20; + const Id flow_stack_type = TypeArray(t_uint, Constant(t_uint, FLOW_STACK_SIZE)); + jmp_to = Emit(OpVariable(TypePointer(spv::StorageClass::Function, t_uint), + spv::StorageClass::Function, Constant(t_uint, first_address))); + flow_stack = Emit(OpVariable(TypePointer(spv::StorageClass::Function, flow_stack_type), + spv::StorageClass::Function, ConstantNull(flow_stack_type))); + flow_stack_top = + Emit(OpVariable(t_func_uint, spv::StorageClass::Function, Constant(t_uint, 0))); + + Name(jmp_to, "jmp_to"); + Name(flow_stack, "flow_stack"); + Name(flow_stack_top, "flow_stack_top"); + + Emit(OpBranch(loop_label)); + Emit(loop_label); + Emit(OpLoopMerge(merge_label, continue_label, spv::LoopControlMask::Unroll)); + Emit(OpBranch(dummy_label)); + + Emit(dummy_label); + const Id default_branch = OpLabel(); + const Id jmp_to_load = Emit(OpLoad(t_uint, jmp_to)); + Emit(OpSelectionMerge(jump_label, spv::SelectionControlMask::MaskNone)); + Emit(OpSwitch(jmp_to_load, default_branch, literals, branch_labels)); + + Emit(default_branch); + Emit(OpReturn()); + + for (const auto& pair : ir.GetBasicBlocks()) { + const auto& [address, bb] = pair; + Emit(labels.at(address)); + + VisitBasicBlock(bb); + + const auto next_it = labels.lower_bound(address + 1); + const Id next_label = next_it != labels.end() ? next_it->second : default_branch; + Emit(OpBranch(next_label)); + } + + Emit(jump_label); + Emit(OpBranch(continue_label)); + Emit(continue_label); + Emit(OpBranch(loop_label)); + Emit(merge_label); + Emit(OpReturn()); + Emit(OpFunctionEnd()); + } + + ShaderEntries GetShaderEntries() const { + ShaderEntries entries; + entries.const_buffers_base_binding = const_buffers_base_binding; + entries.global_buffers_base_binding = global_buffers_base_binding; + entries.samplers_base_binding = samplers_base_binding; + for (const auto& cbuf : ir.GetConstantBuffers()) { + entries.const_buffers.emplace_back(cbuf.second, cbuf.first); + } + for (const auto& gmem_pair : ir.GetGlobalMemory()) { + const auto& [base, usage] = gmem_pair; + entries.global_buffers.emplace_back(base.cbuf_index, base.cbuf_offset); + } + for (const auto& sampler : ir.GetSamplers()) { + entries.samplers.emplace_back(sampler); + } + for (const auto& attr : ir.GetInputAttributes()) { + entries.attributes.insert(GetGenericAttributeLocation(attr.first)); + } + entries.clip_distances = ir.GetClipDistances(); + entries.shader_length = ir.GetLength(); + entries.entry_function = execute_function; + entries.interfaces = interfaces; + return entries; + } + +private: + using OperationDecompilerFn = Id (SPIRVDecompiler::*)(Operation); + using OperationDecompilersArray = + std::array<OperationDecompilerFn, static_cast<std::size_t>(OperationCode::Amount)>; + + static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount); + static constexpr u32 CBUF_STRIDE = 16; + + void AllocateBindings() { + const u32 binding_base = static_cast<u32>(stage) * STAGE_BINDING_STRIDE; + u32 binding_iterator = binding_base; + + const auto Allocate = [&binding_iterator](std::size_t count) { + const u32 current_binding = binding_iterator; + binding_iterator += static_cast<u32>(count); + return current_binding; + }; + const_buffers_base_binding = Allocate(ir.GetConstantBuffers().size()); + global_buffers_base_binding = Allocate(ir.GetGlobalMemory().size()); + samplers_base_binding = Allocate(ir.GetSamplers().size()); + + ASSERT_MSG(binding_iterator - binding_base < STAGE_BINDING_STRIDE, + "Stage binding stride is too small"); + } + + void AllocateLabels() { + for (const auto& pair : ir.GetBasicBlocks()) { + const u32 address = pair.first; + labels.emplace(address, OpLabel(fmt::format("label_0x{:x}", address))); + } + } + + void DeclareVertex() { + if (stage != ShaderStage::Vertex) + return; + + DeclareVertexRedeclarations(); + } + + void DeclareGeometry() { + if (stage != ShaderStage::Geometry) + return; + + UNIMPLEMENTED(); + } + + void DeclareFragment() { + if (stage != ShaderStage::Fragment) + return; + + for (u32 rt = 0; rt < static_cast<u32>(frag_colors.size()); ++rt) { + if (!IsRenderTargetUsed(rt)) { + continue; + } + + const Id id = AddGlobalVariable(OpVariable(t_out_float4, spv::StorageClass::Output)); + Name(id, fmt::format("frag_color{}", rt)); + Decorate(id, spv::Decoration::Location, rt); + + frag_colors[rt] = id; + interfaces.push_back(id); + } + + if (header.ps.omap.depth) { + frag_depth = AddGlobalVariable(OpVariable(t_out_float, spv::StorageClass::Output)); + Name(frag_depth, "frag_depth"); + Decorate(frag_depth, spv::Decoration::BuiltIn, + static_cast<u32>(spv::BuiltIn::FragDepth)); + + interfaces.push_back(frag_depth); + } + + frag_coord = DeclareBuiltIn(spv::BuiltIn::FragCoord, spv::StorageClass::Input, t_in_float4, + "frag_coord"); + front_facing = DeclareBuiltIn(spv::BuiltIn::FrontFacing, spv::StorageClass::Input, + t_in_bool, "front_facing"); + } + + void DeclareRegisters() { + for (const u32 gpr : ir.GetRegisters()) { + const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero); + Name(id, fmt::format("gpr_{}", gpr)); + registers.emplace(gpr, AddGlobalVariable(id)); + } + } + + void DeclarePredicates() { + for (const auto pred : ir.GetPredicates()) { + const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); + Name(id, fmt::format("pred_{}", static_cast<u32>(pred))); + predicates.emplace(pred, AddGlobalVariable(id)); + } + } + + void DeclareLocalMemory() { + if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { + const auto element_count = static_cast<u32>(Common::AlignUp(local_memory_size, 4) / 4); + const Id type_array = TypeArray(t_float, Constant(t_uint, element_count)); + const Id type_pointer = TypePointer(spv::StorageClass::Private, type_array); + Name(type_pointer, "LocalMemory"); + + local_memory = + OpVariable(type_pointer, spv::StorageClass::Private, ConstantNull(type_array)); + AddGlobalVariable(Name(local_memory, "local_memory")); + } + } + + void DeclareInternalFlags() { + constexpr std::array<const char*, INTERNAL_FLAGS_COUNT> names = {"zero", "sign", "carry", + "overflow"}; + for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) { + const auto flag_code = static_cast<InternalFlag>(flag); + const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); + internal_flags[flag] = AddGlobalVariable(Name(id, names[flag])); + } + } + + void DeclareInputAttributes() { + for (const auto element : ir.GetInputAttributes()) { + const Attribute::Index index = element.first; + if (!IsGenericAttribute(index)) { + continue; + } + + UNIMPLEMENTED_IF(stage == ShaderStage::Geometry); + + const u32 location = GetGenericAttributeLocation(index); + const Id id = OpVariable(t_in_float4, spv::StorageClass::Input); + Name(AddGlobalVariable(id), fmt::format("in_attr{}", location)); + input_attributes.emplace(index, id); + interfaces.push_back(id); + + Decorate(id, spv::Decoration::Location, location); + + if (stage != ShaderStage::Fragment) { + continue; + } + switch (header.ps.GetAttributeUse(location)) { + case AttributeUse::Constant: + Decorate(id, spv::Decoration::Flat); + break; + case AttributeUse::ScreenLinear: + Decorate(id, spv::Decoration::NoPerspective); + break; + case AttributeUse::Perspective: + // Default + break; + default: + UNREACHABLE_MSG("Unused attribute being fetched"); + } + } + } + + void DeclareOutputAttributes() { + for (const auto index : ir.GetOutputAttributes()) { + if (!IsGenericAttribute(index)) { + continue; + } + const auto location = GetGenericAttributeLocation(index); + const Id id = OpVariable(t_out_float4, spv::StorageClass::Output); + Name(AddGlobalVariable(id), fmt::format("out_attr{}", location)); + output_attributes.emplace(index, id); + interfaces.push_back(id); + + Decorate(id, spv::Decoration::Location, location); + } + } + + void DeclareConstantBuffers() { + u32 binding = const_buffers_base_binding; + for (const auto& entry : ir.GetConstantBuffers()) { + const auto [index, size] = entry; + const Id id = OpVariable(t_cbuf_ubo, spv::StorageClass::Uniform); + AddGlobalVariable(Name(id, fmt::format("cbuf_{}", index))); + + Decorate(id, spv::Decoration::Binding, binding++); + Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); + constant_buffers.emplace(index, id); + } + } + + void DeclareGlobalBuffers() { + u32 binding = global_buffers_base_binding; + for (const auto& entry : ir.GetGlobalMemory()) { + const auto [base, usage] = entry; + const Id id = OpVariable(t_gmem_ssbo, spv::StorageClass::StorageBuffer); + AddGlobalVariable( + Name(id, fmt::format("gmem_{}_{}", base.cbuf_index, base.cbuf_offset))); + + Decorate(id, spv::Decoration::Binding, binding++); + Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); + global_buffers.emplace(base, id); + } + } + + void DeclareSamplers() { + u32 binding = samplers_base_binding; + for (const auto& sampler : ir.GetSamplers()) { + const auto dim = GetSamplerDim(sampler); + const int depth = sampler.IsShadow() ? 1 : 0; + const int arrayed = sampler.IsArray() ? 1 : 0; + // TODO(Rodrigo): Sampled 1 indicates that the image will be used with a sampler. When + // SULD and SUST instructions are implemented, replace this value. + const int sampled = 1; + const Id image_type = + TypeImage(t_float, dim, depth, arrayed, false, sampled, spv::ImageFormat::Unknown); + const Id sampled_image_type = TypeSampledImage(image_type); + const Id pointer_type = + TypePointer(spv::StorageClass::UniformConstant, sampled_image_type); + const Id id = OpVariable(pointer_type, spv::StorageClass::UniformConstant); + AddGlobalVariable(Name(id, fmt::format("sampler_{}", sampler.GetIndex()))); + + sampler_images.insert( + {static_cast<u32>(sampler.GetIndex()), {image_type, sampled_image_type, id}}); + + Decorate(id, spv::Decoration::Binding, binding++); + Decorate(id, spv::Decoration::DescriptorSet, DESCRIPTOR_SET); + } + } + + void DeclareVertexRedeclarations() { + vertex_index = DeclareBuiltIn(spv::BuiltIn::VertexIndex, spv::StorageClass::Input, + t_in_uint, "vertex_index"); + instance_index = DeclareBuiltIn(spv::BuiltIn::InstanceIndex, spv::StorageClass::Input, + t_in_uint, "instance_index"); + + bool is_point_size_declared = false; + bool is_clip_distances_declared = false; + for (const auto index : ir.GetOutputAttributes()) { + if (index == Attribute::Index::PointSize) { + is_point_size_declared = true; + } else if (index == Attribute::Index::ClipDistances0123 || + index == Attribute::Index::ClipDistances4567) { + is_clip_distances_declared = true; + } + } + + std::vector<Id> members; + members.push_back(t_float4); + if (is_point_size_declared) { + members.push_back(t_float); + } + if (is_clip_distances_declared) { + members.push_back(TypeArray(t_float, Constant(t_uint, 8))); + } + + const Id gl_per_vertex_struct = Name(TypeStruct(members), "PerVertex"); + Decorate(gl_per_vertex_struct, spv::Decoration::Block); + + u32 declaration_index = 0; + const auto MemberDecorateBuiltIn = [&](spv::BuiltIn builtin, std::string name, + bool condition) { + if (!condition) + return u32{}; + MemberName(gl_per_vertex_struct, declaration_index, name); + MemberDecorate(gl_per_vertex_struct, declaration_index, spv::Decoration::BuiltIn, + static_cast<u32>(builtin)); + return declaration_index++; + }; + + position_index = MemberDecorateBuiltIn(spv::BuiltIn::Position, "position", true); + point_size_index = + MemberDecorateBuiltIn(spv::BuiltIn::PointSize, "point_size", is_point_size_declared); + clip_distances_index = MemberDecorateBuiltIn(spv::BuiltIn::ClipDistance, "clip_distances", + is_clip_distances_declared); + + const Id type_pointer = TypePointer(spv::StorageClass::Output, gl_per_vertex_struct); + per_vertex = OpVariable(type_pointer, spv::StorageClass::Output); + AddGlobalVariable(Name(per_vertex, "per_vertex")); + interfaces.push_back(per_vertex); + } + + void VisitBasicBlock(const NodeBlock& bb) { + for (const Node node : bb) { + static_cast<void>(Visit(node)); + } + } + + Id Visit(Node node) { + if (const auto operation = std::get_if<OperationNode>(node)) { + const auto operation_index = static_cast<std::size_t>(operation->GetCode()); + const auto decompiler = operation_decompilers[operation_index]; + if (decompiler == nullptr) { + UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index); + } + return (this->*decompiler)(*operation); + + } else if (const auto gpr = std::get_if<GprNode>(node)) { + const u32 index = gpr->GetIndex(); + if (index == Register::ZeroIndex) { + return Constant(t_float, 0.0f); + } + return Emit(OpLoad(t_float, registers.at(index))); + + } else if (const auto immediate = std::get_if<ImmediateNode>(node)) { + return BitcastTo<Type::Float>(Constant(t_uint, immediate->GetValue())); + + } else if (const auto predicate = std::get_if<PredicateNode>(node)) { + const auto value = [&]() -> Id { + switch (const auto index = predicate->GetIndex(); index) { + case Tegra::Shader::Pred::UnusedIndex: + return v_true; + case Tegra::Shader::Pred::NeverExecute: + return v_false; + default: + return Emit(OpLoad(t_bool, predicates.at(index))); + } + }(); + if (predicate->IsNegated()) { + return Emit(OpLogicalNot(t_bool, value)); + } + return value; + + } else if (const auto abuf = std::get_if<AbufNode>(node)) { + const auto attribute = abuf->GetIndex(); + const auto element = abuf->GetElement(); + + switch (attribute) { + case Attribute::Index::Position: + if (stage != ShaderStage::Fragment) { + UNIMPLEMENTED(); + break; + } else { + if (element == 3) { + return Constant(t_float, 1.0f); + } + return Emit(OpLoad(t_float, AccessElement(t_in_float, frag_coord, element))); + } + case Attribute::Index::TessCoordInstanceIDVertexID: + // TODO(Subv): Find out what the values are for the first two elements when inside a + // vertex shader, and what's the value of the fourth element when inside a Tess Eval + // shader. + ASSERT(stage == ShaderStage::Vertex); + switch (element) { + case 2: + return BitcastFrom<Type::Uint>(Emit(OpLoad(t_uint, instance_index))); + case 3: + return BitcastFrom<Type::Uint>(Emit(OpLoad(t_uint, vertex_index))); + } + UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); + return Constant(t_float, 0); + case Attribute::Index::FrontFacing: + // TODO(Subv): Find out what the values are for the other elements. + ASSERT(stage == ShaderStage::Fragment); + if (element == 3) { + const Id is_front_facing = Emit(OpLoad(t_bool, front_facing)); + const Id true_value = + BitcastTo<Type::Float>(Constant(t_int, static_cast<s32>(-1))); + const Id false_value = BitcastTo<Type::Float>(Constant(t_int, 0)); + return Emit(OpSelect(t_float, is_front_facing, true_value, false_value)); + } + UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); + return Constant(t_float, 0.0f); + default: + if (IsGenericAttribute(attribute)) { + const Id pointer = + AccessElement(t_in_float, input_attributes.at(attribute), element); + return Emit(OpLoad(t_float, pointer)); + } + break; + } + UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); + + } else if (const auto cbuf = std::get_if<CbufNode>(node)) { + const Node offset = cbuf->GetOffset(); + const Id buffer_id = constant_buffers.at(cbuf->GetIndex()); + + Id buffer_index{}; + Id buffer_element{}; + + if (const auto immediate = std::get_if<ImmediateNode>(offset)) { + // Direct access + const u32 offset_imm = immediate->GetValue(); + ASSERT(offset_imm % 4 == 0); + buffer_index = Constant(t_uint, offset_imm / 16); + buffer_element = Constant(t_uint, (offset_imm / 4) % 4); + + } else if (std::holds_alternative<OperationNode>(*offset)) { + // Indirect access + // TODO(Rodrigo): Use a uniform buffer stride of 4 and drop this slow math (which + // emits sub-optimal code on GLSL from my testing). + const Id offset_id = BitcastTo<Type::Uint>(Visit(offset)); + const Id unsafe_offset = Emit(OpUDiv(t_uint, offset_id, Constant(t_uint, 4))); + const Id final_offset = Emit( + OpUMod(t_uint, unsafe_offset, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS - 1))); + buffer_index = Emit(OpUDiv(t_uint, final_offset, Constant(t_uint, 4))); + buffer_element = Emit(OpUMod(t_uint, final_offset, Constant(t_uint, 4))); + + } else { + UNREACHABLE_MSG("Unmanaged offset node type"); + } + + const Id pointer = Emit(OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0), + buffer_index, buffer_element)); + return Emit(OpLoad(t_float, pointer)); + + } else if (const auto gmem = std::get_if<GmemNode>(node)) { + const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); + const Id real = BitcastTo<Type::Uint>(Visit(gmem->GetRealAddress())); + const Id base = BitcastTo<Type::Uint>(Visit(gmem->GetBaseAddress())); + + Id offset = Emit(OpISub(t_uint, real, base)); + offset = Emit(OpUDiv(t_uint, offset, Constant(t_uint, 4u))); + return Emit(OpLoad(t_float, Emit(OpAccessChain(t_gmem_float, gmem_buffer, + Constant(t_uint, 0u), offset)))); + + } else if (const auto conditional = std::get_if<ConditionalNode>(node)) { + // It's invalid to call conditional on nested nodes, use an operation instead + const Id true_label = OpLabel(); + const Id skip_label = OpLabel(); + Emit(OpBranchConditional(Visit(conditional->GetCondition()), true_label, skip_label)); + Emit(true_label); + + VisitBasicBlock(conditional->GetCode()); + + Emit(OpBranch(skip_label)); + Emit(skip_label); + return {}; + + } else if (const auto comment = std::get_if<CommentNode>(node)) { + Name(Emit(OpUndef(t_void)), comment->GetText()); + return {}; + } + + UNREACHABLE(); + return {}; + } + + template <Id (Module::*func)(Id, Id), Type result_type, Type type_a = result_type> + Id Unary(Operation operation) { + const Id type_def = GetTypeDefinition(result_type); + const Id op_a = VisitOperand<type_a>(operation, 0); + + const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a))); + if (IsPrecise(operation)) { + Decorate(value, spv::Decoration::NoContraction); + } + return value; + } + + template <Id (Module::*func)(Id, Id, Id), Type result_type, Type type_a = result_type, + Type type_b = type_a> + Id Binary(Operation operation) { + const Id type_def = GetTypeDefinition(result_type); + const Id op_a = VisitOperand<type_a>(operation, 0); + const Id op_b = VisitOperand<type_b>(operation, 1); + + const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b))); + if (IsPrecise(operation)) { + Decorate(value, spv::Decoration::NoContraction); + } + return value; + } + + template <Id (Module::*func)(Id, Id, Id, Id), Type result_type, Type type_a = result_type, + Type type_b = type_a, Type type_c = type_b> + Id Ternary(Operation operation) { + const Id type_def = GetTypeDefinition(result_type); + const Id op_a = VisitOperand<type_a>(operation, 0); + const Id op_b = VisitOperand<type_b>(operation, 1); + const Id op_c = VisitOperand<type_c>(operation, 2); + + const Id value = BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b, op_c))); + if (IsPrecise(operation)) { + Decorate(value, spv::Decoration::NoContraction); + } + return value; + } + + template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type, Type type_a = result_type, + Type type_b = type_a, Type type_c = type_b, Type type_d = type_c> + Id Quaternary(Operation operation) { + const Id type_def = GetTypeDefinition(result_type); + const Id op_a = VisitOperand<type_a>(operation, 0); + const Id op_b = VisitOperand<type_b>(operation, 1); + const Id op_c = VisitOperand<type_c>(operation, 2); + const Id op_d = VisitOperand<type_d>(operation, 3); + + const Id value = + BitcastFrom<result_type>(Emit((this->*func)(type_def, op_a, op_b, op_c, op_d))); + if (IsPrecise(operation)) { + Decorate(value, spv::Decoration::NoContraction); + } + return value; + } + + Id Assign(Operation operation) { + const Node dest = operation[0]; + const Node src = operation[1]; + + Id target{}; + if (const auto gpr = std::get_if<GprNode>(dest)) { + if (gpr->GetIndex() == Register::ZeroIndex) { + // Writing to Register::ZeroIndex is a no op + return {}; + } + target = registers.at(gpr->GetIndex()); + + } else if (const auto abuf = std::get_if<AbufNode>(dest)) { + target = [&]() -> Id { + switch (const auto attribute = abuf->GetIndex(); attribute) { + case Attribute::Index::Position: + return AccessElement(t_out_float, per_vertex, position_index, + abuf->GetElement()); + case Attribute::Index::PointSize: + return AccessElement(t_out_float, per_vertex, point_size_index); + case Attribute::Index::ClipDistances0123: + return AccessElement(t_out_float, per_vertex, clip_distances_index, + abuf->GetElement()); + case Attribute::Index::ClipDistances4567: + return AccessElement(t_out_float, per_vertex, clip_distances_index, + abuf->GetElement() + 4); + default: + if (IsGenericAttribute(attribute)) { + return AccessElement(t_out_float, output_attributes.at(attribute), + abuf->GetElement()); + } + UNIMPLEMENTED_MSG("Unhandled output attribute: {}", + static_cast<u32>(attribute)); + return {}; + } + }(); + + } else if (const auto lmem = std::get_if<LmemNode>(dest)) { + Id address = BitcastTo<Type::Uint>(Visit(lmem->GetAddress())); + address = Emit(OpUDiv(t_uint, address, Constant(t_uint, 4))); + target = Emit(OpAccessChain(t_prv_float, local_memory, {address})); + } + + Emit(OpStore(target, Visit(src))); + return {}; + } + + Id HNegate(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id HMergeF32(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id HMergeH0(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id HMergeH1(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id HPack2(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id LogicalAssign(Operation operation) { + const Node dest = operation[0]; + const Node src = operation[1]; + + Id target{}; + if (const auto pred = std::get_if<PredicateNode>(dest)) { + ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment"); + + const auto index = pred->GetIndex(); + switch (index) { + case Tegra::Shader::Pred::NeverExecute: + case Tegra::Shader::Pred::UnusedIndex: + // Writing to these predicates is a no-op + return {}; + } + target = predicates.at(index); + + } else if (const auto flag = std::get_if<InternalFlagNode>(dest)) { + target = internal_flags.at(static_cast<u32>(flag->GetFlag())); + } + + Emit(OpStore(target, Visit(src))); + return {}; + } + + Id LogicalPick2(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id LogicalAll2(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id LogicalAny2(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id GetTextureSampler(Operation operation) { + const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); + const auto entry = sampler_images.at(static_cast<u32>(meta->sampler.GetIndex())); + return Emit(OpLoad(entry.sampled_image_type, entry.sampler)); + } + + Id GetTextureImage(Operation operation) { + const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); + const auto entry = sampler_images.at(static_cast<u32>(meta->sampler.GetIndex())); + return Emit(OpImage(entry.image_type, GetTextureSampler(operation))); + } + + Id GetTextureCoordinates(Operation operation) { + const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); + std::vector<Id> coords; + for (std::size_t i = 0; i < operation.GetOperandsCount(); ++i) { + coords.push_back(Visit(operation[i])); + } + if (meta->sampler.IsArray()) { + const Id array_integer = BitcastTo<Type::Int>(Visit(meta->array)); + coords.push_back(Emit(OpConvertSToF(t_float, array_integer))); + } + if (meta->sampler.IsShadow()) { + coords.push_back(Visit(meta->depth_compare)); + } + + const std::array<Id, 4> t_float_lut = {nullptr, t_float2, t_float3, t_float4}; + return coords.size() == 1 + ? coords[0] + : Emit(OpCompositeConstruct(t_float_lut.at(coords.size() - 1), coords)); + } + + Id GetTextureElement(Operation operation, Id sample_value) { + const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); + ASSERT(meta); + return Emit(OpCompositeExtract(t_float, sample_value, meta->element)); + } + + Id Texture(Operation operation) { + const Id texture = Emit(OpImageSampleImplicitLod(t_float4, GetTextureSampler(operation), + GetTextureCoordinates(operation))); + return GetTextureElement(operation, texture); + } + + Id TextureLod(Operation operation) { + const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); + const Id texture = Emit(OpImageSampleExplicitLod( + t_float4, GetTextureSampler(operation), GetTextureCoordinates(operation), + spv::ImageOperandsMask::Lod, Visit(meta->lod))); + return GetTextureElement(operation, texture); + } + + Id TextureGather(Operation operation) { + const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); + const auto coords = GetTextureCoordinates(operation); + + Id texture; + if (meta->sampler.IsShadow()) { + texture = Emit(OpImageDrefGather(t_float4, GetTextureSampler(operation), coords, + Visit(meta->component))); + } else { + u32 component_value = 0; + if (meta->component) { + const auto component = std::get_if<ImmediateNode>(meta->component); + ASSERT_MSG(component, "Component is not an immediate value"); + component_value = component->GetValue(); + } + texture = Emit(OpImageGather(t_float4, GetTextureSampler(operation), coords, + Constant(t_uint, component_value))); + } + + return GetTextureElement(operation, texture); + } + + Id TextureQueryDimensions(Operation operation) { + const auto meta = std::get_if<MetaTexture>(&operation.GetMeta()); + const auto image_id = GetTextureImage(operation); + AddCapability(spv::Capability::ImageQuery); + + if (meta->element == 3) { + return BitcastTo<Type::Float>(Emit(OpImageQueryLevels(t_int, image_id))); + } + + const Id lod = VisitOperand<Type::Uint>(operation, 0); + const std::size_t coords_count = [&]() { + switch (const auto type = meta->sampler.GetType(); type) { + case Tegra::Shader::TextureType::Texture1D: + return 1; + case Tegra::Shader::TextureType::Texture2D: + case Tegra::Shader::TextureType::TextureCube: + return 2; + case Tegra::Shader::TextureType::Texture3D: + return 3; + default: + UNREACHABLE_MSG("Invalid texture type={}", static_cast<u32>(type)); + return 2; + } + }(); + + if (meta->element >= coords_count) { + return Constant(t_float, 0.0f); + } + + const std::array<Id, 3> types = {t_int, t_int2, t_int3}; + const Id sizes = Emit(OpImageQuerySizeLod(types.at(coords_count - 1), image_id, lod)); + const Id size = Emit(OpCompositeExtract(t_int, sizes, meta->element)); + return BitcastTo<Type::Float>(size); + } + + Id TextureQueryLod(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id TexelFetch(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id Branch(Operation operation) { + const auto target = std::get_if<ImmediateNode>(operation[0]); + UNIMPLEMENTED_IF(!target); + + Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue()))); + BranchingOp([&]() { Emit(OpBranch(continue_label)); }); + return {}; + } + + Id PushFlowStack(Operation operation) { + const auto target = std::get_if<ImmediateNode>(operation[0]); + ASSERT(target); + + const Id current = Emit(OpLoad(t_uint, flow_stack_top)); + const Id next = Emit(OpIAdd(t_uint, current, Constant(t_uint, 1))); + const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, current)); + + Emit(OpStore(access, Constant(t_uint, target->GetValue()))); + Emit(OpStore(flow_stack_top, next)); + return {}; + } + + Id PopFlowStack(Operation operation) { + const Id current = Emit(OpLoad(t_uint, flow_stack_top)); + const Id previous = Emit(OpISub(t_uint, current, Constant(t_uint, 1))); + const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, previous)); + const Id target = Emit(OpLoad(t_uint, access)); + + Emit(OpStore(flow_stack_top, previous)); + Emit(OpStore(jmp_to, target)); + BranchingOp([&]() { Emit(OpBranch(continue_label)); }); + return {}; + } + + Id Exit(Operation operation) { + switch (stage) { + case ShaderStage::Vertex: { + // TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't + // seem to be working on Nvidia's drivers and Intel (mesa and blob) doesn't support it. + const Id position = AccessElement(t_float4, per_vertex, position_index); + Id depth = Emit(OpLoad(t_float, AccessElement(t_out_float, position, 2))); + depth = Emit(OpFAdd(t_float, depth, Constant(t_float, 1.0f))); + depth = Emit(OpFMul(t_float, depth, Constant(t_float, 0.5f))); + Emit(OpStore(AccessElement(t_out_float, position, 2), depth)); + break; + } + case ShaderStage::Fragment: { + const auto SafeGetRegister = [&](u32 reg) { + // TODO(Rodrigo): Replace with contains once C++20 releases + if (const auto it = registers.find(reg); it != registers.end()) { + return Emit(OpLoad(t_float, it->second)); + } + return Constant(t_float, 0.0f); + }; + + UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, + "Sample mask write is unimplemented"); + + // TODO(Rodrigo): Alpha testing + + // Write the color outputs using the data in the shader registers, disabled + // rendertargets/components are skipped in the register assignment. + u32 current_reg = 0; + for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { + // TODO(Subv): Figure out how dual-source blending is configured in the Switch. + for (u32 component = 0; component < 4; ++component) { + if (header.ps.IsColorComponentOutputEnabled(rt, component)) { + Emit(OpStore(AccessElement(t_out_float, frag_colors.at(rt), component), + SafeGetRegister(current_reg))); + ++current_reg; + } + } + } + if (header.ps.omap.depth) { + // The depth output is always 2 registers after the last color output, and + // current_reg already contains one past the last color register. + Emit(OpStore(frag_depth, SafeGetRegister(current_reg + 1))); + } + break; + } + } + + BranchingOp([&]() { Emit(OpReturn()); }); + return {}; + } + + Id Discard(Operation operation) { + BranchingOp([&]() { Emit(OpKill()); }); + return {}; + } + + Id EmitVertex(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id EndPrimitive(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id YNegate(Operation operation) { + UNIMPLEMENTED(); + return {}; + } + + Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type, + const std::string& name) { + const Id id = OpVariable(type, storage); + Decorate(id, spv::Decoration::BuiltIn, static_cast<u32>(builtin)); + AddGlobalVariable(Name(id, name)); + interfaces.push_back(id); + return id; + } + + bool IsRenderTargetUsed(u32 rt) const { + for (u32 component = 0; component < 4; ++component) { + if (header.ps.IsColorComponentOutputEnabled(rt, component)) { + return true; + } + } + return false; + } + + template <typename... Args> + Id AccessElement(Id pointer_type, Id composite, Args... elements_) { + std::vector<Id> members; + auto elements = {elements_...}; + for (const auto element : elements) { + members.push_back(Constant(t_uint, element)); + } + + return Emit(OpAccessChain(pointer_type, composite, members)); + } + + template <Type type> + Id VisitOperand(Operation operation, std::size_t operand_index) { + const Id value = Visit(operation[operand_index]); + + switch (type) { + case Type::Bool: + case Type::Bool2: + case Type::Float: + return value; + case Type::Int: + return Emit(OpBitcast(t_int, value)); + case Type::Uint: + return Emit(OpBitcast(t_uint, value)); + case Type::HalfFloat: + UNIMPLEMENTED(); + } + UNREACHABLE(); + return value; + } + + template <Type type> + Id BitcastFrom(Id value) { + switch (type) { + case Type::Bool: + case Type::Bool2: + case Type::Float: + return value; + case Type::Int: + case Type::Uint: + return Emit(OpBitcast(t_float, value)); + case Type::HalfFloat: + UNIMPLEMENTED(); + } + UNREACHABLE(); + return value; + } + + template <Type type> + Id BitcastTo(Id value) { + switch (type) { + case Type::Bool: + case Type::Bool2: + UNREACHABLE(); + case Type::Float: + return Emit(OpBitcast(t_float, value)); + case Type::Int: + return Emit(OpBitcast(t_int, value)); + case Type::Uint: + return Emit(OpBitcast(t_uint, value)); + case Type::HalfFloat: + UNIMPLEMENTED(); + } + UNREACHABLE(); + return value; + } + + Id GetTypeDefinition(Type type) { + switch (type) { + case Type::Bool: + return t_bool; + case Type::Bool2: + return t_bool2; + case Type::Float: + return t_float; + case Type::Int: + return t_int; + case Type::Uint: + return t_uint; + case Type::HalfFloat: + UNIMPLEMENTED(); + } + UNREACHABLE(); + return {}; + } + + void BranchingOp(std::function<void()> call) { + const Id true_label = OpLabel(); + const Id skip_label = OpLabel(); + Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::Flatten)); + Emit(OpBranchConditional(v_true, true_label, skip_label, 1, 0)); + Emit(true_label); + call(); + + Emit(skip_label); + } + + static constexpr OperationDecompilersArray operation_decompilers = { + &SPIRVDecompiler::Assign, + + &SPIRVDecompiler::Ternary<&Module::OpSelect, Type::Float, Type::Bool, Type::Float, + Type::Float>, + + &SPIRVDecompiler::Binary<&Module::OpFAdd, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFMul, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFDiv, Type::Float>, + &SPIRVDecompiler::Ternary<&Module::OpFma, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpFNegate, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::Float>, + &SPIRVDecompiler::Ternary<&Module::OpFClamp, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFMin, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFMax, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpCos, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpSin, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpExp2, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpLog2, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpInverseSqrt, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpSqrt, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpRoundEven, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpFloor, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpCeil, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpTrunc, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpConvertSToF, Type::Float, Type::Int>, + &SPIRVDecompiler::Unary<&Module::OpConvertUToF, Type::Float, Type::Uint>, + + &SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpIMul, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpSDiv, Type::Int>, + &SPIRVDecompiler::Unary<&Module::OpSNegate, Type::Int>, + &SPIRVDecompiler::Unary<&Module::OpSAbs, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpSMin, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpSMax, Type::Int>, + + &SPIRVDecompiler::Unary<&Module::OpConvertFToS, Type::Int, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Int, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Int, Type::Int, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Int, Type::Int, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpShiftRightArithmetic, Type::Int, Type::Int, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Int>, + &SPIRVDecompiler::Unary<&Module::OpNot, Type::Int>, + &SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Int>, + &SPIRVDecompiler::Ternary<&Module::OpBitFieldSExtract, Type::Int>, + &SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Int>, + + &SPIRVDecompiler::Binary<&Module::OpIAdd, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpIMul, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpUDiv, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpUMin, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpUMax, Type::Uint>, + &SPIRVDecompiler::Unary<&Module::OpConvertFToU, Type::Uint, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpBitcast, Type::Uint, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpShiftLeftLogical, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpShiftRightLogical, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpShiftRightArithmetic, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpBitwiseAnd, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpBitwiseOr, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpBitwiseXor, Type::Uint>, + &SPIRVDecompiler::Unary<&Module::OpNot, Type::Uint>, + &SPIRVDecompiler::Quaternary<&Module::OpBitFieldInsert, Type::Uint>, + &SPIRVDecompiler::Ternary<&Module::OpBitFieldUExtract, Type::Uint>, + &SPIRVDecompiler::Unary<&Module::OpBitCount, Type::Uint>, + + &SPIRVDecompiler::Binary<&Module::OpFAdd, Type::HalfFloat>, + &SPIRVDecompiler::Binary<&Module::OpFMul, Type::HalfFloat>, + &SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>, + &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>, + &SPIRVDecompiler::HNegate, + &SPIRVDecompiler::HMergeF32, + &SPIRVDecompiler::HMergeH0, + &SPIRVDecompiler::HMergeH1, + &SPIRVDecompiler::HPack2, + + &SPIRVDecompiler::LogicalAssign, + &SPIRVDecompiler::Binary<&Module::OpLogicalAnd, Type::Bool>, + &SPIRVDecompiler::Binary<&Module::OpLogicalOr, Type::Bool>, + &SPIRVDecompiler::Binary<&Module::OpLogicalNotEqual, Type::Bool>, + &SPIRVDecompiler::Unary<&Module::OpLogicalNot, Type::Bool>, + &SPIRVDecompiler::LogicalPick2, + &SPIRVDecompiler::LogicalAll2, + &SPIRVDecompiler::LogicalAny2, + + &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::Float>, + &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::Float>, + &SPIRVDecompiler::Unary<&Module::OpIsNan, Type::Bool>, + + &SPIRVDecompiler::Binary<&Module::OpSLessThan, Type::Bool, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpSLessThanEqual, Type::Bool, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpSGreaterThan, Type::Bool, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Int>, + &SPIRVDecompiler::Binary<&Module::OpSGreaterThanEqual, Type::Bool, Type::Int>, + + &SPIRVDecompiler::Binary<&Module::OpULessThan, Type::Bool, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpIEqual, Type::Bool, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpULessThanEqual, Type::Bool, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpUGreaterThan, Type::Bool, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Uint>, + &SPIRVDecompiler::Binary<&Module::OpUGreaterThanEqual, Type::Bool, Type::Uint>, + + &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::HalfFloat>, + &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::HalfFloat>, + &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::HalfFloat>, + &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>, + &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>, + &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>, + + &SPIRVDecompiler::Texture, + &SPIRVDecompiler::TextureLod, + &SPIRVDecompiler::TextureGather, + &SPIRVDecompiler::TextureQueryDimensions, + &SPIRVDecompiler::TextureQueryLod, + &SPIRVDecompiler::TexelFetch, + + &SPIRVDecompiler::Branch, + &SPIRVDecompiler::PushFlowStack, + &SPIRVDecompiler::PopFlowStack, + &SPIRVDecompiler::Exit, + &SPIRVDecompiler::Discard, + + &SPIRVDecompiler::EmitVertex, + &SPIRVDecompiler::EndPrimitive, + + &SPIRVDecompiler::YNegate, + }; + + const ShaderIR& ir; + const ShaderStage stage; + const Tegra::Shader::Header header; + + const Id t_void = Name(TypeVoid(), "void"); + + const Id t_bool = Name(TypeBool(), "bool"); + const Id t_bool2 = Name(TypeVector(t_bool, 2), "bool2"); + + const Id t_int = Name(TypeInt(32, true), "int"); + const Id t_int2 = Name(TypeVector(t_int, 2), "int2"); + const Id t_int3 = Name(TypeVector(t_int, 3), "int3"); + const Id t_int4 = Name(TypeVector(t_int, 4), "int4"); + + const Id t_uint = Name(TypeInt(32, false), "uint"); + const Id t_uint2 = Name(TypeVector(t_uint, 2), "uint2"); + const Id t_uint3 = Name(TypeVector(t_uint, 3), "uint3"); + const Id t_uint4 = Name(TypeVector(t_uint, 4), "uint4"); + + const Id t_float = Name(TypeFloat(32), "float"); + const Id t_float2 = Name(TypeVector(t_float, 2), "float2"); + const Id t_float3 = Name(TypeVector(t_float, 3), "float3"); + const Id t_float4 = Name(TypeVector(t_float, 4), "float4"); + + const Id t_prv_bool = Name(TypePointer(spv::StorageClass::Private, t_bool), "prv_bool"); + const Id t_prv_float = Name(TypePointer(spv::StorageClass::Private, t_float), "prv_float"); + + const Id t_func_uint = Name(TypePointer(spv::StorageClass::Function, t_uint), "func_uint"); + + const Id t_in_bool = Name(TypePointer(spv::StorageClass::Input, t_bool), "in_bool"); + const Id t_in_uint = Name(TypePointer(spv::StorageClass::Input, t_uint), "in_uint"); + const Id t_in_float = Name(TypePointer(spv::StorageClass::Input, t_float), "in_float"); + const Id t_in_float4 = Name(TypePointer(spv::StorageClass::Input, t_float4), "in_float4"); + + const Id t_out_float = Name(TypePointer(spv::StorageClass::Output, t_float), "out_float"); + const Id t_out_float4 = Name(TypePointer(spv::StorageClass::Output, t_float4), "out_float4"); + + const Id t_cbuf_float = TypePointer(spv::StorageClass::Uniform, t_float); + const Id t_cbuf_array = + Decorate(Name(TypeArray(t_float4, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS)), "CbufArray"), + spv::Decoration::ArrayStride, CBUF_STRIDE); + const Id t_cbuf_struct = MemberDecorate( + Decorate(TypeStruct(t_cbuf_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); + const Id t_cbuf_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_struct); + + const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float); + const Id t_gmem_array = + Name(Decorate(TypeRuntimeArray(t_float), spv::Decoration::ArrayStride, 4u), "GmemArray"); + const Id t_gmem_struct = MemberDecorate( + Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); + const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); + + const Id v_float_zero = Constant(t_float, 0.0f); + const Id v_true = ConstantTrue(t_bool); + const Id v_false = ConstantFalse(t_bool); + + Id per_vertex{}; + std::map<u32, Id> registers; + std::map<Tegra::Shader::Pred, Id> predicates; + Id local_memory{}; + std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{}; + std::map<Attribute::Index, Id> input_attributes; + std::map<Attribute::Index, Id> output_attributes; + std::map<u32, Id> constant_buffers; + std::map<GlobalMemoryBase, Id> global_buffers; + std::map<u32, SamplerImage> sampler_images; + + Id instance_index{}; + Id vertex_index{}; + std::array<Id, Maxwell::NumRenderTargets> frag_colors{}; + Id frag_depth{}; + Id frag_coord{}; + Id front_facing{}; + + u32 position_index{}; + u32 point_size_index{}; + u32 clip_distances_index{}; + + std::vector<Id> interfaces; + + u32 const_buffers_base_binding{}; + u32 global_buffers_base_binding{}; + u32 samplers_base_binding{}; + + Id execute_function{}; + Id jmp_to{}; + Id flow_stack_top{}; + Id flow_stack{}; + Id continue_label{}; + std::map<u32, Id> labels; +}; + +DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage) { + auto decompiler = std::make_unique<SPIRVDecompiler>(ir, stage); + decompiler->Decompile(); + return {std::move(decompiler), decompiler->GetShaderEntries()}; +} + +} // namespace Vulkan::VKShader diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h new file mode 100644 index 000000000..329d8fa38 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h @@ -0,0 +1,80 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <memory> +#include <set> +#include <utility> +#include <vector> + +#include <sirit/sirit.h> + +#include "common/common_types.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/shader/shader_ir.h" + +namespace VideoCommon::Shader { +class ShaderIR; +} + +namespace Vulkan::VKShader { + +using Maxwell = Tegra::Engines::Maxwell3D::Regs; + +using SamplerEntry = VideoCommon::Shader::Sampler; + +constexpr u32 DESCRIPTOR_SET = 0; + +class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer { +public: + explicit constexpr ConstBufferEntry(const VideoCommon::Shader::ConstBuffer& entry, u32 index) + : VideoCommon::Shader::ConstBuffer{entry}, index{index} {} + + constexpr u32 GetIndex() const { + return index; + } + +private: + u32 index{}; +}; + +class GlobalBufferEntry { +public: + explicit GlobalBufferEntry(u32 cbuf_index, u32 cbuf_offset) + : cbuf_index{cbuf_index}, cbuf_offset{cbuf_offset} {} + + u32 GetCbufIndex() const { + return cbuf_index; + } + + u32 GetCbufOffset() const { + return cbuf_offset; + } + +private: + u32 cbuf_index{}; + u32 cbuf_offset{}; +}; + +struct ShaderEntries { + u32 const_buffers_base_binding{}; + u32 global_buffers_base_binding{}; + u32 samplers_base_binding{}; + std::vector<ConstBufferEntry> const_buffers; + std::vector<GlobalBufferEntry> global_buffers; + std::vector<SamplerEntry> samplers; + std::set<u32> attributes; + std::array<bool, Maxwell::NumClipDistances> clip_distances{}; + std::size_t shader_length{}; + Sirit::Id entry_function{}; + std::vector<Sirit::Id> interfaces; +}; + +using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>; + +DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage); + +} // namespace Vulkan::VKShader diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index ea3c71eed..ff19ada55 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp @@ -18,6 +18,23 @@ using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; using Tegra::Shader::Register; +namespace { +u32 GetUniformTypeElementsCount(Tegra::Shader::UniformType uniform_type) { + switch (uniform_type) { + case Tegra::Shader::UniformType::Single: + return 1; + case Tegra::Shader::UniformType::Double: + return 2; + case Tegra::Shader::UniformType::Quad: + case Tegra::Shader::UniformType::UnsignedQuad: + return 4; + default: + UNIMPLEMENTED_MSG("Unimplemented size={}!", static_cast<u32>(uniform_type)); + return 1; + } +} +} // namespace + u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { const Instruction instr = {program_code[pc]}; const auto opcode = OpCode::Decode(instr); @@ -126,45 +143,15 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { break; } case OpCode::Id::LDG: { - const u32 count = [&]() { - switch (instr.ldg.type) { - case Tegra::Shader::UniformType::Single: - return 1; - case Tegra::Shader::UniformType::Double: - return 2; - case Tegra::Shader::UniformType::Quad: - case Tegra::Shader::UniformType::UnsignedQuad: - return 4; - default: - UNIMPLEMENTED_MSG("Unimplemented LDG size!"); - return 1; - } - }(); - - const Node addr_register = GetRegister(instr.gpr8); - const Node base_address = - TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size())); - const auto cbuf = std::get_if<CbufNode>(base_address); - ASSERT(cbuf != nullptr); - const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset()); - ASSERT(cbuf_offset_imm != nullptr); - const auto cbuf_offset = cbuf_offset_imm->GetValue(); - - bb.push_back(Comment( - fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset))); - - const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset}; - used_global_memory_bases.insert(descriptor); - - const Node immediate_offset = - Immediate(static_cast<u32>(instr.ldg.immediate_offset.Value())); - const Node base_real_address = - Operation(OperationCode::UAdd, NO_PRECISE, immediate_offset, addr_register); + const auto [real_address_base, base_address, descriptor] = + TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8), + static_cast<u32>(instr.ldg.immediate_offset.Value()), false); + const u32 count = GetUniformTypeElementsCount(instr.ldg.type); for (u32 i = 0; i < count; ++i) { const Node it_offset = Immediate(i * 4); const Node real_address = - Operation(OperationCode::UAdd, NO_PRECISE, base_real_address, it_offset); + Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset); const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); SetTemporal(bb, i, gmem); @@ -174,6 +161,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { } break; } + case OpCode::Id::STG: { + const auto [real_address_base, base_address, descriptor] = + TrackAndGetGlobalMemory(bb, GetRegister(instr.gpr8), + static_cast<u32>(instr.stg.immediate_offset.Value()), true); + + // Encode in temporary registers like this: real_base_address, {registers_to_be_written...} + SetTemporal(bb, 0, real_address_base); + + const u32 count = GetUniformTypeElementsCount(instr.stg.type); + for (u32 i = 0; i < count; ++i) { + SetTemporal(bb, i + 1, GetRegister(instr.gpr0.Value() + i)); + } + for (u32 i = 0; i < count; ++i) { + const Node it_offset = Immediate(i * 4); + const Node real_address = + Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset); + const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor)); + + bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1))); + } + break; + } case OpCode::Id::ST_A: { UNIMPLEMENTED_IF_MSG(instr.gpr8.Value() != Register::ZeroIndex, "Indirect attribute loads are not supported"); @@ -236,4 +245,34 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { return pc; } +std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb, + Node addr_register, + u32 immediate_offset, + bool is_write) { + const Node base_address{ + TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))}; + const auto cbuf = std::get_if<CbufNode>(base_address); + ASSERT(cbuf != nullptr); + const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset()); + ASSERT(cbuf_offset_imm != nullptr); + const auto cbuf_offset = cbuf_offset_imm->GetValue(); + + bb.push_back( + Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", cbuf->GetIndex(), cbuf_offset))); + + const GlobalMemoryBase descriptor{cbuf->GetIndex(), cbuf_offset}; + const auto& [entry, is_new] = used_global_memory.try_emplace(descriptor); + auto& usage = entry->second; + if (is_write) { + usage.is_written = true; + } else { + usage.is_read = true; + } + + const auto real_address = + Operation(OperationCode::UAdd, NO_PRECISE, Immediate(immediate_offset), addr_register); + + return {real_address, base_address, descriptor}; +} + } // namespace VideoCommon::Shader diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 4888998d3..1afab08c0 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -276,6 +276,11 @@ struct GlobalMemoryBase { } }; +struct GlobalMemoryUsage { + bool is_read{}; + bool is_written{}; +}; + struct MetaArithmetic { bool precise{}; }; @@ -578,8 +583,8 @@ public: return used_clip_distances; } - const std::set<GlobalMemoryBase>& GetGlobalMemoryBases() const { - return used_global_memory_bases; + const std::map<GlobalMemoryBase, GlobalMemoryUsage>& GetGlobalMemory() const { + return used_global_memory; } std::size_t GetLength() const { @@ -781,6 +786,11 @@ private: std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, s64 cursor); + std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(NodeBlock& bb, + Node addr_register, + u32 immediate_offset, + bool is_write); + template <typename... T> Node Operation(OperationCode code, const T*... operands) { return StoreNode(OperationNode(code, operands...)); @@ -834,7 +844,7 @@ private: std::map<u32, ConstBuffer> used_cbufs; std::set<Sampler> used_samplers; std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; - std::set<GlobalMemoryBase> used_global_memory_bases; + std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory; Tegra::Shader::Header header; }; 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, return PixelFormat::Z16; case Tegra::Texture::TextureFormat::Z24S8: return PixelFormat::Z24S8; + case Tegra::Texture::TextureFormat::ZF32_X24S8: + return PixelFormat::Z32FS8; case Tegra::Texture::TextureFormat::DXT1: return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1; case Tegra::Texture::TextureFormat::DXT23: diff --git a/src/video_core/texture_cache.cpp b/src/video_core/texture_cache.cpp new file mode 100644 index 000000000..e96eba7cc --- /dev/null +++ b/src/video_core/texture_cache.cpp @@ -0,0 +1,386 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/cityhash.h" +#include "common/common_types.h" +#include "core/core.h" +#include "video_core/surface.h" +#include "video_core/texture_cache.h" +#include "video_core/textures/decoders.h" +#include "video_core/textures/texture.h" + +namespace VideoCommon { + +using VideoCore::Surface::SurfaceTarget; + +using VideoCore::Surface::ComponentTypeFromDepthFormat; +using VideoCore::Surface::ComponentTypeFromRenderTarget; +using VideoCore::Surface::ComponentTypeFromTexture; +using VideoCore::Surface::PixelFormatFromDepthFormat; +using VideoCore::Surface::PixelFormatFromRenderTargetFormat; +using VideoCore::Surface::PixelFormatFromTextureFormat; +using VideoCore::Surface::SurfaceTargetFromTextureType; + +constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) { + return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile); +} + +SurfaceParams SurfaceParams::CreateForTexture(Core::System& system, + const Tegra::Texture::FullTextureInfo& config) { + SurfaceParams params; + params.is_tiled = config.tic.IsTiled(); + params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, + params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, + params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, + params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1; + params.pixel_format = + PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), false); + params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); + params.type = GetFormatType(params.pixel_format); + params.target = SurfaceTargetFromTextureType(config.tic.texture_type); + params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); + params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); + params.depth = config.tic.Depth(); + if (params.target == SurfaceTarget::TextureCubemap || + params.target == SurfaceTarget::TextureCubeArray) { + params.depth *= 6; + } + params.pitch = params.is_tiled ? 0 : config.tic.Pitch(); + params.unaligned_height = config.tic.Height(); + params.num_levels = config.tic.max_mip_level + 1; + + params.CalculateCachedValues(); + return params; +} + +SurfaceParams SurfaceParams::CreateForDepthBuffer( + Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, + u32 block_width, u32 block_height, u32 block_depth, + Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) { + SurfaceParams params; + params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; + params.block_width = 1 << std::min(block_width, 5U); + params.block_height = 1 << std::min(block_height, 5U); + params.block_depth = 1 << std::min(block_depth, 5U); + params.tile_width_spacing = 1; + params.pixel_format = PixelFormatFromDepthFormat(format); + params.component_type = ComponentTypeFromDepthFormat(format); + params.type = GetFormatType(params.pixel_format); + params.width = zeta_width; + params.height = zeta_height; + params.unaligned_height = zeta_height; + params.target = SurfaceTarget::Texture2D; + params.depth = 1; + params.num_levels = 1; + + params.CalculateCachedValues(); + return params; +} + +SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) { + const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; + SurfaceParams params; + params.is_tiled = + config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; + params.block_width = 1 << config.memory_layout.block_width; + params.block_height = 1 << config.memory_layout.block_height; + params.block_depth = 1 << config.memory_layout.block_depth; + params.tile_width_spacing = 1; + params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); + params.component_type = ComponentTypeFromRenderTarget(config.format); + params.type = GetFormatType(params.pixel_format); + if (params.is_tiled) { + params.width = config.width; + } else { + const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT; + params.pitch = config.width; + params.width = params.pitch / bpp; + } + params.height = config.height; + params.depth = 1; + params.unaligned_height = config.height; + params.target = SurfaceTarget::Texture2D; + params.num_levels = 1; + + params.CalculateCachedValues(); + return params; +} + +SurfaceParams SurfaceParams::CreateForFermiCopySurface( + const Tegra::Engines::Fermi2D::Regs::Surface& config) { + SurfaceParams params{}; + params.is_tiled = !config.linear; + params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0, + params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, + params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0, + params.tile_width_spacing = 1; + params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); + params.component_type = ComponentTypeFromRenderTarget(config.format); + params.type = GetFormatType(params.pixel_format); + params.width = config.width; + params.height = config.height; + params.unaligned_height = config.height; + // TODO(Rodrigo): Try to guess the surface target from depth and layer parameters + params.target = SurfaceTarget::Texture2D; + params.depth = 1; + params.num_levels = 1; + + params.CalculateCachedValues(); + return params; +} + +u32 SurfaceParams::GetMipWidth(u32 level) const { + return std::max(1U, width >> level); +} + +u32 SurfaceParams::GetMipHeight(u32 level) const { + return std::max(1U, height >> level); +} + +u32 SurfaceParams::GetMipDepth(u32 level) const { + return IsLayered() ? depth : std::max(1U, depth >> level); +} + +bool SurfaceParams::IsLayered() const { + switch (target) { + case SurfaceTarget::Texture1DArray: + case SurfaceTarget::Texture2DArray: + case SurfaceTarget::TextureCubeArray: + case SurfaceTarget::TextureCubemap: + return true; + default: + return false; + } +} + +u32 SurfaceParams::GetMipBlockHeight(u32 level) const { + // Auto block resizing algorithm from: + // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c + if (level == 0) { + return block_height; + } + const u32 height{GetMipHeight(level)}; + const u32 default_block_height{GetDefaultBlockHeight(pixel_format)}; + const u32 blocks_in_y{(height + default_block_height - 1) / default_block_height}; + u32 block_height = 16; + while (block_height > 1 && blocks_in_y <= block_height * 4) { + block_height >>= 1; + } + return block_height; +} + +u32 SurfaceParams::GetMipBlockDepth(u32 level) const { + if (level == 0) + return block_depth; + if (target != SurfaceTarget::Texture3D) + return 1; + + const u32 depth{GetMipDepth(level)}; + u32 block_depth = 32; + while (block_depth > 1 && depth * 2 <= block_depth) { + block_depth >>= 1; + } + if (block_depth == 32 && GetMipBlockHeight(level) >= 4) { + return 16; + } + return block_depth; +} + +std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const { + std::size_t offset = 0; + for (u32 i = 0; i < level; i++) { + offset += GetInnerMipmapMemorySize(i, false, IsLayered(), false); + } + return offset; +} + +std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const { + std::size_t offset = 0; + for (u32 i = 0; i < level; i++) { + offset += GetInnerMipmapMemorySize(i, true, false, false); + } + return offset; +} + +std::size_t SurfaceParams::GetGuestLayerSize() const { + return GetInnerMemorySize(false, true, false); +} + +std::size_t SurfaceParams::GetHostLayerSize(u32 level) const { + return GetInnerMipmapMemorySize(level, true, IsLayered(), false); +} + +bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const { + if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) != + std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format, + view_params.component_type, view_params.type)) { + return false; + } + + const SurfaceTarget view_target{view_params.target}; + if (view_target == target) { + return true; + } + + switch (target) { + case SurfaceTarget::Texture1D: + case SurfaceTarget::Texture2D: + case SurfaceTarget::Texture3D: + return false; + case SurfaceTarget::Texture1DArray: + return view_target == SurfaceTarget::Texture1D; + case SurfaceTarget::Texture2DArray: + return view_target == SurfaceTarget::Texture2D; + case SurfaceTarget::TextureCubemap: + return view_target == SurfaceTarget::Texture2D || + view_target == SurfaceTarget::Texture2DArray; + case SurfaceTarget::TextureCubeArray: + return view_target == SurfaceTarget::Texture2D || + view_target == SurfaceTarget::Texture2DArray || + view_target == SurfaceTarget::TextureCubemap; + default: + UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target)); + return false; + } +} + +bool SurfaceParams::IsPixelFormatZeta() const { + return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat && + pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat; +} + +void SurfaceParams::CalculateCachedValues() { + guest_size_in_bytes = GetInnerMemorySize(false, false, false); + + // ASTC is uncompressed in software, in emulated as RGBA8 + if (IsPixelFormatASTC(pixel_format)) { + host_size_in_bytes = width * height * depth * 4; + } else { + host_size_in_bytes = GetInnerMemorySize(true, false, false); + } + + switch (target) { + case SurfaceTarget::Texture1D: + case SurfaceTarget::Texture2D: + case SurfaceTarget::Texture3D: + num_layers = 1; + break; + case SurfaceTarget::Texture1DArray: + case SurfaceTarget::Texture2DArray: + case SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubeArray: + num_layers = depth; + break; + default: + UNREACHABLE(); + } +} + +std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only, + bool uncompressed) const { + const bool tiled{as_host_size ? false : is_tiled}; + const u32 tile_x{GetDefaultBlockWidth(pixel_format)}; + const u32 tile_y{GetDefaultBlockHeight(pixel_format)}; + const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), tile_x)}; + const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), tile_y)}; + const u32 depth{layer_only ? 1U : GetMipDepth(level)}; + return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(pixel_format), width, height, + depth, GetMipBlockHeight(level), GetMipBlockDepth(level)); +} + +std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only, + bool uncompressed) const { + std::size_t size = 0; + for (u32 level = 0; level < num_levels; ++level) { + size += GetInnerMipmapMemorySize(level, as_host_size, layer_only, uncompressed); + } + if (!as_host_size && is_tiled) { + size = Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth); + } + return size; +} + +std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const { + std::map<u64, std::pair<u32, u32>> view_offset_map; + switch (target) { + case SurfaceTarget::Texture1D: + case SurfaceTarget::Texture2D: + case SurfaceTarget::Texture3D: { + constexpr u32 layer = 0; + for (u32 level = 0; level < num_levels; ++level) { + const std::size_t offset{GetGuestMipmapLevelOffset(level)}; + view_offset_map.insert({offset, {layer, level}}); + } + break; + } + case SurfaceTarget::Texture1DArray: + case SurfaceTarget::Texture2DArray: + case SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubeArray: { + const std::size_t layer_size{GetGuestLayerSize()}; + for (u32 level = 0; level < num_levels; ++level) { + const std::size_t level_offset{GetGuestMipmapLevelOffset(level)}; + for (u32 layer = 0; layer < num_layers; ++layer) { + const auto layer_offset{static_cast<std::size_t>(layer_size * layer)}; + const std::size_t offset{level_offset + layer_offset}; + view_offset_map.insert({offset, {layer, level}}); + } + } + break; + } + default: + UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target)); + } + return view_offset_map; +} + +bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const { + return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) && + IsInBounds(view_params, layer, level); +} + +bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const { + return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level); +} + +bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const { + if (view_params.target != SurfaceTarget::Texture3D) { + return true; + } + return view_params.depth == GetMipDepth(level); +} + +bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const { + return layer + view_params.num_layers <= num_layers && + level + view_params.num_levels <= num_levels; +} + +std::size_t HasheableSurfaceParams::Hash() const { + return static_cast<std::size_t>( + Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this))); +} + +bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const { + return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width, + height, depth, pitch, unaligned_height, num_levels, pixel_format, + component_type, type, target) == + std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth, + rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch, + rhs.unaligned_height, rhs.num_levels, rhs.pixel_format, rhs.component_type, + rhs.type, rhs.target); +} + +std::size_t ViewKey::Hash() const { + return static_cast<std::size_t>( + Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this))); +} + +bool ViewKey::operator==(const ViewKey& rhs) const { + return std::tie(base_layer, num_layers, base_level, num_levels) == + std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels); +} + +} // namespace VideoCommon diff --git a/src/video_core/texture_cache.h b/src/video_core/texture_cache.h new file mode 100644 index 000000000..041551691 --- /dev/null +++ b/src/video_core/texture_cache.h @@ -0,0 +1,586 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <list> +#include <memory> +#include <set> +#include <tuple> +#include <type_traits> +#include <unordered_map> + +#include <boost/icl/interval_map.hpp> +#include <boost/range/iterator_range.hpp> + +#include "common/assert.h" +#include "common/common_types.h" +#include "core/memory.h" +#include "video_core/engines/fermi_2d.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/gpu.h" +#include "video_core/rasterizer_interface.h" +#include "video_core/surface.h" + +namespace Core { +class System; +} + +namespace Tegra::Texture { +struct FullTextureInfo; +} + +namespace VideoCore { +class RasterizerInterface; +} + +namespace VideoCommon { + +class HasheableSurfaceParams { +public: + std::size_t Hash() const; + + bool operator==(const HasheableSurfaceParams& rhs) const; + +protected: + // Avoid creation outside of a managed environment. + HasheableSurfaceParams() = default; + + bool is_tiled; + u32 block_width; + u32 block_height; + u32 block_depth; + u32 tile_width_spacing; + u32 width; + u32 height; + u32 depth; + u32 pitch; + u32 unaligned_height; + u32 num_levels; + VideoCore::Surface::PixelFormat pixel_format; + VideoCore::Surface::ComponentType component_type; + VideoCore::Surface::SurfaceType type; + VideoCore::Surface::SurfaceTarget target; +}; + +class SurfaceParams final : public HasheableSurfaceParams { +public: + /// Creates SurfaceCachedParams from a texture configuration. + static SurfaceParams CreateForTexture(Core::System& system, + const Tegra::Texture::FullTextureInfo& config); + + /// Creates SurfaceCachedParams for a depth buffer configuration. + static SurfaceParams CreateForDepthBuffer( + Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, + u32 block_width, u32 block_height, u32 block_depth, + Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type); + + /// Creates SurfaceCachedParams from a framebuffer configuration. + static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index); + + /// Creates SurfaceCachedParams from a Fermi2D surface configuration. + static SurfaceParams CreateForFermiCopySurface( + const Tegra::Engines::Fermi2D::Regs::Surface& config); + + bool IsTiled() const { + return is_tiled; + } + + u32 GetBlockWidth() const { + return block_width; + } + + u32 GetTileWidthSpacing() const { + return tile_width_spacing; + } + + u32 GetWidth() const { + return width; + } + + u32 GetHeight() const { + return height; + } + + u32 GetDepth() const { + return depth; + } + + u32 GetPitch() const { + return pitch; + } + + u32 GetNumLevels() const { + return num_levels; + } + + VideoCore::Surface::PixelFormat GetPixelFormat() const { + return pixel_format; + } + + VideoCore::Surface::ComponentType GetComponentType() const { + return component_type; + } + + VideoCore::Surface::SurfaceTarget GetTarget() const { + return target; + } + + VideoCore::Surface::SurfaceType GetType() const { + return type; + } + + std::size_t GetGuestSizeInBytes() const { + return guest_size_in_bytes; + } + + std::size_t GetHostSizeInBytes() const { + return host_size_in_bytes; + } + + u32 GetNumLayers() const { + return num_layers; + } + + /// Returns the width of a given mipmap level. + u32 GetMipWidth(u32 level) const; + + /// Returns the height of a given mipmap level. + u32 GetMipHeight(u32 level) const; + + /// Returns the depth of a given mipmap level. + u32 GetMipDepth(u32 level) const; + + /// Returns true if these parameters are from a layered surface. + bool IsLayered() const; + + /// Returns the block height of a given mipmap level. + u32 GetMipBlockHeight(u32 level) const; + + /// Returns the block depth of a given mipmap level. + u32 GetMipBlockDepth(u32 level) const; + + /// Returns the offset in bytes in guest memory of a given mipmap level. + std::size_t GetGuestMipmapLevelOffset(u32 level) const; + + /// Returns the offset in bytes in host memory (linear) of a given mipmap level. + std::size_t GetHostMipmapLevelOffset(u32 level) const; + + /// Returns the size of a layer in bytes in guest memory. + std::size_t GetGuestLayerSize() const; + + /// Returns the size of a layer in bytes in host memory for a given mipmap level. + std::size_t GetHostLayerSize(u32 level) const; + + /// Returns true if another surface can be familiar with this. This is a loosely defined term + /// that reflects the possibility of these two surface parameters potentially being part of a + /// bigger superset. + bool IsFamiliar(const SurfaceParams& view_params) const; + + /// Returns true if the pixel format is a depth and/or stencil format. + bool IsPixelFormatZeta() const; + + /// Creates a map that redirects an address difference to a layer and mipmap level. + std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const; + + /// Returns true if the passed surface view parameters is equal or a valid subset of this. + bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const; + +private: + /// Calculates values that can be deduced from HasheableSurfaceParams. + void CalculateCachedValues(); + + /// Returns the size of a given mipmap level. + std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only, + bool uncompressed) const; + + /// Returns the size of all mipmap levels and aligns as needed. + std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const; + + /// Returns true if the passed view width and height match the size of this params in a given + /// mipmap level. + bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const; + + /// Returns true if the passed view depth match the size of this params in a given mipmap level. + bool IsDepthValid(const SurfaceParams& view_params, u32 level) const; + + /// Returns true if the passed view layers and mipmap levels are in bounds. + bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const; + + std::size_t guest_size_in_bytes; + std::size_t host_size_in_bytes; + u32 num_layers; +}; + +struct ViewKey { + std::size_t Hash() const; + + bool operator==(const ViewKey& rhs) const; + + u32 base_layer{}; + u32 num_layers{}; + u32 base_level{}; + u32 num_levels{}; +}; + +} // namespace VideoCommon + +namespace std { + +template <> +struct hash<VideoCommon::SurfaceParams> { + std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept { + return k.Hash(); + } +}; + +template <> +struct hash<VideoCommon::ViewKey> { + std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept { + return k.Hash(); + } +}; + +} // namespace std + +namespace VideoCommon { + +template <typename TView, typename TExecutionContext> +class SurfaceBase { + static_assert(std::is_trivially_copyable_v<TExecutionContext>); + +public: + virtual void LoadBuffer() = 0; + + virtual TExecutionContext FlushBuffer(TExecutionContext exctx) = 0; + + virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0; + + TView* TryGetView(VAddr view_addr, const SurfaceParams& view_params) { + if (view_addr < cpu_addr || !params.IsFamiliar(view_params)) { + // It can't be a view if it's in a prior address. + return {}; + } + + const auto relative_offset{static_cast<u64>(view_addr - cpu_addr)}; + const auto it{view_offset_map.find(relative_offset)}; + if (it == view_offset_map.end()) { + // Couldn't find an aligned view. + return {}; + } + const auto [layer, level] = it->second; + + if (!params.IsViewValid(view_params, layer, level)) { + return {}; + } + + return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels()); + } + + VAddr GetCpuAddr() const { + ASSERT(is_registered); + return cpu_addr; + } + + u8* GetHostPtr() const { + ASSERT(is_registered); + return host_ptr; + } + + CacheAddr GetCacheAddr() const { + ASSERT(is_registered); + return cache_addr; + } + + std::size_t GetSizeInBytes() const { + return params.GetGuestSizeInBytes(); + } + + void MarkAsModified(bool is_modified_) { + is_modified = is_modified_; + } + + const SurfaceParams& GetSurfaceParams() const { + return params; + } + + TView* GetView(VAddr view_addr, const SurfaceParams& view_params) { + TView* view{TryGetView(view_addr, view_params)}; + ASSERT(view != nullptr); + return view; + } + + void Register(VAddr cpu_addr_, u8* host_ptr_) { + ASSERT(!is_registered); + is_registered = true; + cpu_addr = cpu_addr_; + host_ptr = host_ptr_; + cache_addr = ToCacheAddr(host_ptr_); + } + + void Register(VAddr cpu_addr_) { + Register(cpu_addr_, Memory::GetPointer(cpu_addr_)); + } + + void Unregister() { + ASSERT(is_registered); + is_registered = false; + } + + bool IsRegistered() const { + return is_registered; + } + +protected: + explicit SurfaceBase(const SurfaceParams& params) + : params{params}, view_offset_map{params.CreateViewOffsetMap()} {} + + ~SurfaceBase() = default; + + virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0; + + bool IsModified() const { + return is_modified; + } + + const SurfaceParams params; + +private: + TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) { + const ViewKey key{base_layer, num_layers, base_level, num_levels}; + const auto [entry, is_cache_miss] = views.try_emplace(key); + auto& view{entry->second}; + if (is_cache_miss) { + view = CreateView(key); + } + return view.get(); + } + + const std::map<u64, std::pair<u32, u32>> view_offset_map; + + VAddr cpu_addr{}; + u8* host_ptr{}; + CacheAddr cache_addr{}; + bool is_modified{}; + bool is_registered{}; + std::unordered_map<ViewKey, std::unique_ptr<TView>> views; +}; + +template <typename TSurface, typename TView, typename TExecutionContext> +class TextureCache { + static_assert(std::is_trivially_copyable_v<TExecutionContext>); + using ResultType = std::tuple<TView*, TExecutionContext>; + using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface*>>; + using IntervalType = typename IntervalMap::interval_type; + +public: + void InvalidateRegion(CacheAddr addr, std::size_t size) { + for (TSurface* surface : GetSurfacesInRegion(addr, size)) { + if (!surface->IsRegistered()) { + // Skip duplicates + continue; + } + Unregister(surface); + } + } + + ResultType GetTextureSurface(TExecutionContext exctx, + const Tegra::Texture::FullTextureInfo& config) { + auto& memory_manager{system.GPU().MemoryManager()}; + const auto cpu_addr{memory_manager.GpuToCpuAddress(config.tic.Address())}; + if (!cpu_addr) { + return {{}, exctx}; + } + const auto params{SurfaceParams::CreateForTexture(system, config)}; + return GetSurfaceView(exctx, *cpu_addr, params, true); + } + + ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) { + const auto& regs{system.GPU().Maxwell3D().regs}; + if (!regs.zeta.Address() || !regs.zeta_enable) { + return {{}, exctx}; + } + + auto& memory_manager{system.GPU().MemoryManager()}; + const auto cpu_addr{memory_manager.GpuToCpuAddress(regs.zeta.Address())}; + if (!cpu_addr) { + return {{}, exctx}; + } + + const auto depth_params{SurfaceParams::CreateForDepthBuffer( + system, regs.zeta_width, regs.zeta_height, regs.zeta.format, + regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height, + regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)}; + return GetSurfaceView(exctx, *cpu_addr, depth_params, preserve_contents); + } + + ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index, + bool preserve_contents) { + ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); + + const auto& regs{system.GPU().Maxwell3D().regs}; + if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 || + regs.rt[index].format == Tegra::RenderTargetFormat::NONE) { + return {{}, exctx}; + } + + auto& memory_manager{system.GPU().MemoryManager()}; + const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; + const auto cpu_addr{memory_manager.GpuToCpuAddress( + config.Address() + config.base_layer * config.layer_stride * sizeof(u32))}; + if (!cpu_addr) { + return {{}, exctx}; + } + + return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index), + preserve_contents); + } + + ResultType GetFermiSurface(TExecutionContext exctx, + const Tegra::Engines::Fermi2D::Regs::Surface& config) { + const auto cpu_addr{system.GPU().MemoryManager().GpuToCpuAddress(config.Address())}; + ASSERT(cpu_addr); + return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFermiCopySurface(config), + true); + } + + TSurface* TryFindFramebufferSurface(const u8* host_ptr) const { + const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))}; + return it != registered_surfaces.end() ? *it->second.begin() : nullptr; + } + +protected: + TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) + : system{system}, rasterizer{rasterizer} {} + + ~TextureCache() = default; + + virtual ResultType TryFastGetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr, + const SurfaceParams& params, bool preserve_contents, + const std::vector<TSurface*>& overlaps) = 0; + + virtual std::unique_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0; + + void Register(TSurface* surface, VAddr cpu_addr, u8* host_ptr) { + surface->Register(cpu_addr, host_ptr); + registered_surfaces.add({GetSurfaceInterval(surface), {surface}}); + rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1); + } + + void Unregister(TSurface* surface) { + registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}}); + rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1); + surface->Unregister(); + } + + TSurface* GetUncachedSurface(const SurfaceParams& params) { + if (TSurface* surface = TryGetReservedSurface(params); surface) + return surface; + // No reserved surface available, create a new one and reserve it + auto new_surface{CreateSurface(params)}; + TSurface* surface{new_surface.get()}; + ReserveSurface(params, std::move(new_surface)); + return surface; + } + + Core::System& system; + +private: + ResultType GetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, const SurfaceParams& params, + bool preserve_contents) { + const auto host_ptr{Memory::GetPointer(cpu_addr)}; + const auto cache_addr{ToCacheAddr(host_ptr)}; + const auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())}; + if (overlaps.empty()) { + return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents); + } + + if (overlaps.size() == 1) { + if (TView* view = overlaps[0]->TryGetView(cpu_addr, params); view) + return {view, exctx}; + } + + TView* fast_view; + std::tie(fast_view, exctx) = + TryFastGetSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents, overlaps); + + for (TSurface* surface : overlaps) { + if (!fast_view) { + // Flush even when we don't care about the contents, to preserve memory not written + // by the new surface. + exctx = surface->FlushBuffer(exctx); + } + Unregister(surface); + } + + if (fast_view) { + return {fast_view, exctx}; + } + + return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents); + } + + ResultType LoadSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr, + const SurfaceParams& params, bool preserve_contents) { + TSurface* new_surface{GetUncachedSurface(params)}; + Register(new_surface, cpu_addr, host_ptr); + if (preserve_contents) { + exctx = LoadSurface(exctx, new_surface); + } + return {new_surface->GetView(cpu_addr, params), exctx}; + } + + TExecutionContext LoadSurface(TExecutionContext exctx, TSurface* surface) { + surface->LoadBuffer(); + exctx = surface->UploadTexture(exctx); + surface->MarkAsModified(false); + return exctx; + } + + std::vector<TSurface*> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const { + if (size == 0) { + return {}; + } + const IntervalType interval{cache_addr, cache_addr + size}; + + std::vector<TSurface*> surfaces; + for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) { + surfaces.push_back(*pair.second.begin()); + } + return surfaces; + } + + void ReserveSurface(const SurfaceParams& params, std::unique_ptr<TSurface> surface) { + surface_reserve[params].push_back(std::move(surface)); + } + + TSurface* TryGetReservedSurface(const SurfaceParams& params) { + auto search{surface_reserve.find(params)}; + if (search == surface_reserve.end()) { + return {}; + } + for (auto& surface : search->second) { + if (!surface->IsRegistered()) { + return surface.get(); + } + } + return {}; + } + + IntervalType GetSurfaceInterval(TSurface* surface) const { + return IntervalType::right_open(surface->GetCacheAddr(), + surface->GetCacheAddr() + surface->GetSizeInBytes()); + } + + VideoCore::RasterizerInterface& rasterizer; + + IntervalMap registered_surfaces; + + /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have + /// previously been used. This is to prevent surfaces from being constantly created and + /// destroyed when used with different surface parameters. + std::unordered_map<SurfaceParams, std::list<std::unique_ptr<TSurface>>> surface_reserve; +}; + +} // namespace VideoCommon diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 2eb86d6e5..31b65c04c 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -151,6 +151,12 @@ target_link_libraries(yuzu PRIVATE common core input_common video_core) target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::OpenGL Qt5::Widgets) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) +target_compile_definitions(yuzu PRIVATE + # Use QStringBuilder for string concatenation to reduce + # the overall number of temporary strings created. + -DQT_USE_QSTRINGBUILDER +) + if (YUZU_ENABLE_COMPATIBILITY_REPORTING) target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_COMPATIBILITY_REPORTING) endif() 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 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + #include <QApplication> #include <QHBoxLayout> #include <QKeyEvent> +#include <QOffscreenSurface> +#include <QOpenGLWindow> +#include <QPainter> #include <QScreen> #include <QWindow> #include <fmt/format.h> @@ -82,13 +89,36 @@ void EmuThread::run() { render_window->moveContext(); } +class GGLContext : public Core::Frontend::GraphicsContext { +public: + explicit GGLContext(QOpenGLContext* shared_context) : surface() { + context = std::make_unique<QOpenGLContext>(shared_context); + surface.setFormat(shared_context->format()); + surface.create(); + } + + void MakeCurrent() override { + context->makeCurrent(&surface); + } + + void DoneCurrent() override { + context->doneCurrent(); + } + + void SwapBuffers() override {} + +private: + std::unique_ptr<QOpenGLContext> context; + QOffscreenSurface surface; +}; + // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL // context. // The corresponding functionality is handled in EmuThread instead -class GGLWidgetInternal : public QGLWidget { +class GGLWidgetInternal : public QOpenGLWindow { public: - GGLWidgetInternal(QGLFormat fmt, GRenderWindow* parent) - : QGLWidget(fmt, parent), parent(parent) {} + GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) + : QOpenGLWindow(shared_context), parent(parent) {} void paintEvent(QPaintEvent* ev) override { if (do_painting) { @@ -101,9 +131,51 @@ public: parent->OnFramebufferSizeChanged(); } + void keyPressEvent(QKeyEvent* event) override { + InputCommon::GetKeyboard()->PressKey(event->key()); + } + + void keyReleaseEvent(QKeyEvent* event) override { + InputCommon::GetKeyboard()->ReleaseKey(event->key()); + } + + void mousePressEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchBeginEvent + + const auto pos{event->pos()}; + if (event->button() == Qt::LeftButton) { + const auto [x, y] = parent->ScaleTouch(pos); + parent->TouchPressed(x, y); + } else if (event->button() == Qt::RightButton) { + InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); + } + } + + void mouseMoveEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchUpdateEvent + + const auto pos{event->pos()}; + const auto [x, y] = parent->ScaleTouch(pos); + parent->TouchMoved(x, y); + InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); + } + + void mouseReleaseEvent(QMouseEvent* event) override { + if (event->source() == Qt::MouseEventSynthesizedBySystem) + return; // touch input is handled in TouchEndEvent + + if (event->button() == Qt::LeftButton) + parent->TouchReleased(); + else if (event->button() == Qt::RightButton) + InputCommon::GetMotionEmu()->EndTilt(); + } + void DisablePainting() { do_painting = false; } + void EnablePainting() { do_painting = true; } @@ -114,7 +186,7 @@ private: }; GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) - : QWidget(parent), child(nullptr), emu_thread(emu_thread) { + : QWidget(parent), child(nullptr), context(nullptr), emu_thread(emu_thread) { setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); @@ -137,19 +209,19 @@ void GRenderWindow::moveContext() { auto thread = (QThread::currentThread() == qApp->thread() && emu_thread != nullptr) ? emu_thread : qApp->thread(); - child->context()->moveToThread(thread); + context->moveToThread(thread); } void GRenderWindow::SwapBuffers() { - // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, + // In our multi-threaded QWidget use case we shouldn't need to call `makeCurrent`, // since we never call `doneCurrent` in this thread. // However: // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called // since the last time `swapBuffers` was executed; // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. - child->makeCurrent(); + context->makeCurrent(child); - child->swapBuffers(); + context->swapBuffers(child); if (!first_frame) { emit FirstFrameDisplayed(); first_frame = true; @@ -157,11 +229,11 @@ void GRenderWindow::SwapBuffers() { } void GRenderWindow::MakeCurrent() { - child->makeCurrent(); + context->makeCurrent(child); } void GRenderWindow::DoneCurrent() { - child->doneCurrent(); + context->doneCurrent(); } void GRenderWindow::PollEvents() {} @@ -174,14 +246,26 @@ void GRenderWindow::PollEvents() {} void GRenderWindow::OnFramebufferSizeChanged() { // Screen changes potentially incur a change in screen DPI, hence we should update the // framebuffer size - qreal pixelRatio = windowPixelRatio(); + qreal pixelRatio = GetWindowPixelRatio(); unsigned width = child->QPaintDevice::width() * pixelRatio; unsigned height = child->QPaintDevice::height() * pixelRatio; UpdateCurrentFramebufferLayout(width, height); } +void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { + if (child) { + child->keyPressEvent(event); + } +} + +void GRenderWindow::ForwardKeyReleaseEvent(QKeyEvent* event) { + if (child) { + child->keyReleaseEvent(event); + } +} + void GRenderWindow::BackupGeometry() { - geometry = ((QGLWidget*)this)->saveGeometry(); + geometry = ((QWidget*)this)->saveGeometry(); } void GRenderWindow::RestoreGeometry() { @@ -199,18 +283,18 @@ QByteArray GRenderWindow::saveGeometry() { // If we are a top-level widget, store the current geometry // otherwise, store the last backup if (parent() == nullptr) - return ((QGLWidget*)this)->saveGeometry(); + return ((QWidget*)this)->saveGeometry(); else return geometry; } -qreal GRenderWindow::windowPixelRatio() const { +qreal GRenderWindow::GetWindowPixelRatio() const { // windowHandle() might not be accessible until the window is displayed to screen. return windowHandle() ? windowHandle()->screen()->devicePixelRatio() : 1.0f; } std::pair<unsigned, unsigned> GRenderWindow::ScaleTouch(const QPointF pos) const { - const qreal pixel_ratio = windowPixelRatio(); + const qreal pixel_ratio = GetWindowPixelRatio(); return {static_cast<unsigned>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), static_cast<unsigned>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; } @@ -220,47 +304,6 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); } -void GRenderWindow::keyPressEvent(QKeyEvent* event) { - InputCommon::GetKeyboard()->PressKey(event->key()); -} - -void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { - InputCommon::GetKeyboard()->ReleaseKey(event->key()); -} - -void GRenderWindow::mousePressEvent(QMouseEvent* event) { - if (event->source() == Qt::MouseEventSynthesizedBySystem) - return; // touch input is handled in TouchBeginEvent - - auto pos = event->pos(); - if (event->button() == Qt::LeftButton) { - const auto [x, y] = ScaleTouch(pos); - this->TouchPressed(x, y); - } else if (event->button() == Qt::RightButton) { - InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); - } -} - -void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { - if (event->source() == Qt::MouseEventSynthesizedBySystem) - return; // touch input is handled in TouchUpdateEvent - - auto pos = event->pos(); - const auto [x, y] = ScaleTouch(pos); - this->TouchMoved(x, y); - InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); -} - -void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { - if (event->source() == Qt::MouseEventSynthesizedBySystem) - return; // touch input is handled in TouchEndEvent - - if (event->button() == Qt::LeftButton) - this->TouchReleased(); - else if (event->button() == Qt::RightButton) - InputCommon::GetMotionEmu()->EndTilt(); -} - void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { // TouchBegin always has exactly one touch point, so take the .first() const auto [x, y] = ScaleTouch(event->touchPoints().first().pos()); @@ -313,35 +356,60 @@ void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { NotifyClientAreaSizeChanged(std::make_pair(width, height)); } +std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const { + return std::make_unique<GGLContext>(shared_context.get()); +} + void GRenderWindow::InitRenderTarget() { - if (child) { - delete child; - } + shared_context.reset(); + context.reset(); - if (layout()) { - delete layout(); - } + delete child; + child = nullptr; + + delete container; + container = nullptr; + + delete layout(); first_frame = false; // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, // WA_DontShowOnScreen, WA_DeleteOnClose - QGLFormat fmt; + QSurfaceFormat fmt; fmt.setVersion(4, 3); - fmt.setProfile(QGLFormat::CoreProfile); + fmt.setProfile(QSurfaceFormat::CoreProfile); + // TODO: expose a setting for buffer value (ie default/single/double/triple) + fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); + shared_context = std::make_unique<QOpenGLContext>(); + shared_context->setFormat(fmt); + shared_context->create(); + context = std::make_unique<QOpenGLContext>(); + context->setShareContext(shared_context.get()); + context->setFormat(fmt); + context->create(); fmt.setSwapInterval(false); - // Requests a forward-compatible context, which is required to get a 3.2+ context on OS X - fmt.setOption(QGL::NoDeprecatedFunctions); + child = new GGLWidgetInternal(this, shared_context.get()); + container = QWidget::createWindowContainer(child, this); - child = new GGLWidgetInternal(fmt, this); QBoxLayout* layout = new QHBoxLayout(this); - - resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); - layout->addWidget(child); + layout->addWidget(container); layout->setMargin(0); setLayout(layout); + // Reset minimum size to avoid unwanted resizes when this function is called for a second time. + setMinimumSize(1, 1); + + // Show causes the window to actually be created and the OpenGL context as well, but we don't + // want the widget to be shown yet, so immediately hide it. + show(); + hide(); + + resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); + child->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); + container->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); + OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); 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 @@ #include <atomic> #include <condition_variable> #include <mutex> -#include <QGLWidget> #include <QImage> #include <QThread> +#include <QWidget> #include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" @@ -21,6 +21,8 @@ class QTouchEvent; class GGLWidgetInternal; class GMainWindow; class GRenderWindow; +class QSurface; +class QOpenGLContext; namespace VideoCore { enum class LoadCallbackStage; @@ -121,25 +123,21 @@ public: void MakeCurrent() override; void DoneCurrent() override; void PollEvents() override; + std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; + + void ForwardKeyPressEvent(QKeyEvent* event); + void ForwardKeyReleaseEvent(QKeyEvent* event); void BackupGeometry(); void RestoreGeometry(); void restoreGeometry(const QByteArray& geometry); // overridden QByteArray saveGeometry(); // overridden - qreal windowPixelRatio() const; + qreal GetWindowPixelRatio() const; + std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; void closeEvent(QCloseEvent* event) override; - - void keyPressEvent(QKeyEvent* event) override; - void keyReleaseEvent(QKeyEvent* event) override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - bool event(QEvent* event) override; - void focusOutEvent(QFocusEvent* event) override; void OnClientAreaResized(unsigned width, unsigned height); @@ -161,7 +159,6 @@ signals: void FirstFrameDisplayed(); private: - std::pair<unsigned, unsigned> ScaleTouch(const QPointF pos) const; void TouchBeginEvent(const QTouchEvent* event); void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); @@ -169,11 +166,17 @@ private: void OnMinimalClientAreaChangeRequest( const std::pair<unsigned, unsigned>& minimal_size) override; - GGLWidgetInternal* child; + QWidget* container = nullptr; + GGLWidgetInternal* child = nullptr; QByteArray geometry; EmuThread* emu_thread; + // Context that backs the GGLWidgetInternal (and will be used by core to render) + std::unique_ptr<QOpenGLContext> context; + // Context that will be shared between all newly created contexts. This should never be made + // current + std::unique_ptr<QOpenGLContext> shared_context; /// Temporary storage of the screenshot taken 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() { ReadSetting("use_accurate_gpu_emulation", false).toBool(); Settings::values.use_asynchronous_gpu_emulation = ReadSetting("use_asynchronous_gpu_emulation", false).toBool(); + Settings::values.force_30fps_mode = ReadSetting("force_30fps_mode", false).toBool(); Settings::values.bg_red = ReadSetting("bg_red", 0.0).toFloat(); Settings::values.bg_green = ReadSetting("bg_green", 0.0).toFloat(); @@ -523,8 +524,8 @@ void Config::ReadValues() { qt_config->beginGroup("Paths"); UISettings::values.roms_path = ReadSetting("romsPath").toString(); UISettings::values.symbols_path = ReadSetting("symbolsPath").toString(); - UISettings::values.gamedir = ReadSetting("gameListRootDir", ".").toString(); - UISettings::values.gamedir_deepscan = ReadSetting("gameListDeepScan", false).toBool(); + UISettings::values.game_directory_path = ReadSetting("gameListRootDir", ".").toString(); + UISettings::values.game_directory_deepscan = ReadSetting("gameListDeepScan", false).toBool(); UISettings::values.recent_files = ReadSetting("recentFiles").toStringList(); qt_config->endGroup(); @@ -664,6 +665,7 @@ void Config::SaveValues() { WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, false); + WriteSetting("force_30fps_mode", Settings::values.force_30fps_mode, false); // Cast to double because Qt's written float values are not human-readable WriteSetting("bg_red", (double)Settings::values.bg_red, 0.0); @@ -768,8 +770,8 @@ void Config::SaveValues() { WriteSetting("romsPath", UISettings::values.roms_path); WriteSetting("symbolsPath", UISettings::values.symbols_path); WriteSetting("screenshotPath", UISettings::values.screenshot_path); - WriteSetting("gameListRootDir", UISettings::values.gamedir, "."); - WriteSetting("gameListDeepScan", UISettings::values.gamedir_deepscan, false); + WriteSetting("gameListRootDir", UISettings::values.game_directory_path, "."); + WriteSetting("gameListDeepScan", UISettings::values.game_directory_deepscan, false); WriteSetting("recentFiles", UISettings::values.recent_files); qt_config->endGroup(); diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 51bd1f121..a5218b051 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -12,7 +12,7 @@ #include "yuzu/hotkeys.h" ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) - : QDialog(parent), registry(registry), ui(new Ui::ConfigureDialog) { + : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { ui->setupUi(this); ui->hotkeysTab->Populate(registry); this->setConfiguration(); 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) ConfigureGeneral::~ConfigureGeneral() = default; void ConfigureGeneral::setConfiguration() { - ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan); + ui->toggle_deepscan->setChecked(UISettings::values.game_directory_deepscan); ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); @@ -36,7 +36,7 @@ void ConfigureGeneral::setConfiguration() { } void ConfigureGeneral::applyConfiguration() { - UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked(); + UISettings::values.game_directory_deepscan = ui->toggle_deepscan->isChecked(); UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 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() { ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); + ui->force_30fps_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue)); } @@ -90,6 +92,7 @@ void ConfigureGraphics::applyConfiguration() { Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); Settings::values.use_asynchronous_gpu_emulation = ui->use_asynchronous_gpu_emulation->isChecked(); + Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); Settings::values.bg_red = static_cast<float>(bg_color.redF()); Settings::values.bg_green = static_cast<float>(bg_color.greenF()); 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 @@ </widget> </item> <item> + <widget class="QCheckBox" name="force_30fps_mode"> + <property name="text"> + <string>Force 30 FPS mode</string> + </property> + </widget> + </item> + <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QLabel" name="label"> diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index bfb562535..a7a8752e5 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -66,20 +66,21 @@ void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { } void ConfigureHotkeys::Configure(QModelIndex index) { - if (index.parent() == QModelIndex()) + if (!index.parent().isValid()) { return; + } index = index.sibling(index.row(), 1); - auto* model = ui->hotkey_list->model(); - auto previous_key = model->data(index); - - auto* hotkey_dialog = new SequenceDialog; - int return_code = hotkey_dialog->exec(); + auto* const model = ui->hotkey_list->model(); + const auto previous_key = model->data(index); - auto key_sequence = hotkey_dialog->GetSequence(); + SequenceDialog hotkey_dialog{this}; - if (return_code == QDialog::Rejected || key_sequence.isEmpty()) + const int return_code = hotkey_dialog.exec(); + const auto key_sequence = hotkey_dialog.GetSequence(); + if (return_code == QDialog::Rejected || key_sequence.isEmpty()) { return; + } if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { QMessageBox::critical(this, tr("Error in inputted key"), @@ -90,7 +91,7 @@ void ConfigureHotkeys::Configure(QModelIndex index) { } } -bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) { +bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) const { return GetUsedKeyList().contains(key_sequence); } diff --git a/src/yuzu/configuration/configure_hotkeys.h b/src/yuzu/configuration/configure_hotkeys.h index cd203aad6..73fb8a175 100644 --- a/src/yuzu/configuration/configure_hotkeys.h +++ b/src/yuzu/configuration/configure_hotkeys.h @@ -6,7 +6,6 @@ #include <memory> #include <QWidget> -#include "core/settings.h" namespace Ui { class ConfigureHotkeys; @@ -39,7 +38,7 @@ signals: private: void Configure(QModelIndex index); - bool IsUsedKey(QKeySequence key_sequence); + bool IsUsedKey(QKeySequence key_sequence) const; QList<QKeySequence> GetUsedKeyList() const; std::unique_ptr<Ui::ConfigureHotkeys> ui; 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() { const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; void GameList::RefreshGameDirectory() { - if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) { + if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); search_field->clear(); - PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } } diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 3db0e90da..2cf5c58a0 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -95,7 +95,7 @@ public: if (row2.isEmpty()) return row1; - return row1 + "\n " + row2; + return QString(row1 + "\n " + row2); } return GameListItem::data(role); 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() OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); game_list->LoadCompatibilityList(); - game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); // Show one-time "callout" messages to the user ShowTelemetryCallout(); @@ -1278,8 +1279,8 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); if (reload) { - game_list->PopulateAsync(UISettings::values.gamedir, - UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } config->Save(); @@ -1367,7 +1368,8 @@ void GMainWindow::OnMenuInstallToNAND() { const auto success = [this]() { QMessageBox::information(this, tr("Successfully Installed"), tr("The file was successfully installed.")); - game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); }; const auto failed = [this]() { @@ -1494,8 +1496,8 @@ void GMainWindow::OnMenuInstallToNAND() { void GMainWindow::OnMenuSelectGameListRoot() { QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory")); if (!dir_path.isEmpty()) { - UISettings::values.gamedir = dir_path; - game_list->PopulateAsync(dir_path, UISettings::values.gamedir_deepscan); + UISettings::values.game_directory_path = dir_path; + game_list->PopulateAsync(dir_path, UISettings::values.game_directory_deepscan); } } @@ -1517,7 +1519,8 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) : FileUtil::UserPath::NANDDir, dir_path.toStdString()); Service::FileSystem::CreateFactories(*vfs); - game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } } @@ -1669,8 +1672,8 @@ void GMainWindow::OnConfigure() { const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); if (reload) { - game_list->PopulateAsync(UISettings::values.gamedir, - UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } config->Save(); @@ -1920,7 +1923,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { Service::FileSystem::CreateFactories(*vfs); if (behavior == ReinitializeKeyBehavior::Warning) { - game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + game_list->PopulateAsync(UISettings::values.game_directory_path, + UISettings::values.game_directory_deepscan); } } @@ -2027,6 +2031,18 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { event->acceptProposedAction(); } +void GMainWindow::keyPressEvent(QKeyEvent* event) { + if (render_window) { + render_window->ForwardKeyPressEvent(event); + } +} + +void GMainWindow::keyReleaseEvent(QKeyEvent* event) { + if (render_window) { + render_window->ForwardKeyReleaseEvent(event); + } +} + bool GMainWindow::ConfirmChangeGame() { if (emu_thread == nullptr) return true; @@ -2094,7 +2110,8 @@ int main(int argc, char* argv[]) { QCoreApplication::setOrganizationName("yuzu team"); QCoreApplication::setApplicationName("yuzu"); - QApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); + // Enables the core to make the qt created contexts current on std::threads + QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); QApplication app(argc, argv); // 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: void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; + + // Overrides used to forward signals to the render window when the focus moves out. + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; }; 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 { QString roms_path; QString symbols_path; QString screenshot_path; - QString gamedir; - bool gamedir_deepscan; + QString game_directory_path; + bool game_directory_deepscan; QStringList recent_files; 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 @@ #include "input_common/sdl/sdl.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" +class SDLGLContext : public Core::Frontend::GraphicsContext { +public: + explicit SDLGLContext() { + // create a hidden window to make the shared context against + window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, // x position + SDL_WINDOWPOS_UNDEFINED, // y position + Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, + SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + context = SDL_GL_CreateContext(window); + } + + ~SDLGLContext() { + SDL_GL_DeleteContext(context); + SDL_DestroyWindow(window); + } + + void MakeCurrent() override { + SDL_GL_MakeCurrent(window, context); + } + + void DoneCurrent() override { + SDL_GL_MakeCurrent(window, nullptr); + } + + void SwapBuffers() override {} + +private: + SDL_Window* window; + SDL_GLContext context; +}; + void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); InputCommon::GetMotionEmu()->Tilt(x, y); @@ -153,6 +184,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc); @@ -171,7 +203,6 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { if (fullscreen) { Fullscreen(); } - gl_context = SDL_GL_CreateContext(render_window); if (gl_context == nullptr) { @@ -278,3 +309,7 @@ void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second); } + +std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2::CreateSharedContext() const { + return std::make_unique<SDLGLContext>(); +} 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: /// Releases the GL context from the caller thread void DoneCurrent() override; + std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; + /// Whether the window is still open, and a close request hasn't yet been sent bool IsOpen() const; |