diff options
Diffstat (limited to 'src')
67 files changed, 693 insertions, 530 deletions
diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 2c4ab4332..a4f7944cd 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h @@ -188,9 +188,8 @@ public: #ifdef _WIN32 template <typename Path> - [[nodiscard]] void Open(const Path& path, FileAccessMode mode, - FileType type = FileType::BinaryFile, - FileShareFlag flag = FileShareFlag::ShareReadOnly) { + void Open(const Path& path, FileAccessMode mode, FileType type = FileType::BinaryFile, + FileShareFlag flag = FileShareFlag::ShareReadOnly) { using ValueType = typename Path::value_type; if constexpr (IsChar<ValueType>) { Open(ToU8String(path), mode, type, flag); diff --git a/src/common/settings.h b/src/common/settings.h index d01c0448c..9bee6e10f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -554,6 +554,7 @@ struct Values { Setting<bool> use_docked_mode{true, "use_docked_mode"}; BasicSetting<bool> enable_raw_input{false, "enable_raw_input"}; + BasicSetting<bool> controller_navigation{true, "controller_navigation"}; Setting<bool> vibration_enabled{true, "vibration_enabled"}; Setting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 49830b8ab..9acf7551e 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -65,14 +65,20 @@ private: #ifdef ARCHITECTURE_x86_64 -std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, - u32 emulated_clock_frequency) { +std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, + u64 emulated_clock_frequency) { const auto& caps = GetCPUCaps(); u64 rtsc_frequency = 0; if (caps.invariant_tsc) { rtsc_frequency = EstimateRDTSCFrequency(); } - if (rtsc_frequency == 0) { + + // Fallback to StandardWallClock if the hardware TSC does not have the precision greater than: + // - A nanosecond + // - The emulated CPU frequency + // - The emulated clock counter frequency (CNTFRQ) + if (rtsc_frequency <= WallClock::NS_RATIO || rtsc_frequency <= emulated_cpu_frequency || + rtsc_frequency <= emulated_clock_frequency) { return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); } else { @@ -83,8 +89,8 @@ std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, #else -std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, - u32 emulated_clock_frequency) { +std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, + u64 emulated_clock_frequency) { return std::make_unique<StandardWallClock>(emulated_cpu_frequency, emulated_clock_frequency); } diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index cef3e9499..874448c27 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -13,6 +13,10 @@ namespace Common { class WallClock { public: + static constexpr u64 NS_RATIO = 1'000'000'000; + static constexpr u64 US_RATIO = 1'000'000; + static constexpr u64 MS_RATIO = 1'000; + virtual ~WallClock() = default; /// Returns current wall time in nanoseconds @@ -49,7 +53,7 @@ private: bool is_native; }; -[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u32 emulated_cpu_frequency, - u32 emulated_clock_frequency); +[[nodiscard]] std::unique_ptr<WallClock> CreateBestMatchingClock(u64 emulated_cpu_frequency, + u64 emulated_clock_frequency); } // namespace Common diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 82ee2c8a1..91b842829 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -47,9 +47,9 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen _mm_mfence(); time_point.inner.last_measure = __rdtsc(); time_point.inner.accumulated_ticks = 0U; - ns_rtsc_factor = GetFixedPoint64Factor(1000000000, rtsc_frequency); - us_rtsc_factor = GetFixedPoint64Factor(1000000, rtsc_frequency); - ms_rtsc_factor = GetFixedPoint64Factor(1000, rtsc_frequency); + ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); + us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); + ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency); clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency); cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency); } diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h index 0ddf9b83e..87b3d63a4 100644 --- a/src/common/x64/xbyak_abi.h +++ b/src/common/x64/xbyak_abi.h @@ -37,12 +37,12 @@ constexpr Xbyak::Reg IndexToReg(size_t reg_index) { } } -inline std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) { - std::bitset<32> bits; +constexpr std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) { + size_t bits = 0; for (const Xbyak::Reg& reg : regs) { - bits[RegToIndex(reg)] = true; + bits |= size_t{1} << RegToIndex(reg); } - return bits; + return {bits}; } constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF); @@ -57,7 +57,7 @@ constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx; constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8; constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9; -const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({ +constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({ // GPRs Xbyak::util::rcx, Xbyak::util::rdx, @@ -74,7 +74,7 @@ const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({ Xbyak::util::xmm5, }); -const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({ +constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({ // GPRs Xbyak::util::rbx, Xbyak::util::rsi, @@ -108,7 +108,7 @@ constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi; constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx; constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx; -const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({ +constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({ // GPRs Xbyak::util::rcx, Xbyak::util::rdx, @@ -137,7 +137,7 @@ const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({ Xbyak::util::xmm15, }); -const std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({ +constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({ // GPRs Xbyak::util::rbx, Xbyak::util::rbp, diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index d12037b11..a7cdf45e6 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -749,6 +749,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback raw_status.gyro.y.value, raw_status.gyro.z.value, }); + emulated.SetGyroThreshold(raw_status.gyro.x.properties.threshold); emulated.UpdateRotation(raw_status.delta_timestamp); emulated.UpdateOrientation(raw_status.delta_timestamp); force_update_motion = raw_status.force_update; diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp index 6e126be19..05042fd99 100644 --- a/src/core/hid/motion_input.cpp +++ b/src/core/hid/motion_input.cpp @@ -10,7 +10,7 @@ namespace Core::HID { MotionInput::MotionInput() { // Initialize PID constants with default values SetPID(0.3f, 0.005f, 0.0f); - SetGyroThreshold(0.00005f); + SetGyroThreshold(0.007f); } void MotionInput::SetPID(f32 new_kp, f32 new_ki, f32 new_kd) { @@ -31,7 +31,7 @@ void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) { gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f); } - if (gyro.Length2() < gyro_threshold) { + if (gyro.Length() < gyro_threshold) { gyro = {}; } else { only_accelerometer = false; diff --git a/src/core/hle/kernel/k_affinity_mask.h b/src/core/hle/kernel/k_affinity_mask.h index b906895fc..cf704ce87 100644 --- a/src/core/hle/kernel/k_affinity_mask.h +++ b/src/core/hle/kernel/k_affinity_mask.h @@ -31,8 +31,6 @@ public: } constexpr void SetAffinity(s32 core, bool set) { - ASSERT(0 <= core && core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); - if (set) { this->mask |= GetCoreBit(core); } else { diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index b650ea31d..2ebbc0819 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -276,22 +276,23 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state, KMemoryPermission perm) { - std::lock_guard lock{page_table_lock}; - const u64 size{num_pages * PageSize}; - if (!CanContain(addr, size, state)) { - return ResultInvalidCurrentMemory; - } + // Validate the mapping request. + R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); - if (IsRegionMapped(addr, size)) { - return ResultInvalidCurrentMemory; - } + // Lock the table. + std::lock_guard lock{page_table_lock}; + + // Verify that the destination memory is unmapped. + R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free, + KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryAttribute::None)); KPageLinkedList page_linked_list; - CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool, - allocation_option)); - CASCADE_CODE(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup)); + R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool, + allocation_option)); + R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup)); block_manager->Update(addr, num_pages, state, perm); @@ -395,39 +396,12 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, return ResultSuccess; } -void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) { - auto node{page_linked_list.Nodes().begin()}; - PAddr map_addr{node->GetAddress()}; - std::size_t src_num_pages{node->GetNumPages()}; - - block_manager->IterateForRange(start, end, [&](const KMemoryInfo& info) { - if (info.state != KMemoryState::Free) { - return; - } - - std::size_t dst_num_pages{GetSizeInRange(info, start, end) / PageSize}; - VAddr dst_addr{GetAddressInRange(info, start)}; - - while (dst_num_pages) { - if (!src_num_pages) { - node = std::next(node); - map_addr = node->GetAddress(); - src_num_pages = node->GetNumPages(); - } - - const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)}; - Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map, - map_addr); - - dst_addr += num_pages * PageSize; - map_addr += num_pages * PageSize; - src_num_pages -= num_pages; - dst_num_pages -= num_pages; - } - }); -} ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { + // Lock the physical memory lock. + std::lock_guard phys_lk(map_physical_memory_lock); + + // Lock the table. std::lock_guard lock{page_table_lock}; std::size_t mapped_size{}; @@ -463,7 +437,35 @@ ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { // We succeeded, so commit the memory reservation. memory_reservation.Commit(); - MapPhysicalMemory(page_linked_list, addr, end_addr); + // Map the memory. + auto node{page_linked_list.Nodes().begin()}; + PAddr map_addr{node->GetAddress()}; + std::size_t src_num_pages{node->GetNumPages()}; + block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { + if (info.state != KMemoryState::Free) { + return; + } + + std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize}; + VAddr dst_addr{GetAddressInRange(info, addr)}; + + while (dst_num_pages) { + if (!src_num_pages) { + node = std::next(node); + map_addr = node->GetAddress(); + src_num_pages = node->GetNumPages(); + } + + const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)}; + Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map, + map_addr); + + dst_addr += num_pages * PageSize; + map_addr += num_pages * PageSize; + src_num_pages -= num_pages; + dst_num_pages -= num_pages; + } + }); mapped_physical_memory_size += remaining_size; @@ -503,23 +505,8 @@ ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) { return ResultSuccess; } - CASCADE_CODE(UnmapMemory(addr, size)); - - auto process{system.Kernel().CurrentProcess()}; - process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); - mapped_physical_memory_size -= mapped_size; - - return ResultSuccess; -} - -ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) { - std::lock_guard lock{page_table_lock}; - - const VAddr end_addr{addr + size}; - ResultCode result{ResultSuccess}; - KPageLinkedList page_linked_list; - // Unmap each region within the range + KPageLinkedList page_linked_list; block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) { if (info.state == KMemoryState::Normal) { const std::size_t block_size{GetSizeInRange(info, addr, end_addr)}; @@ -535,7 +522,6 @@ ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) { } } }); - if (result.IsError()) { return result; } @@ -546,10 +532,14 @@ ResultCode KPageTable::UnmapMemory(VAddr addr, std::size_t size) { block_manager->Update(addr, num_pages, KMemoryState::Free); + auto process{system.Kernel().CurrentProcess()}; + process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); + mapped_physical_memory_size -= mapped_size; + return ResultSuccess; } -ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) { +ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { std::lock_guard lock{page_table_lock}; KMemoryState src_state{}; @@ -588,7 +578,7 @@ ResultCode KPageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) { return ResultSuccess; } -ResultCode KPageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) { +ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { std::lock_guard lock{page_table_lock}; KMemoryState src_state{}; @@ -652,24 +642,26 @@ ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_l return ResultSuccess; } -ResultCode KPageTable::MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, - KMemoryPermission perm) { - std::lock_guard lock{page_table_lock}; - +ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list, + KMemoryState state, KMemoryPermission perm) { + // Check that the map is in range. const std::size_t num_pages{page_linked_list.GetNumPages()}; const std::size_t size{num_pages * PageSize}; + R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); - if (!CanContain(addr, size, state)) { - return ResultInvalidCurrentMemory; - } + // Lock the table. + std::lock_guard lock{page_table_lock}; - if (IsRegionMapped(addr, num_pages * PageSize)) { - return ResultInvalidCurrentMemory; - } + // Check the memory state. + R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, + KMemoryPermission::None, KMemoryPermission::None, + KMemoryAttribute::None, KMemoryAttribute::None)); - CASCADE_CODE(MapPages(addr, page_linked_list, perm)); + // Map the pages. + R_TRY(MapPages(address, page_linked_list, perm)); - block_manager->Update(addr, num_pages, state, perm); + // Update the blocks. + block_manager->Update(address, num_pages, state, perm); return ResultSuccess; } @@ -693,21 +685,23 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state) { - std::lock_guard lock{page_table_lock}; - + // Check that the unmap is in range. const std::size_t num_pages{page_linked_list.GetNumPages()}; const std::size_t size{num_pages * PageSize}; + R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory); - if (!CanContain(addr, size, state)) { - return ResultInvalidCurrentMemory; - } + // Lock the table. + std::lock_guard lock{page_table_lock}; - if (IsRegionMapped(addr, num_pages * PageSize)) { - return ResultInvalidCurrentMemory; - } + // Check the memory state. + R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, state, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::All, + KMemoryAttribute::None)); - CASCADE_CODE(UnmapPages(addr, page_linked_list)); + // Perform the unmap. + R_TRY(UnmapPages(addr, page_linked_list)); + // Update the blocks. block_manager->Update(addr, num_pages, state, KMemoryPermission::None); return ResultSuccess; @@ -765,7 +759,6 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, // Ensure cache coherency, if we're setting pages as executable. if (is_x) { - // Memory execution state is changing, invalidate CPU cache range system.InvalidateCpuInstructionCacheRange(addr, size); } @@ -793,12 +786,12 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo KMemoryState state{}; KMemoryAttribute attribute{}; - CASCADE_CODE(CheckMemoryState( - &state, nullptr, &attribute, nullptr, addr, size, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, KMemoryPermission::All, - KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, KMemoryAttribute::None, - KMemoryAttribute::IpcAndDeviceMapped)); + R_TRY(CheckMemoryState(&state, nullptr, &attribute, nullptr, addr, size, + KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, + KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, + KMemoryPermission::All, KMemoryPermission::UserReadWrite, + KMemoryAttribute::Mask, KMemoryAttribute::None, + KMemoryAttribute::IpcAndDeviceMapped)); block_manager->Update(addr, size / PageSize, state, perm, attribute | KMemoryAttribute::Locked); @@ -810,12 +803,11 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) { KMemoryState state{}; - CASCADE_CODE( - CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, - KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask, - KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); + R_TRY(CheckMemoryState(&state, nullptr, nullptr, nullptr, addr, size, + KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, + KMemoryState::FlagCanTransfer | KMemoryState::FlagReferenceCounted, + KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::Mask, + KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped)); block_manager->Update(addr, size / PageSize, state, KMemoryPermission::UserReadWrite); return ResultSuccess; @@ -871,8 +863,9 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask)); // Determine the new attribute. - const auto new_attr = ((old_attr & static_cast<KMemoryAttribute>(~mask)) | - static_cast<KMemoryAttribute>(attr & mask)); + const KMemoryAttribute new_attr = + static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) | + static_cast<KMemoryAttribute>(attr & mask))); // Perform operation. this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh); @@ -896,6 +889,9 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) { } ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) { + // Lock the physical memory lock. + std::lock_guard phys_lk(map_physical_memory_lock); + // Try to perform a reduction in heap, instead of an extension. VAddr cur_address{}; std::size_t allocation_size{}; @@ -1025,12 +1021,12 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages, } if (is_map_only) { - CASCADE_CODE(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); + R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); } else { KPageLinkedList page_group; - CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, - memory_pool, allocation_option)); - CASCADE_CODE(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); + R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool, + allocation_option)); + R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); } block_manager->Update(addr, needed_num_pages, state, perm); @@ -1186,7 +1182,7 @@ VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_page ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group, OperationType operation) { - std::lock_guard lock{page_table_lock}; + ASSERT(this->IsLockedByCurrentThread()); ASSERT(Common::IsAligned(addr, PageSize)); ASSERT(num_pages > 0); @@ -1211,7 +1207,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm, OperationType operation, PAddr map_addr) { - std::lock_guard lock{page_table_lock}; + ASSERT(this->IsLockedByCurrentThread()); ASSERT(num_pages > 0); ASSERT(Common::IsAligned(addr, PageSize)); diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index f67986e91..60ae9b9e8 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -37,9 +37,8 @@ public: VAddr src_addr); ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); - ResultCode UnmapMemory(VAddr addr, std::size_t size); - ResultCode Map(VAddr dst_addr, VAddr src_addr, std::size_t size); - ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size); + ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); + ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, KMemoryPermission perm); ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); @@ -88,7 +87,6 @@ private: ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, KMemoryPermission perm); ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); - void MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end); bool IsRegionMapped(VAddr address, u64 size); bool IsRegionContiguous(VAddr addr, u64 size) const; void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list); @@ -148,6 +146,7 @@ private: } std::recursive_mutex page_table_lock; + std::mutex map_physical_memory_lock; std::unique_ptr<KMemoryBlockManager> block_manager; public: @@ -249,7 +248,9 @@ public: return !IsOutsideASLRRegion(address, size); } constexpr PAddr GetPhysicalAddr(VAddr addr) { - return page_table_impl.backing_addr[addr >> PageBits] + addr; + const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; + ASSERT(backing_addr); + return backing_addr + addr; } constexpr bool Contains(VAddr addr) const { return address_space_start <= addr && addr <= address_space_end - 1; diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h index 0b894c8cf..bd779739d 100644 --- a/src/core/hle/kernel/k_priority_queue.h +++ b/src/core/hle/kernel/k_priority_queue.h @@ -258,7 +258,7 @@ private: private: constexpr void ClearAffinityBit(u64& affinity, s32 core) { - affinity &= ~(u64(1) << core); + affinity &= ~(UINT64_C(1) << core); } constexpr s32 GetNextCore(u64& affinity) { diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 265ac6fa1..85c506979 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -146,6 +146,13 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st // Open a reference to the resource limit. process->resource_limit->Open(); + // Clear remaining fields. + process->num_running_threads = 0; + process->is_signaled = false; + process->exception_thread = nullptr; + process->is_suspended = false; + process->schedule_count = 0; + return ResultSuccess; } @@ -157,20 +164,17 @@ KResourceLimit* KProcess::GetResourceLimit() const { return resource_limit; } -void KProcess::IncrementThreadCount() { - ASSERT(num_threads >= 0); - num_created_threads++; - - if (const auto count = ++num_threads; count > peak_num_threads) { - peak_num_threads = count; - } +void KProcess::IncrementRunningThreadCount() { + ASSERT(num_running_threads.load() >= 0); + ++num_running_threads; } -void KProcess::DecrementThreadCount() { - ASSERT(num_threads > 0); +void KProcess::DecrementRunningThreadCount() { + ASSERT(num_running_threads.load() > 0); - if (const auto count = --num_threads; count == 0) { - LOG_WARNING(Kernel, "Process termination is not fully implemented."); + if (const auto prev = num_running_threads--; prev == 1) { + // TODO(bunnei): Process termination to be implemented when multiprocess is supported. + UNIMPLEMENTED_MSG("KProcess termination is not implemennted!"); } } diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index c2a672021..38b446350 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -235,8 +235,8 @@ public: ++schedule_count; } - void IncrementThreadCount(); - void DecrementThreadCount(); + void IncrementRunningThreadCount(); + void DecrementRunningThreadCount(); void SetRunningThread(s32 core, KThread* thread, u64 idle_count) { running_threads[core] = thread; @@ -473,9 +473,7 @@ private: bool is_suspended{}; bool is_initialized{}; - std::atomic<s32> num_created_threads{}; - std::atomic<u16> num_threads{}; - u16 peak_num_threads{}; + std::atomic<u16> num_running_threads{}; std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{}; std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{}; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index b32d4f285..c96520828 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -710,23 +710,19 @@ void KScheduler::Unload(KThread* thread) { } void KScheduler::Reload(KThread* thread) { - LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr"); + LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName()); - if (thread) { - ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); - - Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); - cpu_core.LoadContext(thread->GetContext32()); - cpu_core.LoadContext(thread->GetContext64()); - cpu_core.SetTlsAddress(thread->GetTLSAddress()); - cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); - cpu_core.ClearExclusiveState(); - } + Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); + cpu_core.LoadContext(thread->GetContext32()); + cpu_core.LoadContext(thread->GetContext64()); + cpu_core.SetTlsAddress(thread->GetTLSAddress()); + cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); + cpu_core.ClearExclusiveState(); } void KScheduler::SwitchContextStep2() { // Load context of new thread - Reload(current_thread.load()); + Reload(GetCurrentThread()); RescheduleCurrentCore(); } @@ -735,13 +731,17 @@ void KScheduler::ScheduleImpl() { KThread* previous_thread = GetCurrentThread(); KThread* next_thread = state.highest_priority_thread; - state.needs_scheduling = false; + state.needs_scheduling.store(false); // We never want to schedule a null thread, so use the idle thread if we don't have a next. if (next_thread == nullptr) { next_thread = idle_thread; } + if (next_thread->GetCurrentCore() != core_id) { + next_thread->SetCurrentCore(core_id); + } + // We never want to schedule a dummy thread, as these are only used by host threads for locking. if (next_thread->GetThreadType() == ThreadType::Dummy) { ASSERT_MSG(false, "Dummy threads should never be scheduled!"); @@ -755,14 +755,8 @@ void KScheduler::ScheduleImpl() { return; } - if (next_thread->GetCurrentCore() != core_id) { - next_thread->SetCurrentCore(core_id); - } - - current_thread.store(next_thread); - + // Update the CPU time tracking variables. KProcess* const previous_process = system.Kernel().CurrentProcess(); - UpdateLastContextSwitchTime(previous_thread, previous_process); // Save context for previous thread @@ -770,6 +764,10 @@ void KScheduler::ScheduleImpl() { std::shared_ptr<Common::Fiber>* old_context; old_context = &previous_thread->GetHostContext(); + + // Set the new thread. + current_thread.store(next_thread); + guard.Unlock(); Common::Fiber::YieldTo(*old_context, *switch_fiber); @@ -797,8 +795,8 @@ void KScheduler::SwitchToCurrent() { do { auto next_thread = current_thread.load(); if (next_thread != nullptr) { - next_thread->context_guard.Lock(); - if (next_thread->GetRawState() != ThreadState::Runnable) { + const auto locked = next_thread->context_guard.TryLock(); + if (state.needs_scheduling.load()) { next_thread->context_guard.Unlock(); break; } @@ -806,6 +804,9 @@ void KScheduler::SwitchToCurrent() { next_thread->context_guard.Unlock(); break; } + if (!locked) { + continue; + } } auto thread = next_thread ? next_thread : idle_thread; Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext()); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index f42abb8a1..de3ffe0c7 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -215,7 +215,6 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s parent = owner; parent->Open(); - parent->IncrementThreadCount(); } // Initialize thread context. @@ -327,11 +326,6 @@ void KThread::Finalize() { } } - // Decrement the parent process's thread count. - if (parent != nullptr) { - parent->DecrementThreadCount(); - } - // Perform inherited finalization. KSynchronizationObject::Finalize(); } @@ -1011,7 +1005,7 @@ ResultCode KThread::Run() { if (IsUserThread() && IsSuspended()) { this->UpdateState(); } - owner->IncrementThreadCount(); + owner->IncrementRunningThreadCount(); } // Set our state and finish. @@ -1026,10 +1020,11 @@ ResultCode KThread::Run() { void KThread::Exit() { ASSERT(this == GetCurrentThreadPointer(kernel)); - // Release the thread resource hint from parent. + // Release the thread resource hint, running thread count from parent. if (parent != nullptr) { parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1); resource_limit_release_hint = true; + parent->DecrementRunningThreadCount(); } // Perform termination. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index c7f5140f4..4f7aebf3f 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -230,7 +230,7 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr return result; } - return page_table.Map(dst_addr, src_addr, size); + return page_table.MapMemory(dst_addr, src_addr, size); } static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { @@ -249,7 +249,7 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad return result; } - return page_table.Unmap(dst_addr, src_addr, size); + return page_table.UnmapMemory(dst_addr, src_addr, size); } static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { @@ -2613,7 +2613,7 @@ static const FunctionDef SVC_Table_32[] = { {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"}, {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"}, {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"}, - {0x36, nullptr, "Unknown"}, + {0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"}, {0x37, nullptr, "Unknown"}, {0x38, nullptr, "Unknown"}, {0x39, nullptr, "Unknown"}, diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index ed6281772..577bf5c31 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -109,8 +109,9 @@ public: bool HasHDRumble() const { if (sdl_controller) { - return (SDL_GameControllerGetType(sdl_controller.get()) == - SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO); + const auto type = SDL_GameControllerGetType(sdl_controller.get()); + return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) || + (type == SDL_CONTROLLER_TYPE_PS5); } return false; } diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 9aaeb91be..d1cdb1ab2 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -339,7 +339,7 @@ void UDPClient::StartCommunication(std::size_t client, const std::string& host, } } -const PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const { +PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const { const std::size_t client = pad_index / PADS_PER_CLIENT; return { .guid = clients[client].uuid, @@ -348,9 +348,9 @@ const PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const { }; } -const Common::UUID UDPClient::GetHostUUID(const std::string host) const { - const auto ip = boost::asio::ip::address_v4::from_string(host); - const auto hex_host = fmt::format("{:06x}", ip.to_ulong()); +Common::UUID UDPClient::GetHostUUID(const std::string& host) const { + const auto ip = boost::asio::ip::make_address_v4(host); + const auto hex_host = fmt::format("{:06x}", ip.to_uint()); return Common::UUID{hex_host}; } diff --git a/src/input_common/drivers/udp_client.h b/src/input_common/drivers/udp_client.h index 61a1fff37..30d7c2682 100644 --- a/src/input_common/drivers/udp_client.h +++ b/src/input_common/drivers/udp_client.h @@ -145,8 +145,8 @@ private: void OnPortInfo(Response::PortInfo); void OnPadData(Response::PadData, std::size_t client); void StartCommunication(std::size_t client, const std::string& host, u16 port); - const PadIdentifier GetPadIdentifier(std::size_t pad_index) const; - const Common::UUID GetHostUUID(const std::string host) const; + PadIdentifier GetPadIdentifier(std::size_t pad_index) const; + Common::UUID GetHostUUID(const std::string& host) const; Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index 390581c94..fe2faee5a 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h @@ -16,7 +16,7 @@ // Pad Identifier of data source struct PadIdentifier { - Common::UUID guid{}; + Common::UUID guid{Common::INVALID_UUID}; std::size_t port{}; std::size_t pad{}; @@ -89,7 +89,7 @@ struct UpdateCallback { // Triggered if data changed on the controller and the engine is on configuring mode struct MappingCallback { - std::function<void(MappingData)> on_data; + std::function<void(const MappingData&)> on_data; }; // Input Identifier of data source diff --git a/src/input_common/input_mapping.cpp b/src/input_common/input_mapping.cpp index 475257f42..a7a6ad8c2 100644 --- a/src/input_common/input_mapping.cpp +++ b/src/input_common/input_mapping.cpp @@ -2,14 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included -#include "common/common_types.h" #include "common/settings.h" #include "input_common/input_engine.h" #include "input_common/input_mapping.h" namespace InputCommon { -MappingFactory::MappingFactory() {} +MappingFactory::MappingFactory() = default; void MappingFactory::BeginMapping(Polling::InputType type) { is_enabled = true; @@ -19,7 +18,7 @@ void MappingFactory::BeginMapping(Polling::InputType type) { second_axis = -1; } -[[nodiscard]] const Common::ParamPackage MappingFactory::GetNextInput() { +Common::ParamPackage MappingFactory::GetNextInput() { Common::ParamPackage input; input_queue.Pop(input); return input; @@ -57,7 +56,7 @@ void MappingFactory::StopMapping() { void MappingFactory::RegisterButton(const MappingData& data) { Common::ParamPackage new_input; new_input.Set("engine", data.engine); - if (data.pad.guid != Common::UUID{}) { + if (data.pad.guid.IsValid()) { new_input.Set("guid", data.pad.guid.Format()); } new_input.Set("port", static_cast<int>(data.pad.port)); @@ -93,7 +92,7 @@ void MappingFactory::RegisterButton(const MappingData& data) { void MappingFactory::RegisterStick(const MappingData& data) { Common::ParamPackage new_input; new_input.Set("engine", data.engine); - if (data.pad.guid != Common::UUID{}) { + if (data.pad.guid.IsValid()) { new_input.Set("guid", data.pad.guid.Format()); } new_input.Set("port", static_cast<int>(data.pad.port)); @@ -138,7 +137,7 @@ void MappingFactory::RegisterStick(const MappingData& data) { void MappingFactory::RegisterMotion(const MappingData& data) { Common::ParamPackage new_input; new_input.Set("engine", data.engine); - if (data.pad.guid != Common::UUID{}) { + if (data.pad.guid.IsValid()) { new_input.Set("guid", data.pad.guid.Format()); } new_input.Set("port", static_cast<int>(data.pad.port)); diff --git a/src/input_common/input_mapping.h b/src/input_common/input_mapping.h index 93564b5f8..e0dfbc7ad 100644 --- a/src/input_common/input_mapping.h +++ b/src/input_common/input_mapping.h @@ -3,8 +3,14 @@ // Refer to the license.txt file included #pragma once + +#include "common/param_package.h" #include "common/threadsafe_queue.h" +namespace InputCommon::Polling { +enum class InputType; +} + namespace InputCommon { class InputEngine; struct MappingData; @@ -20,7 +26,7 @@ public: void BeginMapping(Polling::InputType type); /// Returns an input event with mapping information from the input_queue - [[nodiscard]] const Common::ParamPackage GetNextInput(); + [[nodiscard]] Common::ParamPackage GetNextInput(); /** * Registers mapping input data from the driver diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 7b370335f..2f3c0735a 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp @@ -504,9 +504,10 @@ private: class InputFromMotion final : public Common::Input::InputDevice { public: - explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, + explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_, InputEngine* input_engine_) - : identifier(identifier_), motion_sensor(motion_sensor_), input_engine(input_engine_) { + : identifier(identifier_), motion_sensor(motion_sensor_), gyro_threshold(gyro_threshold_), + input_engine(input_engine_) { UpdateCallback engine_callback{[this]() { OnChange(); }}; const InputIdentifier input_identifier{ .identifier = identifier, @@ -525,8 +526,9 @@ public: const auto basic_motion = input_engine->GetMotion(identifier, motion_sensor); Common::Input::MotionStatus status{}; const Common::Input::AnalogProperties properties = { - .deadzone = 0.001f, + .deadzone = 0.0f, .range = 1.0f, + .threshold = gyro_threshold, .offset = 0.0f, }; status.accel.x = {.raw_value = basic_motion.accel_x, .properties = properties}; @@ -551,6 +553,7 @@ public: private: const PadIdentifier identifier; const int motion_sensor; + const float gyro_threshold; int callback_key; InputEngine* input_engine; }; @@ -873,9 +876,11 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice( if (params.Has("motion")) { const auto motion_sensor = params.Get("motion", 0); + const auto gyro_threshold = params.Get("threshold", 0.007f); input_engine->PreSetController(identifier); input_engine->PreSetMotion(identifier, motion_sensor); - return std::make_unique<InputFromMotion>(identifier, motion_sensor, input_engine.get()); + return std::make_unique<InputFromMotion>(identifier, motion_sensor, gyro_threshold, + input_engine.get()); } const auto deadzone = std::clamp(params.Get("deadzone", 0.15f), 0.0f, 1.0f); diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 940744c5f..a4d7ed645 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -27,7 +27,7 @@ namespace InputCommon { struct InputSubsystem::Impl { void Initialize() { mapping_factory = std::make_shared<MappingFactory>(); - MappingCallback mapping_callback{[this](MappingData data) { RegisterInput(data); }}; + MappingCallback mapping_callback{[this](const MappingData& data) { RegisterInput(data); }}; keyboard = std::make_shared<Keyboard>("keyboard"); keyboard->SetMappingCallback(mapping_callback); @@ -284,7 +284,7 @@ struct InputSubsystem::Impl { #endif } - void RegisterInput(MappingData data) { + void RegisterInput(const MappingData& data) { mapping_factory->RegisterInput(data); } @@ -394,7 +394,7 @@ void InputSubsystem::BeginMapping(Polling::InputType type) { impl->mapping_factory->BeginMapping(type); } -const Common::ParamPackage InputSubsystem::GetNextInput() const { +Common::ParamPackage InputSubsystem::GetNextInput() const { return impl->mapping_factory->GetNextInput(); } diff --git a/src/input_common/main.h b/src/input_common/main.h index c6f97f691..baf107e0f 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -126,7 +126,7 @@ public: void BeginMapping(Polling::InputType type); /// Returns an input event with mapping information. - [[nodiscard]] const Common::ParamPackage GetNextInput() const; + [[nodiscard]] Common::ParamPackage GetNextInput() const; /// Stop polling from all backends. void StopMapping() const; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 50918317f..08b3a81ce 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -387,6 +387,14 @@ void SetupSignedNanCapabilities(const Profile& profile, const IR::Program& progr } } +void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) { + if (ctx.runtime_info.xfb_varyings.empty()) { + return; + } + ctx.AddCapability(spv::Capability::TransformFeedback); + ctx.AddExecutionMode(main_func, spv::ExecutionMode::Xfb); +} + void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) { if (info.uses_sampled_1d) { ctx.AddCapability(spv::Capability::Sampled1D); @@ -442,9 +450,6 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct if (info.uses_sample_id) { ctx.AddCapability(spv::Capability::SampleRateShading); } - if (!ctx.runtime_info.xfb_varyings.empty()) { - ctx.AddCapability(spv::Capability::TransformFeedback); - } if (info.uses_derivatives) { ctx.AddCapability(spv::Capability::DerivativeControl); } @@ -484,6 +489,7 @@ std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in SetupSignedNanCapabilities(profile, program, ctx, main); } SetupCapabilities(profile, program.info, ctx); + SetupTransformFeedbackCapabilities(ctx, main); PatchPhiNodes(program, ctx); return ctx.Assemble(); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 0d37b405c..46ba52a25 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -74,7 +74,7 @@ Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& const auto [scope, semantics]{AtomicArgs(ctx)}; return (ctx.*atomic_func)(ctx.U64, pointer, scope, semantics, value); } - LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); + LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2, binding, offset, sizeof(u32[2]))}; const Id original_value{ctx.OpBitcast(ctx.U64, ctx.OpLoad(ctx.U32[2], pointer))}; @@ -267,7 +267,7 @@ Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const const auto [scope, semantics]{AtomicArgs(ctx)}; return ctx.OpAtomicExchange(ctx.U64, pointer, scope, semantics, value); } - LOG_ERROR(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); + LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic"); const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2, binding, offset, sizeof(u32[2]))}; const Id original{ctx.OpBitcast(ctx.U64, ctx.OpLoad(ctx.U32[2], pointer))}; diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/video_minimum_maximum.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/video_minimum_maximum.cpp index 78869601f..4851b0b8d 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/video_minimum_maximum.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/video_minimum_maximum.cpp @@ -57,16 +57,6 @@ void TranslatorVisitor::VMNMX(u64 insn) { if (vmnmx.sat != 0) { throw NotImplementedException("VMNMX SAT"); } - // Selectors were shown to default to 2 in unit tests - if (vmnmx.src_a_selector != 2) { - throw NotImplementedException("VMNMX Selector {}", vmnmx.src_a_selector.Value()); - } - if (vmnmx.src_b_selector != 2) { - throw NotImplementedException("VMNMX Selector {}", vmnmx.src_b_selector.Value()); - } - if (vmnmx.src_a_width != VideoWidth::Word) { - throw NotImplementedException("VMNMX Source Width {}", vmnmx.src_a_width.Value()); - } const bool is_b_imm{vmnmx.is_src_b_reg == 0}; const IR::U32 src_a{GetReg8(insn)}; @@ -76,10 +66,14 @@ void TranslatorVisitor::VMNMX(u64 insn) { const VideoWidth a_width{vmnmx.src_a_width}; const VideoWidth b_width{GetVideoSourceWidth(vmnmx.src_b_width, is_b_imm)}; + const u32 a_selector{static_cast<u32>(vmnmx.src_a_selector)}; + // Immediate values can't have a selector + const u32 b_selector{is_b_imm ? 0U : static_cast<u32>(vmnmx.src_b_selector)}; + const bool src_a_signed{vmnmx.src_a_sign != 0}; const bool src_b_signed{vmnmx.src_b_sign != 0}; - const IR::U32 op_a{ExtractVideoOperandValue(ir, src_a, a_width, 0, src_a_signed)}; - const IR::U32 op_b{ExtractVideoOperandValue(ir, src_b, b_width, 0, src_b_signed)}; + const IR::U32 op_a{ExtractVideoOperandValue(ir, src_a, a_width, a_selector, src_a_signed)}; + const IR::U32 op_b{ExtractVideoOperandValue(ir, src_b, b_width, b_selector, src_b_signed)}; // First operation's sign is only dependent on operand b's sign const bool op_1_signed{src_b_signed}; diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index cd535f20d..eac83da9d 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -21,7 +21,6 @@ namespace Shader::Maxwell { [[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b, Environment& env_vertex_b); -[[nodiscard]] void ConvertLegacyToGeneric(IR::Program& program, - const Shader::RuntimeInfo& runtime_info); +void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info); } // namespace Shader::Maxwell diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp index a78c469be..b6a20f904 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp @@ -688,7 +688,7 @@ void VisitUsages(Info& info, IR::Inst& inst) { case IR::Opcode::StorageAtomicAnd64: case IR::Opcode::StorageAtomicOr64: case IR::Opcode::StorageAtomicXor64: - info.used_storage_buffer_types |= IR::Type::U64; + info.used_storage_buffer_types |= IR::Type::U64 | IR::Type::U32x2; info.uses_int64_bit_atomics = true; break; case IR::Opcode::BindlessImageAtomicIAdd32: diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 43bed63ac..fa26eb8b0 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -131,6 +131,8 @@ public: void DownloadMemory(VAddr cpu_addr, u64 size); + bool InlineMemory(VAddr dest_address, size_t copy_size, std::span<u8> inlined_buffer); + void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size); void DisableGraphicsUniformBuffer(size_t stage, u32 index); @@ -808,6 +810,8 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { return; } MICROPROFILE_SCOPE(GPU_DownloadMemory); + const bool is_accuracy_normal = + Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::Normal; boost::container::small_vector<std::pair<BufferCopy, BufferId>, 1> downloads; u64 total_size_bytes = 0; @@ -819,6 +823,9 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) { buffer.ForEachDownloadRangeAndClear( cpu_addr, size, [&](u64 range_offset, u64 range_size) { + if (is_accuracy_normal) { + return; + } const VAddr buffer_addr = buffer.CpuAddr(); const auto add_download = [&](VAddr start, VAddr end) { const u64 new_offset = start - buffer_addr; @@ -1417,10 +1424,8 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 s const IntervalType base_interval{cpu_addr, cpu_addr + size}; common_ranges.add(base_interval); - const bool is_accuracy_high = - Settings::values.gpu_accuracy.GetValue() == Settings::GPUAccuracy::High; const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); - if (!is_async && !is_accuracy_high) { + if (!is_async) { return; } uncommitted_ranges.add(base_interval); @@ -1474,6 +1479,8 @@ typename BufferCache<P>::OverlapResult BufferCache<P>::ResolveOverlaps(VAddr cpu // When this memory region has been joined a bunch of times, we assume it's being used // as a stream buffer. Increase the size to skip constantly recreating buffers. has_stream_leap = true; + begin -= PAGE_SIZE * 256; + cpu_addr = begin; end += PAGE_SIZE * 256; } } @@ -1642,6 +1649,42 @@ void BufferCache<P>::MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, } template <class P> +bool BufferCache<P>::InlineMemory(VAddr dest_address, size_t copy_size, + std::span<u8> inlined_buffer) { + const bool is_dirty = IsRegionRegistered(dest_address, copy_size); + if (!is_dirty) { + return false; + } + if (!IsRegionGpuModified(dest_address, copy_size)) { + return false; + } + + const IntervalType subtract_interval{dest_address, dest_address + copy_size}; + ClearDownload(subtract_interval); + common_ranges.subtract(subtract_interval); + + BufferId buffer_id = FindBuffer(dest_address, static_cast<u32>(copy_size)); + auto& buffer = slot_buffers[buffer_id]; + SynchronizeBuffer(buffer, dest_address, static_cast<u32>(copy_size)); + + if constexpr (USE_MEMORY_MAPS) { + std::array copies{BufferCopy{ + .src_offset = 0, + .dst_offset = buffer.Offset(dest_address), + .size = copy_size, + }}; + auto upload_staging = runtime.UploadStagingBuffer(copy_size); + u8* const src_pointer = upload_staging.mapped_span.data(); + std::memcpy(src_pointer, inlined_buffer.data(), copy_size); + runtime.CopyBuffer(buffer, upload_staging.buffer, copies); + } else { + buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); + } + + return true; +} + +template <class P> void BufferCache<P>::DownloadBufferMemory(Buffer& buffer) { DownloadBufferMemory(buffer, buffer.CpuAddr(), buffer.SizeBytes()); } diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp index 71d7e1473..351b110fe 100644 --- a/src/video_core/engines/engine_upload.cpp +++ b/src/video_core/engines/engine_upload.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "video_core/engines/engine_upload.h" #include "video_core/memory_manager.h" +#include "video_core/rasterizer_interface.h" #include "video_core/textures/decoders.h" namespace Tegra::Engines::Upload { @@ -16,6 +17,10 @@ State::State(MemoryManager& memory_manager_, Registers& regs_) State::~State() = default; +void State::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { + rasterizer = rasterizer_; +} + void State::ProcessExec(const bool is_linear_) { write_offset = 0; copy_size = regs.line_length_in * regs.line_count; @@ -32,7 +37,7 @@ void State::ProcessData(const u32 data, const bool is_last_call) { } const GPUVAddr address{regs.dest.Address()}; if (is_linear) { - memory_manager.WriteBlock(address, inner_buffer.data(), copy_size); + rasterizer->AccelerateInlineToMemory(address, copy_size, inner_buffer); } else { UNIMPLEMENTED_IF(regs.dest.z != 0); UNIMPLEMENTED_IF(regs.dest.depth != 1); diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h index 1c7f1effa..c9c5ec8c3 100644 --- a/src/video_core/engines/engine_upload.h +++ b/src/video_core/engines/engine_upload.h @@ -12,6 +12,10 @@ namespace Tegra { class MemoryManager; } +namespace VideoCore { +class RasterizerInterface; +} + namespace Tegra::Engines::Upload { struct Registers { @@ -60,6 +64,9 @@ public: void ProcessExec(bool is_linear_); void ProcessData(u32 data, bool is_last_call); + /// Binds a rasterizer to this engine. + void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + private: u32 write_offset = 0; u32 copy_size = 0; @@ -68,6 +75,7 @@ private: bool is_linear = false; Registers& regs; MemoryManager& memory_manager; + VideoCore::RasterizerInterface* rasterizer = nullptr; }; } // namespace Tegra::Engines::Upload diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 492b4c5a3..5a1c12076 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -22,6 +22,7 @@ KeplerCompute::~KeplerCompute() = default; void KeplerCompute::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { rasterizer = rasterizer_; + upload_state.BindRasterizer(rasterizer); } void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_call) { diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index 560551157..8aed16caa 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -19,6 +19,10 @@ KeplerMemory::KeplerMemory(Core::System& system_, MemoryManager& memory_manager) KeplerMemory::~KeplerMemory() = default; +void KeplerMemory::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { + upload_state.BindRasterizer(rasterizer_); +} + void KeplerMemory::CallMethod(u32 method, u32 method_argument, bool is_last_call) { ASSERT_MSG(method < Regs::NUM_REGS, "Invalid KeplerMemory register, increase the size of the Regs structure"); diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 0d8ea09a9..949e2fae1 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h @@ -22,6 +22,10 @@ namespace Tegra { class MemoryManager; } +namespace VideoCore { +class RasterizerInterface; +} + namespace Tegra::Engines { /** @@ -38,6 +42,9 @@ public: explicit KeplerMemory(Core::System& system_, MemoryManager& memory_manager); ~KeplerMemory() override; + /// Binds a rasterizer to this engine. + void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); + /// Write the value to the register identified by method. void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index b18b8a02a..5d6d217bb 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -31,6 +31,7 @@ Maxwell3D::~Maxwell3D() = default; void Maxwell3D::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_) { rasterizer = rasterizer_; + upload_state.BindRasterizer(rasterizer_); } void Maxwell3D::InitializeRegisterDefaults() { @@ -240,7 +241,7 @@ void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) ((method - MacroRegistersStart) >> 1) % static_cast<u32>(macro_positions.size()); // Execute the current macro. - macro_engine->Execute(*this, macro_positions[entry], parameters); + macro_engine->Execute(macro_positions[entry], parameters); if (mme_draw.current_mode != MMEDrawMode::Undefined) { FlushMMEInlineDraw(); } diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index f22342dfb..dc9df6c8b 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -1557,7 +1557,8 @@ private: static constexpr u32 null_cb_data = 0xFFFFFFFF; struct CBDataState { - std::array<std::array<u32, 0x4000>, 16> buffer; + static constexpr size_t inline_size = 0x4000; + std::array<std::array<u32, inline_size>, 16> buffer; u32 current{null_cb_data}; u32 id{null_cb_data}; u32 start_pos{}; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 705765c99..ba9ba082f 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -59,6 +59,7 @@ struct GPU::Impl { maxwell_3d->BindRasterizer(rasterizer); fermi_2d->BindRasterizer(rasterizer); kepler_compute->BindRasterizer(rasterizer); + kepler_memory->BindRasterizer(rasterizer); maxwell_dma->BindRasterizer(rasterizer); } @@ -502,8 +503,13 @@ struct GPU::Impl { case BufferMethods::SemaphoreAddressHigh: case BufferMethods::SemaphoreAddressLow: case BufferMethods::SemaphoreSequence: + break; case BufferMethods::UnkCacheFlush: + rasterizer->SyncGuestHost(); + break; case BufferMethods::WrcacheFlush: + rasterizer->SignalReference(); + break; case BufferMethods::FenceValue: break; case BufferMethods::RefCnt: @@ -513,7 +519,7 @@ struct GPU::Impl { ProcessFenceActionMethod(); break; case BufferMethods::WaitForInterrupt: - ProcessWaitForInterruptMethod(); + rasterizer->WaitForIdle(); break; case BufferMethods::SemaphoreTrigger: { ProcessSemaphoreTriggerMethod(); diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 3188b83ed..26b8ea233 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -12,9 +12,6 @@ #include "video_core/framebuffer_config.h" namespace Core { -namespace Frontend { -class EmuWindow; -} class System; } // namespace Core @@ -25,7 +22,6 @@ class ShaderNotify; namespace Tegra { class DmaPusher; -class CDmaPusher; struct CommandList; enum class RenderTargetFormat : u32 { @@ -88,15 +84,9 @@ enum class DepthFormat : u32 { D32_FLOAT_S8X24_UINT = 0x19, }; -struct CommandListHeader; -class DebugContext; - namespace Engines { -class Fermi2D; class Maxwell3D; -class MaxwellDMA; class KeplerCompute; -class KeplerMemory; } // namespace Engines enum class EngineID { @@ -190,12 +180,6 @@ public: /// Returns a const reference to the GPU DMA pusher. [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const; - /// Returns a reference to the GPU CDMA pusher. - [[nodiscard]] Tegra::CDmaPusher& CDmaPusher(); - - /// Returns a const reference to the GPU CDMA pusher. - [[nodiscard]] const Tegra::CDmaPusher& CDmaPusher() const; - /// Returns a reference to the underlying renderer. [[nodiscard]] VideoCore::RendererBase& Renderer(); diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp index d7fabe605..0aeda4ce8 100644 --- a/src/video_core/macro/macro.cpp +++ b/src/video_core/macro/macro.cpp @@ -2,12 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> #include <optional> + #include <boost/container_hash/hash.hpp> + #include "common/assert.h" -#include "common/logging/log.h" #include "common/settings.h" -#include "video_core/engines/maxwell_3d.h" #include "video_core/macro/macro.h" #include "video_core/macro/macro_hle.h" #include "video_core/macro/macro_interpreter.h" @@ -24,8 +25,7 @@ void MacroEngine::AddCode(u32 method, u32 data) { uploaded_macro_code[method].push_back(data); } -void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, - const std::vector<u32>& parameters) { +void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) { auto compiled_macro = macro_cache.find(method); if (compiled_macro != macro_cache.end()) { const auto& cache_info = compiled_macro->second; @@ -66,10 +66,9 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method, cache_info.lle_program = Compile(code); } - auto hle_program = hle_macros->GetHLEProgram(cache_info.hash); - if (hle_program.has_value()) { + if (auto hle_program = hle_macros->GetHLEProgram(cache_info.hash)) { cache_info.has_hle_program = true; - cache_info.hle_program = std::move(hle_program.value()); + cache_info.hle_program = std::move(hle_program); cache_info.hle_program->Execute(parameters, method); } else { cache_info.lle_program->Execute(parameters, method); diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h index 31ee3440a..7aaa49286 100644 --- a/src/video_core/macro/macro.h +++ b/src/video_core/macro/macro.h @@ -119,7 +119,7 @@ public: void AddCode(u32 method, u32 data); // Compiles the macro if its not in the cache, and executes the compiled macro - void Execute(Engines::Maxwell3D& maxwell3d, u32 method, const std::vector<u32>& parameters); + void Execute(u32 method, const std::vector<u32>& parameters); protected: virtual std::unique_ptr<CachedMacro> Compile(const std::vector<u32>& code) = 0; diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp index 70ac7c620..900ad23c9 100644 --- a/src/video_core/macro/macro_hle.cpp +++ b/src/video_core/macro/macro_hle.cpp @@ -5,12 +5,15 @@ #include <array> #include <vector> #include "video_core/engines/maxwell_3d.h" +#include "video_core/macro/macro.h" #include "video_core/macro/macro_hle.h" #include "video_core/rasterizer_interface.h" namespace Tegra { - namespace { + +using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters); + // HLE'd functions void HLE_771BB18C62444DA0(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) { const u32 instance_count = parameters[2] & maxwell3d.GetRegisterValue(0xD1B); @@ -77,7 +80,6 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& maxwell3d.CallMethodFromMME(0x8e5, 0x0); maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined; } -} // Anonymous namespace constexpr std::array<std::pair<u64, HLEFunction>, 3> hle_funcs{{ {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, @@ -85,25 +87,31 @@ constexpr std::array<std::pair<u64, HLEFunction>, 3> hle_funcs{{ {0x0217920100488FF7, &HLE_0217920100488FF7}, }}; +class HLEMacroImpl final : public CachedMacro { +public: + explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d_, HLEFunction func_) + : maxwell3d{maxwell3d_}, func{func_} {} + + void Execute(const std::vector<u32>& parameters, u32 method) override { + func(maxwell3d, parameters); + } + +private: + Engines::Maxwell3D& maxwell3d; + HLEFunction func; +}; +} // Anonymous namespace + HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {} HLEMacro::~HLEMacro() = default; -std::optional<std::unique_ptr<CachedMacro>> HLEMacro::GetHLEProgram(u64 hash) const { +std::unique_ptr<CachedMacro> HLEMacro::GetHLEProgram(u64 hash) const { const auto it = std::find_if(hle_funcs.cbegin(), hle_funcs.cend(), [hash](const auto& pair) { return pair.first == hash; }); if (it == hle_funcs.end()) { - return std::nullopt; + return nullptr; } return std::make_unique<HLEMacroImpl>(maxwell3d, it->second); } -HLEMacroImpl::~HLEMacroImpl() = default; - -HLEMacroImpl::HLEMacroImpl(Engines::Maxwell3D& maxwell3d_, HLEFunction func_) - : maxwell3d{maxwell3d_}, func{func_} {} - -void HLEMacroImpl::Execute(const std::vector<u32>& parameters, u32 method) { - func(maxwell3d, parameters); -} - } // namespace Tegra diff --git a/src/video_core/macro/macro_hle.h b/src/video_core/macro/macro_hle.h index cb3bd1600..b86ba84a1 100644 --- a/src/video_core/macro/macro_hle.h +++ b/src/video_core/macro/macro_hle.h @@ -5,10 +5,7 @@ #pragma once #include <memory> -#include <optional> -#include <vector> #include "common/common_types.h" -#include "video_core/macro/macro.h" namespace Tegra { @@ -16,29 +13,17 @@ namespace Engines { class Maxwell3D; } -using HLEFunction = void (*)(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters); - class HLEMacro { public: explicit HLEMacro(Engines::Maxwell3D& maxwell3d_); ~HLEMacro(); - std::optional<std::unique_ptr<CachedMacro>> GetHLEProgram(u64 hash) const; - -private: - Engines::Maxwell3D& maxwell3d; -}; - -class HLEMacroImpl : public CachedMacro { -public: - explicit HLEMacroImpl(Engines::Maxwell3D& maxwell3d, HLEFunction func); - ~HLEMacroImpl(); - - void Execute(const std::vector<u32>& parameters, u32 method) override; + // Allocates and returns a cached macro if the hash matches a known function. + // Returns nullptr otherwise. + [[nodiscard]] std::unique_ptr<CachedMacro> GetHLEProgram(u64 hash) const; private: Engines::Maxwell3D& maxwell3d; - HLEFunction func; }; } // namespace Tegra diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp index 8da26fd59..fba755448 100644 --- a/src/video_core/macro/macro_interpreter.cpp +++ b/src/video_core/macro/macro_interpreter.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> +#include <optional> + #include "common/assert.h" #include "common/logging/log.h" #include "common/microprofile.h" @@ -11,16 +14,81 @@ MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192)); namespace Tegra { -MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d_) - : MacroEngine{maxwell3d_}, maxwell3d{maxwell3d_} {} +namespace { +class MacroInterpreterImpl final : public CachedMacro { +public: + explicit MacroInterpreterImpl(Engines::Maxwell3D& maxwell3d_, const std::vector<u32>& code_) + : maxwell3d{maxwell3d_}, code{code_} {} -std::unique_ptr<CachedMacro> MacroInterpreter::Compile(const std::vector<u32>& code) { - return std::make_unique<MacroInterpreterImpl>(maxwell3d, code); -} + void Execute(const std::vector<u32>& params, u32 method) override; + +private: + /// Resets the execution engine state, zeroing registers, etc. + void Reset(); + + /** + * Executes a single macro instruction located at the current program counter. Returns whether + * the interpreter should keep running. + * + * @param is_delay_slot Whether the current step is being executed due to a delay slot in a + * previous instruction. + */ + bool Step(bool is_delay_slot); + + /// Calculates the result of an ALU operation. src_a OP src_b; + u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b); + + /// Performs the result operation on the input result and stores it in the specified register + /// (if necessary). + void ProcessResult(Macro::ResultOperation operation, u32 reg, u32 result); + + /// Evaluates the branch condition and returns whether the branch should be taken or not. + bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const; + + /// Reads an opcode at the current program counter location. + Macro::Opcode GetOpcode() const; + + /// Returns the specified register's value. Register 0 is hardcoded to always return 0. + u32 GetRegister(u32 register_id) const; + + /// Sets the register to the input value. + void SetRegister(u32 register_id, u32 value); + + /// Sets the method address to use for the next Send instruction. + void SetMethodAddress(u32 address); -MacroInterpreterImpl::MacroInterpreterImpl(Engines::Maxwell3D& maxwell3d_, - const std::vector<u32>& code_) - : maxwell3d{maxwell3d_}, code{code_} {} + /// Calls a GPU Engine method with the input parameter. + void Send(u32 value); + + /// Reads a GPU register located at the method address. + u32 Read(u32 method) const; + + /// Returns the next parameter in the parameter queue. + u32 FetchParameter(); + + Engines::Maxwell3D& maxwell3d; + + /// Current program counter + u32 pc{}; + /// Program counter to execute at after the delay slot is executed. + std::optional<u32> delayed_pc; + + /// General purpose macro registers. + std::array<u32, Macro::NUM_MACRO_REGISTERS> registers = {}; + + /// Method address to use for the next Send instruction. + Macro::MethodAddress method_address = {}; + + /// Input parameters of the current macro. + std::unique_ptr<u32[]> parameters; + std::size_t num_parameters = 0; + std::size_t parameters_capacity = 0; + /// Index of the next parameter that will be fetched by the 'parm' instruction. + u32 next_parameter_index = 0; + + bool carry_flag = false; + const std::vector<u32>& code; +}; void MacroInterpreterImpl::Execute(const std::vector<u32>& params, u32 method) { MICROPROFILE_SCOPE(MacroInterp); @@ -283,5 +351,13 @@ u32 MacroInterpreterImpl::FetchParameter() { ASSERT(next_parameter_index < num_parameters); return parameters[next_parameter_index++]; } +} // Anonymous namespace + +MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d_) + : MacroEngine{maxwell3d_}, maxwell3d{maxwell3d_} {} + +std::unique_ptr<CachedMacro> MacroInterpreter::Compile(const std::vector<u32>& code) { + return std::make_unique<MacroInterpreterImpl>(maxwell3d, code); +} } // namespace Tegra diff --git a/src/video_core/macro/macro_interpreter.h b/src/video_core/macro/macro_interpreter.h index d50c619ce..8a9648e46 100644 --- a/src/video_core/macro/macro_interpreter.h +++ b/src/video_core/macro/macro_interpreter.h @@ -3,10 +3,9 @@ // Refer to the license.txt file included. #pragma once -#include <array> -#include <optional> + #include <vector> -#include "common/bit_field.h" + #include "common/common_types.h" #include "video_core/macro/macro.h" @@ -26,77 +25,4 @@ private: Engines::Maxwell3D& maxwell3d; }; -class MacroInterpreterImpl : public CachedMacro { -public: - explicit MacroInterpreterImpl(Engines::Maxwell3D& maxwell3d_, const std::vector<u32>& code_); - void Execute(const std::vector<u32>& params, u32 method) override; - -private: - /// Resets the execution engine state, zeroing registers, etc. - void Reset(); - - /** - * Executes a single macro instruction located at the current program counter. Returns whether - * the interpreter should keep running. - * - * @param is_delay_slot Whether the current step is being executed due to a delay slot in a - * previous instruction. - */ - bool Step(bool is_delay_slot); - - /// Calculates the result of an ALU operation. src_a OP src_b; - u32 GetALUResult(Macro::ALUOperation operation, u32 src_a, u32 src_b); - - /// Performs the result operation on the input result and stores it in the specified register - /// (if necessary). - void ProcessResult(Macro::ResultOperation operation, u32 reg, u32 result); - - /// Evaluates the branch condition and returns whether the branch should be taken or not. - bool EvaluateBranchCondition(Macro::BranchCondition cond, u32 value) const; - - /// Reads an opcode at the current program counter location. - Macro::Opcode GetOpcode() const; - - /// Returns the specified register's value. Register 0 is hardcoded to always return 0. - u32 GetRegister(u32 register_id) const; - - /// Sets the register to the input value. - void SetRegister(u32 register_id, u32 value); - - /// Sets the method address to use for the next Send instruction. - void SetMethodAddress(u32 address); - - /// Calls a GPU Engine method with the input parameter. - void Send(u32 value); - - /// Reads a GPU register located at the method address. - u32 Read(u32 method) const; - - /// Returns the next parameter in the parameter queue. - u32 FetchParameter(); - - Engines::Maxwell3D& maxwell3d; - - /// Current program counter - u32 pc; - /// Program counter to execute at after the delay slot is executed. - std::optional<u32> delayed_pc; - - /// General purpose macro registers. - std::array<u32, Macro::NUM_MACRO_REGISTERS> registers = {}; - - /// Method address to use for the next Send instruction. - Macro::MethodAddress method_address = {}; - - /// Input parameters of the current macro. - std::unique_ptr<u32[]> parameters; - std::size_t num_parameters = 0; - std::size_t parameters_capacity = 0; - /// Index of the next parameter that will be fetched by the 'parm' instruction. - u32 next_parameter_index = 0; - - bool carry_flag = false; - const std::vector<u32>& code; -}; - } // namespace Tegra diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp index c6b2b2109..47b28ad16 100644 --- a/src/video_core/macro/macro_jit_x64.cpp +++ b/src/video_core/macro/macro_jit_x64.cpp @@ -2,9 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> +#include <bitset> +#include <optional> + +#include <xbyak/xbyak.h> + #include "common/assert.h" +#include "common/bit_field.h" #include "common/logging/log.h" #include "common/microprofile.h" +#include "common/x64/xbyak_abi.h" #include "common/x64/xbyak_util.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/macro/macro_interpreter.h" @@ -14,13 +22,14 @@ MICROPROFILE_DEFINE(MacroJitCompile, "GPU", "Compile macro JIT", MP_RGB(173, 255 MICROPROFILE_DEFINE(MacroJitExecute, "GPU", "Execute macro JIT", MP_RGB(255, 255, 0)); namespace Tegra { +namespace { constexpr Xbyak::Reg64 STATE = Xbyak::util::rbx; constexpr Xbyak::Reg32 RESULT = Xbyak::util::ebp; constexpr Xbyak::Reg64 PARAMETERS = Xbyak::util::r12; constexpr Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d; constexpr Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15; -static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({ +constexpr std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({ STATE, RESULT, PARAMETERS, @@ -28,19 +37,75 @@ static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({ BRANCH_HOLDER, }); -MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d_) - : MacroEngine{maxwell3d_}, maxwell3d{maxwell3d_} {} +// Arbitrarily chosen based on current booting games. +constexpr size_t MAX_CODE_SIZE = 0x10000; -std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) { - return std::make_unique<MacroJITx64Impl>(maxwell3d, code); +std::bitset<32> PersistentCallerSavedRegs() { + return PERSISTENT_REGISTERS & Common::X64::ABI_ALL_CALLER_SAVED; } -MacroJITx64Impl::MacroJITx64Impl(Engines::Maxwell3D& maxwell3d_, const std::vector<u32>& code_) - : CodeGenerator{MAX_CODE_SIZE}, code{code_}, maxwell3d{maxwell3d_} { - Compile(); -} +class MacroJITx64Impl final : public Xbyak::CodeGenerator, public CachedMacro { +public: + explicit MacroJITx64Impl(Engines::Maxwell3D& maxwell3d_, const std::vector<u32>& code_) + : CodeGenerator{MAX_CODE_SIZE}, code{code_}, maxwell3d{maxwell3d_} { + Compile(); + } + + void Execute(const std::vector<u32>& parameters, u32 method) override; + + void Compile_ALU(Macro::Opcode opcode); + void Compile_AddImmediate(Macro::Opcode opcode); + void Compile_ExtractInsert(Macro::Opcode opcode); + void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode); + void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode); + void Compile_Read(Macro::Opcode opcode); + void Compile_Branch(Macro::Opcode opcode); + +private: + void Optimizer_ScanFlags(); + + void Compile(); + bool Compile_NextInstruction(); + + Xbyak::Reg32 Compile_FetchParameter(); + Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst); + + void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg); + void Compile_Send(Xbyak::Reg32 value); -MacroJITx64Impl::~MacroJITx64Impl() = default; + Macro::Opcode GetOpCode() const; + + struct JITState { + Engines::Maxwell3D* maxwell3d{}; + std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{}; + u32 carry_flag{}; + }; + static_assert(offsetof(JITState, maxwell3d) == 0, "Maxwell3D is not at 0x0"); + using ProgramType = void (*)(JITState*, const u32*); + + struct OptimizerState { + bool can_skip_carry{}; + bool has_delayed_pc{}; + bool zero_reg_skip{}; + bool skip_dummy_addimmediate{}; + bool optimize_for_method_move{}; + bool enable_asserts{}; + }; + OptimizerState optimizer{}; + + std::optional<Macro::Opcode> next_opcode{}; + ProgramType program{nullptr}; + + std::array<Xbyak::Label, MAX_CODE_SIZE> labels; + std::array<Xbyak::Label, MAX_CODE_SIZE> delay_skip; + Xbyak::Label end_of_code{}; + + bool is_delay_slot{}; + u32 pc{}; + + const std::vector<u32>& code; + Engines::Maxwell3D& maxwell3d; +}; void MacroJITx64Impl::Execute(const std::vector<u32>& parameters, u32 method) { MICROPROFILE_SCOPE(MacroJitExecute); @@ -307,11 +372,11 @@ void MacroJITx64Impl::Compile_Read(Macro::Opcode opcode) { Compile_ProcessResult(opcode.result_operation, opcode.dst); } -static void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { +void Send(Engines::Maxwell3D* maxwell3d, Macro::MethodAddress method_address, u32 value) { maxwell3d->CallMethodFromMME(method_address.address, value); } -void Tegra::MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { +void MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); mov(Common::X64::ABI_PARAM1, qword[STATE]); mov(Common::X64::ABI_PARAM2, METHOD_ADDRESS); @@ -338,7 +403,7 @@ void Tegra::MacroJITx64Impl::Compile_Send(Xbyak::Reg32 value) { L(dont_process); } -void Tegra::MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) { +void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) { ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid"); const s32 jump_address = static_cast<s32>(pc) + static_cast<s32>(opcode.GetBranchTarget() / sizeof(s32)); @@ -392,7 +457,7 @@ void Tegra::MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) { L(end); } -void Tegra::MacroJITx64Impl::Optimizer_ScanFlags() { +void MacroJITx64Impl::Optimizer_ScanFlags() { optimizer.can_skip_carry = true; optimizer.has_delayed_pc = false; for (auto raw_op : code) { @@ -534,7 +599,7 @@ bool MacroJITx64Impl::Compile_NextInstruction() { return true; } -Xbyak::Reg32 Tegra::MacroJITx64Impl::Compile_FetchParameter() { +Xbyak::Reg32 MacroJITx64Impl::Compile_FetchParameter() { mov(eax, dword[PARAMETERS]); add(PARAMETERS, sizeof(u32)); return eax; @@ -611,9 +676,12 @@ Macro::Opcode MacroJITx64Impl::GetOpCode() const { ASSERT(pc < code.size()); return {code[pc]}; } +} // Anonymous namespace -std::bitset<32> MacroJITx64Impl::PersistentCallerSavedRegs() const { - return PERSISTENT_REGISTERS & Common::X64::ABI_ALL_CALLER_SAVED; -} +MacroJITx64::MacroJITx64(Engines::Maxwell3D& maxwell3d_) + : MacroEngine{maxwell3d_}, maxwell3d{maxwell3d_} {} +std::unique_ptr<CachedMacro> MacroJITx64::Compile(const std::vector<u32>& code) { + return std::make_unique<MacroJITx64Impl>(maxwell3d, code); +} } // namespace Tegra diff --git a/src/video_core/macro/macro_jit_x64.h b/src/video_core/macro/macro_jit_x64.h index d03d480b4..773b037ae 100644 --- a/src/video_core/macro/macro_jit_x64.h +++ b/src/video_core/macro/macro_jit_x64.h @@ -4,12 +4,7 @@ #pragma once -#include <array> -#include <bitset> -#include <xbyak/xbyak.h> -#include "common/bit_field.h" #include "common/common_types.h" -#include "common/x64/xbyak_abi.h" #include "video_core/macro/macro.h" namespace Tegra { @@ -18,9 +13,6 @@ namespace Engines { class Maxwell3D; } -/// MAX_CODE_SIZE is arbitrarily chosen based on current booting games -constexpr size_t MAX_CODE_SIZE = 0x10000; - class MacroJITx64 final : public MacroEngine { public: explicit MacroJITx64(Engines::Maxwell3D& maxwell3d_); @@ -32,67 +24,4 @@ private: Engines::Maxwell3D& maxwell3d; }; -class MacroJITx64Impl : public Xbyak::CodeGenerator, public CachedMacro { -public: - explicit MacroJITx64Impl(Engines::Maxwell3D& maxwell3d_, const std::vector<u32>& code_); - ~MacroJITx64Impl(); - - void Execute(const std::vector<u32>& parameters, u32 method) override; - - void Compile_ALU(Macro::Opcode opcode); - void Compile_AddImmediate(Macro::Opcode opcode); - void Compile_ExtractInsert(Macro::Opcode opcode); - void Compile_ExtractShiftLeftImmediate(Macro::Opcode opcode); - void Compile_ExtractShiftLeftRegister(Macro::Opcode opcode); - void Compile_Read(Macro::Opcode opcode); - void Compile_Branch(Macro::Opcode opcode); - -private: - void Optimizer_ScanFlags(); - - void Compile(); - bool Compile_NextInstruction(); - - Xbyak::Reg32 Compile_FetchParameter(); - Xbyak::Reg32 Compile_GetRegister(u32 index, Xbyak::Reg32 dst); - - void Compile_ProcessResult(Macro::ResultOperation operation, u32 reg); - void Compile_Send(Xbyak::Reg32 value); - - Macro::Opcode GetOpCode() const; - std::bitset<32> PersistentCallerSavedRegs() const; - - struct JITState { - Engines::Maxwell3D* maxwell3d{}; - std::array<u32, Macro::NUM_MACRO_REGISTERS> registers{}; - u32 carry_flag{}; - }; - static_assert(offsetof(JITState, maxwell3d) == 0, "Maxwell3D is not at 0x0"); - using ProgramType = void (*)(JITState*, const u32*); - - struct OptimizerState { - bool can_skip_carry{}; - bool has_delayed_pc{}; - bool zero_reg_skip{}; - bool skip_dummy_addimmediate{}; - bool optimize_for_method_move{}; - bool enable_asserts{}; - }; - OptimizerState optimizer{}; - - std::optional<Macro::Opcode> next_opcode{}; - ProgramType program{nullptr}; - - std::array<Xbyak::Label, MAX_CODE_SIZE> labels; - std::array<Xbyak::Label, MAX_CODE_SIZE> delay_skip; - Xbyak::Label end_of_code{}; - - bool is_delay_slot{}; - u32 pc{}; - std::optional<u32> delayed_pc; - - const std::vector<u32>& code; - Engines::Maxwell3D& maxwell3d; -}; - } // namespace Tegra diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 38d8d9d74..61bfe47c7 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -143,6 +143,8 @@ public: [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align); void Unmap(GPUVAddr gpu_addr, std::size_t size); + void FlushRegion(GPUVAddr gpu_addr, size_t size) const; + private: [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const; void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size); @@ -153,8 +155,6 @@ private: void TryLockPage(PageEntry page_entry, std::size_t size); void TryUnlockPage(PageEntry page_entry, std::size_t size); - void FlushRegion(GPUVAddr gpu_addr, size_t size) const; - void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, bool is_safe) const; void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index b094fc064..1f1f12291 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -123,6 +123,9 @@ public: [[nodiscard]] virtual Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() = 0; + virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span<u8> memory) = 0; + /// Attempt to use a faster method to display the framebuffer to screen [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 9b516c64f..142412a8e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -484,6 +484,28 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA() return accelerate_dma; } +void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span<u8> memory) { + auto cpu_addr = gpu_memory.GpuToCpuAddress(address); + if (!cpu_addr) [[unlikely]] { + gpu_memory.WriteBlock(address, memory.data(), copy_size); + return; + } + gpu_memory.WriteBlockUnsafe(address, memory.data(), copy_size); + { + std::unique_lock<std::mutex> lock{buffer_cache.mutex}; + if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { + buffer_cache.WriteMemory(*cpu_addr, copy_size); + } + } + { + std::scoped_lock lock_texture{texture_cache.mutex}; + texture_cache.WriteMemory(*cpu_addr, copy_size); + } + shader_cache.InvalidateRegion(*cpu_addr, copy_size); + query_cache.InvalidateRegion(*cpu_addr, copy_size); +} + bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) { if (framebuffer_addr == 0) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index d0397b745..98f6fd342 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -106,6 +106,8 @@ public: const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) override; Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; + void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span<u8> memory) override; bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp index 73629d229..b630090e8 100644 --- a/src/video_core/renderer_vulkan/vk_fsr.cpp +++ b/src/video_core/renderer_vulkan/vk_fsr.cpp @@ -214,7 +214,7 @@ VkImageView FSR::Draw(VKScheduler& scheduler, size_t image_index, VkImageView im { VkImageMemoryBarrier fsr_write_barrier = base_barrier; - fsr_write_barrier.image = *images[image_index], + fsr_write_barrier.image = *images[image_index]; fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index fd334a146..2227d9197 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -548,6 +548,28 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA() return accelerate_dma; } +void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span<u8> memory) { + auto cpu_addr = gpu_memory.GpuToCpuAddress(address); + if (!cpu_addr) [[unlikely]] { + gpu_memory.WriteBlock(address, memory.data(), copy_size); + return; + } + gpu_memory.WriteBlockUnsafe(address, memory.data(), copy_size); + { + std::unique_lock<std::mutex> lock{buffer_cache.mutex}; + if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { + buffer_cache.WriteMemory(*cpu_addr, copy_size); + } + } + { + std::scoped_lock lock_texture{texture_cache.mutex}; + texture_cache.WriteMemory(*cpu_addr, copy_size); + } + pipeline_cache.InvalidateRegion(*cpu_addr, copy_size); + query_cache.InvalidateRegion(*cpu_addr, copy_size); +} + bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) { if (!framebuffer_addr) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 866827247..5af2e275b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -99,6 +99,8 @@ public: const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) override; Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; + void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, + std::span<u8> memory) override; bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index 78bf90c48..87636857d 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -170,7 +170,7 @@ void ShaderCache::RemovePendingShaders() { marked_for_removal.clear(); if (!removed_shaders.empty()) { - RemoveShadersFromStorage(std::move(removed_shaders)); + RemoveShadersFromStorage(removed_shaders); } } @@ -213,7 +213,7 @@ void ShaderCache::UnmarkMemory(Entry* entry) { rasterizer.UpdatePagesCachedCount(addr, size, -1); } -void ShaderCache::RemoveShadersFromStorage(std::vector<ShaderInfo*> removed_shaders) { +void ShaderCache::RemoveShadersFromStorage(std::span<ShaderInfo*> removed_shaders) { // Remove them from the cache std::erase_if(storage, [&removed_shaders](const std::unique_ptr<ShaderInfo>& shader) { return std::ranges::find(removed_shaders, shader.get()) != removed_shaders.end(); diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index 136fe294c..8836bc8c6 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h @@ -4,7 +4,6 @@ #pragma once -#include <algorithm> #include <array> #include <memory> #include <mutex> @@ -138,7 +137,7 @@ private: /// @param removed_shaders Shaders to be removed from the storage /// @pre invalidation_mutex is locked /// @pre lookup_mutex is locked - void RemoveShadersFromStorage(std::vector<ShaderInfo*> removed_shaders); + void RemoveShadersFromStorage(std::span<ShaderInfo*> removed_shaders); /// @brief Creates a new entry in the lookup cache and returns its pointer /// @pre lookup_mutex is locked diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 7e8f92840..2c8c10c50 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -393,6 +393,8 @@ void Config::ReadControlValues() { ReadGlobalSetting(Settings::values.enable_accurate_vibrations); ReadGlobalSetting(Settings::values.motion_enabled); + ReadBasicSetting(Settings::values.controller_navigation); + qt_config->endGroup(); } @@ -1001,6 +1003,7 @@ void Config::SaveControlValues() { WriteBasicSetting(Settings::values.keyboard_enabled); WriteBasicSetting(Settings::values.emulate_analog_keyboard); WriteBasicSetting(Settings::values.mouse_panning_sensitivity); + WriteBasicSetting(Settings::values.controller_navigation); WriteBasicSetting(Settings::values.tas_enable); WriteBasicSetting(Settings::values.tas_loop); diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 9241678e4..74f0e0b79 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -429,7 +429,7 @@ </item> <item> <property name="text"> - <string>AMD FidelityFX™️ Super Resolution [Vulkan Only]</string> + <string>AMD FidelityFX™️ Super Resolution (Vulkan Only)</string> </property> </item> </widget> diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 65c8e59ac..20fc2599d 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -131,6 +131,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() { Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked(); + Settings::values.controller_navigation = ui->controller_navigation->isChecked(); } void ConfigureInputAdvanced::LoadConfiguration() { @@ -162,6 +163,7 @@ void ConfigureInputAdvanced::LoadConfiguration() { ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue()); + ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); UpdateUIEnabled(); } diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index df0e4d602..66f2075f2 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -2655,6 +2655,19 @@ </widget> </item> <item row="4" column="0"> + <widget class="QCheckBox" name="controller_navigation"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>23</height> + </size> + </property> + <property name="text"> + <string>Controller navigation</string> + </property> + </widget> + </item> + <item row="5" column="0"> <widget class="QCheckBox" name="mouse_panning"> <property name="minimumSize"> <size> @@ -2667,7 +2680,7 @@ </property> </widget> </item> - <item row="4" column="2"> + <item row="5" column="2"> <widget class="QSpinBox" name="mouse_panning_sensitivity"> <property name="toolTip"> <string>Mouse sensitivity</string> @@ -2689,14 +2702,14 @@ </property> </widget> </item> - <item row="5" column="0"> + <item row="6" column="0"> <widget class="QLabel" name="motion_touch"> <property name="text"> <string>Motion / Touch</string> </property> </widget> </item> - <item row="5" column="2"> + <item row="6" column="2"> <widget class="QPushButton" name="buttonMotionTouch"> <property name="text"> <string>Configure</string> diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index d2132b408..ba563b382 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -147,7 +147,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) { // Retrieve the names from Qt if (param.Get("engine", "") == "keyboard") { const QString button_str = GetKeyName(param.Get("code", 0)); - return QObject::tr("%1%2").arg(toggle, button_str); + return QObject::tr("%1%2%3").arg(toggle, inverted, button_str); } if (common_button_name == Common::Input::ButtonNames::Invalid) { @@ -326,7 +326,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i connect(button, &QPushButton::clicked, [=, this] { HandleClick( button, button_id, - [=, this](Common::ParamPackage params) { + [=, this](const Common::ParamPackage& params) { emulated_controller->SetButtonParam(button_id, params); }, InputCommon::Polling::InputType::Button); @@ -341,7 +341,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i emulated_controller->SetButtonParam(button_id, {}); button_map[button_id]->setText(tr("[not set]")); }); - if (param.Has("button") || param.Has("hat")) { + if (param.Has("code") || param.Has("button") || param.Has("hat")) { context_menu.addAction(tr("Toggle button"), [&] { const bool toggle_value = !param.Get("toggle", false); param.Set("toggle", toggle_value); @@ -349,8 +349,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i emulated_controller->SetButtonParam(button_id, param); }); context_menu.addAction(tr("Invert button"), [&] { - const bool toggle_value = !param.Get("inverted", false); - param.Set("inverted", toggle_value); + const bool invert_value = !param.Get("inverted", false); + param.Set("inverted", invert_value); button_map[button_id]->setText(ButtonToText(param)); emulated_controller->SetButtonParam(button_id, param); }); @@ -392,7 +392,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i connect(button, &QPushButton::clicked, [=, this] { HandleClick( button, motion_id, - [=, this](Common::ParamPackage params) { + [=, this](const Common::ParamPackage& params) { emulated_controller->SetMotionParam(motion_id, params); }, InputCommon::Polling::InputType::Motion); @@ -403,10 +403,22 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i connect(button, &QPushButton::customContextMenuRequested, [=, this](const QPoint& menu_location) { QMenu context_menu; + Common::ParamPackage param = emulated_controller->GetMotionParam(motion_id); context_menu.addAction(tr("Clear"), [&] { emulated_controller->SetMotionParam(motion_id, {}); motion_map[motion_id]->setText(tr("[not set]")); }); + if (param.Has("motion")) { + context_menu.addAction(tr("Set gyro threshold"), [&] { + const int gyro_threshold = + static_cast<int>(param.Get("threshold", 0.007f) * 1000.0f); + const int new_threshold = QInputDialog::getInt( + this, tr("Set threshold"), tr("Choose a value between 0% and 100%"), + gyro_threshold, 0, 100); + param.Set("threshold", new_threshold / 1000.0f); + emulated_controller->SetMotionParam(motion_id, param); + }); + } context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location)); }); } @@ -485,10 +497,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i param.Set("invert_y", invert_str); emulated_controller->SetStickParam(analog_id, param); } - for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; - ++sub_button_id) { - analog_map_buttons[analog_id][sub_button_id]->setText( - AnalogToText(param, analog_sub_buttons[sub_button_id])); + for (int analog_sub_button_id = 0; + analog_sub_button_id < ANALOG_SUB_BUTTONS_NUM; + ++analog_sub_button_id) { + analog_map_buttons[analog_id][analog_sub_button_id]->setText( + AnalogToText(param, analog_sub_buttons[analog_sub_button_id])); } }); context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal( @@ -510,28 +523,37 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu); - connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested, - [=, this](const QPoint& menu_location) { - QMenu context_menu; - Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); - context_menu.addAction(tr("Clear"), [&] { - param.Set("modifier", ""); - analog_map_modifier_button[analog_id]->setText(tr("[not set]")); - emulated_controller->SetStickParam(analog_id, param); - }); - context_menu.addAction(tr("Toggle button"), [&] { - Common::ParamPackage modifier_param = - Common::ParamPackage{param.Get("modifier", "")}; - const bool toggle_value = !modifier_param.Get("toggle", false); - modifier_param.Set("toggle", toggle_value); - param.Set("modifier", modifier_param.Serialize()); - analog_map_modifier_button[analog_id]->setText( - ButtonToText(modifier_param)); - emulated_controller->SetStickParam(analog_id, param); - }); - context_menu.exec( - analog_map_modifier_button[analog_id]->mapToGlobal(menu_location)); + connect( + analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested, + [=, this](const QPoint& menu_location) { + QMenu context_menu; + Common::ParamPackage param = emulated_controller->GetStickParam(analog_id); + context_menu.addAction(tr("Clear"), [&] { + param.Set("modifier", ""); + analog_map_modifier_button[analog_id]->setText(tr("[not set]")); + emulated_controller->SetStickParam(analog_id, param); }); + context_menu.addAction(tr("Toggle button"), [&] { + Common::ParamPackage modifier_param = + Common::ParamPackage{param.Get("modifier", "")}; + const bool toggle_value = !modifier_param.Get("toggle", false); + modifier_param.Set("toggle", toggle_value); + param.Set("modifier", modifier_param.Serialize()); + analog_map_modifier_button[analog_id]->setText(ButtonToText(modifier_param)); + emulated_controller->SetStickParam(analog_id, param); + }); + context_menu.addAction(tr("Invert button"), [&] { + Common::ParamPackage modifier_param = + Common::ParamPackage{param.Get("modifier", "")}; + const bool invert_value = !modifier_param.Get("inverted", false); + modifier_param.Set("inverted", invert_value); + param.Set("modifier", modifier_param.Serialize()); + analog_map_modifier_button[analog_id]->setText(ButtonToText(modifier_param)); + emulated_controller->SetStickParam(analog_id, param); + }); + context_menu.exec( + analog_map_modifier_button[analog_id]->mapToGlobal(menu_location)); + }); connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged), [=, this] { @@ -762,7 +784,7 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { if (devices.size() == 1) { const auto devices_it = std::find_if( input_devices.begin(), input_devices.end(), - [first_engine, first_guid, first_port, first_pad](const Common::ParamPackage param) { + [first_engine, first_guid, first_port, first_pad](const Common::ParamPackage& param) { return param.Get("engine", "") == first_engine && param.Get("guid", "") == first_guid && param.Get("port", 0) == first_port && param.Get("pad", 0) == first_pad; @@ -793,7 +815,7 @@ void ConfigureInputPlayer::UpdateInputDeviceCombobox() { if (is_engine_equal && is_port_equal) { const auto devices_it = std::find_if( input_devices.begin(), input_devices.end(), - [first_engine, first_guid, second_guid, first_port](const Common::ParamPackage param) { + [first_engine, first_guid, second_guid, first_port](const Common::ParamPackage& param) { const bool is_guid_valid = (param.Get("guid", "") == first_guid && param.Get("guid2", "") == second_guid) || @@ -1005,7 +1027,7 @@ int ConfigureInputPlayer::GetIndexFromControllerType(Core::HID::NpadStyleIndex t void ConfigureInputPlayer::UpdateInputDevices() { input_devices = input_subsystem->GetInputDevices(); ui->comboDevices->clear(); - for (auto device : input_devices) { + for (const auto& device : input_devices) { ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {}); } } @@ -1287,7 +1309,7 @@ void ConfigureInputPlayer::HandleClick( } button->setFocus(); - input_setter = new_input_setter; + input_setter = std::move(new_input_setter); input_subsystem->BeginMapping(type); @@ -1337,7 +1359,7 @@ bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse"; } - const auto current_input_device = input_devices[ui->comboDevices->currentIndex()]; + const auto& current_input_device = input_devices[ui->comboDevices->currentIndex()]; return params.Get("engine", "") == current_input_device.Get("engine", "") && (params.Get("guid", "") == current_input_device.Get("guid", "") || params.Get("guid", "") == current_input_device.Get("guid2", "")) && diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index 8539a5c8b..4340de304 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -42,23 +42,25 @@ CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent, job = std::make_unique<CalibrationConfigurationJob>( host, port, [this](CalibrationConfigurationJob::Status status) { - QString text; - switch (status) { - case CalibrationConfigurationJob::Status::Ready: - text = tr("Touch the top left corner <br>of your touchpad."); - break; - case CalibrationConfigurationJob::Status::Stage1Completed: - text = tr("Now touch the bottom right corner <br>of your touchpad."); - break; - case CalibrationConfigurationJob::Status::Completed: - text = tr("Configuration completed!"); - break; - default: - break; - } - QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text)); + QMetaObject::invokeMethod(this, [status, this] { + QString text; + switch (status) { + case CalibrationConfigurationJob::Status::Ready: + text = tr("Touch the top left corner <br>of your touchpad."); + break; + case CalibrationConfigurationJob::Status::Stage1Completed: + text = tr("Now touch the bottom right corner <br>of your touchpad."); + break; + case CalibrationConfigurationJob::Status::Completed: + text = tr("Configuration completed!"); + break; + default: + break; + } + UpdateLabelText(text); + }); if (status == CalibrationConfigurationJob::Status::Completed) { - QMetaObject::invokeMethod(this, "UpdateButtonText", Q_ARG(QString, tr("OK"))); + QMetaObject::invokeMethod(this, [this] { UpdateButtonText(tr("OK")); }); } }, [this](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) { @@ -215,11 +217,11 @@ void ConfigureMotionTouch::OnCemuhookUDPTest() { ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()), [this] { LOG_INFO(Frontend, "UDP input test success"); - QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true)); + QMetaObject::invokeMethod(this, [this] { ShowUDPTestResult(true); }); }, [this] { LOG_ERROR(Frontend, "UDP input test failed"); - QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, false)); + QMetaObject::invokeMethod(this, [this] { ShowUDPTestResult(false); }); }); } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 8b5c4a10a..e3661b390 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -161,7 +161,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { * @return true if the haystack contains all words of userinput */ static bool ContainsAllWords(const QString& haystack, const QString& userinput) { - const QStringList userinput_split = userinput.split(QLatin1Char{' '}, QString::SkipEmptyParts); + const QStringList userinput_split = userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts); return std::all_of(userinput_split.begin(), userinput_split.end(), [&haystack](const QString& s) { return haystack.contains(s); }); diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index d96497c4e..6ed9611c7 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp @@ -190,6 +190,9 @@ void ControllerShortcut::ControllerUpdateEvent(Core::HID::ControllerTriggerType if (type != Core::HID::ControllerTriggerType::Button) { return; } + if (!Settings::values.controller_navigation) { + return; + } if (button_sequence.npad.raw == Core::HID::NpadButton::None && button_sequence.capture.raw == 0 && button_sequence.home.raw == 0) { return; diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp index 86fb28b9f..c2b13123d 100644 --- a/src/yuzu/util/controller_navigation.cpp +++ b/src/yuzu/util/controller_navigation.cpp @@ -40,6 +40,9 @@ void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_b void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) { std::lock_guard lock{mutex}; + if (!Settings::values.controller_navigation) { + return; + } if (type == Core::HID::ControllerTriggerType::Button) { ControllerUpdateButton(); return; |