diff options
98 files changed, 1385 insertions, 705 deletions
diff --git a/externals/dynarmic b/externals/dynarmic -Subproject af2d50288fc537201014c4230bb55ab9018a743 +Subproject 644172477eaf0d822178cb7e96c62b75caa9657 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1bdf70b76..9182dbfd4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,12 +65,14 @@ if (MSVC) /we4305 # 'context': truncation from 'type1' to 'type2' /we4388 # 'expression': signed/unsigned mismatch /we4389 # 'operator': signed/unsigned mismatch + /we4505 # 'function': unreferenced local function has been removed /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? /we4555 # Expression has no effect; expected expression with side-effect /we4715 # 'function': not all control paths return a value /we4834 # Discarding return value of function with 'nodiscard' attribute /we5038 # data member 'member1' will be initialized after data member 'member2' + /we5245 # 'function': unreferenced function with internal linkage has been removed ) if (ARCHITECTURE_x86_64) @@ -103,12 +105,6 @@ else() -Wno-unused-parameter ) - # TODO: Remove when we update to a GCC compiler that enables this - # by default (i.e. GCC 10 or newer). - if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) - add_compile_options(-fconcepts) - endif() - if (ARCHITECTURE_x86_64) add_compile_options("-mcx16") endif() diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index e829af1ac..802943eb7 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -149,7 +149,7 @@ public: } void Unmap(size_t virtual_offset, size_t length) { - std::lock_guard lock{placeholder_mutex}; + std::scoped_lock lock{placeholder_mutex}; // Unmap until there are no more placeholders while (UnmapOnePlaceholder(virtual_offset, length)) { @@ -169,7 +169,7 @@ public: } const size_t virtual_end = virtual_offset + length; - std::lock_guard lock{placeholder_mutex}; + std::scoped_lock lock{placeholder_mutex}; auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end}); while (it != end) { const size_t offset = std::max(it->lower(), virtual_offset); diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 9120cc178..4acbff649 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -101,6 +101,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Service, GRC) \ SUB(Service, HID) \ SUB(Service, IRS) \ + SUB(Service, JIT) \ SUB(Service, LBL) \ SUB(Service, LDN) \ SUB(Service, LDR) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index f803ab796..99c15fa96 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -69,6 +69,7 @@ enum class Class : u8 { Service_GRC, ///< The game recording service Service_HID, ///< The HID (Human interface device) service Service_IRS, ///< The IRS service + Service_JIT, ///< The JIT service Service_LBL, ///< The LBL (LCD backlight) service Service_LDN, ///< The LDN (Local domain network) service Service_LDR, ///< The loader service diff --git a/src/common/settings.h b/src/common/settings.h index a37d83fb3..86e0fa140 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -38,6 +38,7 @@ enum class CPUAccuracy : u32 { Auto = 0, Accurate = 1, Unsafe = 2, + Paranoid = 3, }; enum class FullscreenMode : u32 { @@ -470,7 +471,7 @@ struct Values { // Cpu RangedSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto, - CPUAccuracy::Unsafe, "cpu_accuracy"}; + CPUAccuracy::Paranoid, "cpu_accuracy"}; // TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021 BasicSetting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"}; BasicSetting<bool> cpu_debug_mode{false, "cpu_debug_mode"}; diff --git a/src/common/thread.h b/src/common/thread.h index a8c17c71a..626609372 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -17,7 +17,7 @@ namespace Common { class Event { public: void Set() { - std::lock_guard lk{mutex}; + std::scoped_lock lk{mutex}; if (!is_set) { is_set = true; condvar.notify_one(); diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h index 2c8c2b90e..7272ac6e8 100644 --- a/src/common/threadsafe_queue.h +++ b/src/common/threadsafe_queue.h @@ -52,7 +52,7 @@ public: // line before cv.wait // TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported. // See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details. - std::lock_guard lock{cv_mutex}; + std::scoped_lock lock{cv_mutex}; cv.notify_one(); } @@ -159,7 +159,7 @@ public: template <typename Arg> void Push(Arg&& t) { - std::lock_guard lock{write_lock}; + std::scoped_lock lock{write_lock}; spsc_queue.Push(t); } diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 7a3f21dcf..7fd9d22f8 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -10,25 +10,49 @@ #include "common/uint128.h" #include "common/x64/native_clock.h" +#ifdef _MSC_VER +#include <intrin.h> +#endif + namespace Common { +#ifdef _MSC_VER +__forceinline static u64 FencedRDTSC() { + _mm_lfence(); + _ReadWriteBarrier(); + const u64 result = __rdtsc(); + _mm_lfence(); + _ReadWriteBarrier(); + return result; +} +#else +static u64 FencedRDTSC() { + u64 result; + asm volatile("lfence\n\t" + "rdtsc\n\t" + "shl $32, %%rdx\n\t" + "or %%rdx, %0\n\t" + "lfence" + : "=a"(result) + : + : "rdx", "memory", "cc"); + return result; +} +#endif + u64 EstimateRDTSCFrequency() { // Discard the first result measuring the rdtsc. - _mm_mfence(); - __rdtsc(); + FencedRDTSC(); std::this_thread::sleep_for(std::chrono::milliseconds{1}); - _mm_mfence(); - __rdtsc(); + FencedRDTSC(); // Get the current time. const auto start_time = std::chrono::steady_clock::now(); - _mm_mfence(); - const u64 tsc_start = __rdtsc(); + const u64 tsc_start = FencedRDTSC(); // Wait for 200 milliseconds. std::this_thread::sleep_for(std::chrono::milliseconds{200}); const auto end_time = std::chrono::steady_clock::now(); - _mm_mfence(); - const u64 tsc_end = __rdtsc(); + const u64 tsc_end = FencedRDTSC(); // Calculate differences. const u64 timer_diff = static_cast<u64>( std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count()); @@ -42,8 +66,7 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen u64 rtsc_frequency_) : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{ rtsc_frequency_} { - _mm_mfence(); - time_point.inner.last_measure = __rdtsc(); + time_point.inner.last_measure = FencedRDTSC(); time_point.inner.accumulated_ticks = 0U; ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency); us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency); @@ -58,8 +81,7 @@ u64 NativeClock::GetRTSC() { current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); do { - _mm_mfence(); - const u64 current_measure = __rdtsc(); + const u64 current_measure = FencedRDTSC(); u64 diff = current_measure - current_time_point.inner.last_measure; diff = diff & ~static_cast<u64>(static_cast<s64>(diff) >> 63); // max(diff, 0) new_time_point.inner.last_measure = current_measure > current_time_point.inner.last_measure @@ -80,8 +102,7 @@ void NativeClock::Pause(bool is_paused) { current_time_point.pack = Common::AtomicLoad128(time_point.pack.data()); do { new_time_point.pack = current_time_point.pack; - _mm_mfence(); - new_time_point.inner.last_measure = __rdtsc(); + new_time_point.inner.last_measure = FencedRDTSC(); } while (!Common::AtomicCompareAndSwap(time_point.pack.data(), new_time_point.pack, current_time_point.pack, current_time_point.pack)); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 6536d0544..ffbda7925 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -13,6 +13,8 @@ add_library(core STATIC arm/dynarmic/arm_exclusive_monitor.h arm/exclusive_monitor.cpp arm/exclusive_monitor.h + arm/symbols.cpp + arm/symbols.h constants.cpp constants.h core.cpp @@ -458,6 +460,8 @@ add_library(core STATIC hle/service/hid/controllers/touchscreen.h hle/service/hid/controllers/xpad.cpp hle/service/hid/controllers/xpad.h + hle/service/jit/jit.cpp + hle/service/jit/jit.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h hle/service/ldn/errors.h diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index 0951e1976..08bf1201d 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -8,134 +8,13 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" +#include "core/arm/symbols.h" #include "core/core.h" +#include "core/hle/kernel/k_process.h" #include "core/loader/loader.h" #include "core/memory.h" namespace Core { -namespace { - -constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; -constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; -constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; -constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; - -enum class ELFSymbolType : u8 { - None = 0, - Object = 1, - Function = 2, - Section = 3, - File = 4, - Common = 5, - TLS = 6, -}; - -enum class ELFSymbolBinding : u8 { - Local = 0, - Global = 1, - Weak = 2, -}; - -enum class ELFSymbolVisibility : u8 { - Default = 0, - Internal = 1, - Hidden = 2, - Protected = 3, -}; - -struct ELFSymbol { - u32 name_index; - union { - u8 info; - - BitField<0, 4, ELFSymbolType> type; - BitField<4, 4, ELFSymbolBinding> binding; - }; - ELFSymbolVisibility visibility; - u16 sh_index; - u64 value; - u64 size; -}; -static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); - -using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; - -Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) { - const auto mod_offset = text_offset + memory.Read32(text_offset + 4); - - if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || - memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { - return {}; - } - - const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset; - - VAddr string_table_offset{}; - VAddr symbol_table_offset{}; - u64 symbol_entry_size{}; - - VAddr dynamic_index = dynamic_offset; - while (true) { - const u64 tag = memory.Read64(dynamic_index); - const u64 value = memory.Read64(dynamic_index + 0x8); - dynamic_index += 0x10; - - if (tag == ELF_DYNAMIC_TAG_NULL) { - break; - } - - if (tag == ELF_DYNAMIC_TAG_STRTAB) { - string_table_offset = value; - } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { - symbol_table_offset = value; - } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { - symbol_entry_size = value; - } - } - - if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { - return {}; - } - - const auto string_table_address = text_offset + string_table_offset; - const auto symbol_table_address = text_offset + symbol_table_offset; - - Symbols out; - - VAddr symbol_index = symbol_table_address; - while (symbol_index < string_table_address) { - ELFSymbol symbol{}; - memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol)); - - VAddr string_offset = string_table_address + symbol.name_index; - std::string name; - for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) { - name += static_cast<char>(c); - } - - symbol_index += symbol_entry_size; - out.push_back({symbol, name}); - } - - return out; -} - -std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) { - const auto iter = - std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) { - const auto& symbol = pair.first; - const auto end_address = symbol.value + symbol.size; - return func_address >= symbol.value && func_address < end_address; - }); - - if (iter == symbols.end()) { - return std::nullopt; - } - - return iter->second; -} - -} // Anonymous namespace constexpr u64 SEGMENT_BASE = 0x7100000000ull; @@ -169,9 +48,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex return {}; } - std::map<std::string, Symbols> symbols; + std::map<std::string, Symbols::Symbols> symbols; for (const auto& module : modules) { - symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); + symbols.insert_or_assign(module.second, + Symbols::GetSymbols(module.first, system.Memory(), + system.CurrentProcess()->Is64BitProcess())); } for (auto& entry : out) { @@ -193,7 +74,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex const auto symbol_set = symbols.find(entry.module); if (symbol_set != symbols.end()) { - const auto symbol = GetSymbolName(symbol_set->second, entry.offset); + const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); if (symbol.has_value()) { // TODO(DarkLordZach): Add demangling of symbol names. entry.name = *symbol; @@ -225,9 +106,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { return {}; } - std::map<std::string, Symbols> symbols; + std::map<std::string, Symbols::Symbols> symbols; for (const auto& module : modules) { - symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); + symbols.insert_or_assign(module.second, + Symbols::GetSymbols(module.first, system.Memory(), + system.CurrentProcess()->Is64BitProcess())); } for (auto& entry : out) { @@ -249,7 +132,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { const auto symbol_set = symbols.find(entry.module); if (symbol_set != symbols.end()) { - const auto symbol = GetSymbolName(symbol_set->second, entry.offset); + const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); if (symbol.has_value()) { // TODO(DarkLordZach): Add demangling of symbol names. entry.name = *symbol; diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index c60322442..dce2f4195 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -171,6 +171,9 @@ public: /// Prepare core for thread reschedule (if needed to correctly handle state) virtual void PrepareReschedule() = 0; + /// Signal an interrupt and ask the core to halt as soon as possible. + virtual void SignalInterrupt() = 0; + struct BacktraceEntry { std::string module; u64 address; diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 286976623..ab3210d84 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -25,6 +25,9 @@ namespace Core { using namespace Common::Literals; +constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; +constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; + class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { public: explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) @@ -70,11 +73,13 @@ public: } void InterpreterFallback(u32 pc, std::size_t num_instructions) override { + parent.LogBacktrace(); UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, MemoryReadCode(pc)); } void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { + parent.LogBacktrace(); LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); @@ -82,15 +87,13 @@ public: } void CallSVC(u32 swi) override { - parent.svc_called = true; parent.svc_swi = swi; - parent.jit->HaltExecution(); + parent.jit->HaltExecution(svc_call); } void AddTicks(u64 ticks) override { - if (parent.uses_wall_clock) { - return; - } + ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a // rough approximation of the amount of executed ticks in the system, it may be thrown off // if not all cores are doing a similar amount of work. Instead of doing this, we should @@ -106,12 +109,8 @@ public: } u64 GetTicksRemaining() override { - if (parent.uses_wall_clock) { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return minimum_run_cycles; - } - return 0U; - } + ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); } @@ -146,6 +145,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* // Timing config.wall_clock_cntpct = uses_wall_clock; + config.enable_cycle_counting = !uses_wall_clock; // Code cache size config.code_cache_size = 512_MiB; @@ -186,35 +186,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* if (!Settings::values.cpuopt_recompile_exclusives) { config.recompile_on_exclusive_fastmem_failure = false; } - } + } else { + // Unsafe optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; + } + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + } + if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + } + } - // Unsafe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { - config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma) { + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - } - if (Settings::values.cpuopt_unsafe_reduce_fp_error) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; - } - if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; - } - if (Settings::values.cpuopt_unsafe_inaccurate_nan) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; - } - if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; } - } - // Curated optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { - config.unsafe_optimizations = true; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + // Paranoia mode for debugging optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { + config.unsafe_optimizations = false; + config.optimizations = Dynarmic::no_optimizations; + } } return std::make_unique<Dynarmic::A32::Jit>(config); @@ -222,13 +228,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* void ARM_Dynarmic_32::Run() { while (true) { - jit->Run(); - if (!svc_called) { - break; + const auto hr = jit->Run(); + if (Has(hr, svc_call)) { + Kernel::Svc::Call(system, svc_swi); } - svc_called = false; - Kernel::Svc::Call(system, svc_swi); - if (shutdown) { + if (Has(hr, break_loop)) { break; } } @@ -314,8 +318,11 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { } void ARM_Dynarmic_32::PrepareReschedule() { - jit->HaltExecution(); - shutdown = true; + jit->HaltExecution(break_loop); +} + +void ARM_Dynarmic_32::SignalInterrupt() { + jit->HaltExecution(break_loop); } void ARM_Dynarmic_32::ClearInstructionCache() { diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 5d47b600d..3f68a4ff1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -57,6 +57,7 @@ public: void LoadContext(const ThreadContext64& ctx) override {} void PrepareReschedule() override; + void SignalInterrupt() override; void ClearExclusiveState() override; void ClearInstructionCache() override; @@ -83,9 +84,6 @@ private: // SVC callback u32 svc_swi{}; - bool svc_called{}; - - bool shutdown{}; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 24107f9f6..68822a1fc 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -26,6 +26,9 @@ namespace Core { using Vector = Dynarmic::A64::Vector; using namespace Common::Literals; +constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2; +constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3; + class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { public: explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) @@ -81,6 +84,7 @@ public: } void InterpreterFallback(u64 pc, std::size_t num_instructions) override { + parent.LogBacktrace(); LOG_ERROR(Core_ARM, "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, num_instructions, MemoryReadCode(pc)); @@ -105,7 +109,7 @@ public: break; } - parent.jit->HaltExecution(); + parent.jit->HaltExecution(Dynarmic::HaltReason::CacheInvalidation); } void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { @@ -118,21 +122,19 @@ public: return; case Dynarmic::A64::Exception::Breakpoint: default: + parent.LogBacktrace(); ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); } } void CallSVC(u32 swi) override { - parent.svc_called = true; parent.svc_swi = swi; - parent.jit->HaltExecution(); + parent.jit->HaltExecution(svc_call); } void AddTicks(u64 ticks) override { - if (parent.uses_wall_clock) { - return; - } + ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a // rough approximation of the amount of executed ticks in the system, it may be thrown off @@ -147,12 +149,8 @@ public: } u64 GetTicksRemaining() override { - if (parent.uses_wall_clock) { - if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { - return minimum_run_cycles; - } - return 0U; - } + ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled"); + return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); } @@ -208,6 +206,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* // Timing config.wall_clock_cntpct = uses_wall_clock; + config.enable_cycle_counting = !uses_wall_clock; // Code cache size config.code_cache_size = 512_MiB; @@ -248,35 +247,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* if (!Settings::values.cpuopt_recompile_exclusives) { config.recompile_on_exclusive_fastmem_failure = false; } - } + } else { + // Unsafe optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + } + if (Settings::values.cpuopt_unsafe_fastmem_check) { + config.fastmem_address_space_bits = 64; + } + if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + } + } - // Unsafe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { - config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma) { + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - } - if (Settings::values.cpuopt_unsafe_reduce_fp_error) { - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; - } - if (Settings::values.cpuopt_unsafe_inaccurate_nan) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; - } - if (Settings::values.cpuopt_unsafe_fastmem_check) { config.fastmem_address_space_bits = 64; - } - if (Settings::values.cpuopt_unsafe_ignore_global_monitor) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; } - } - // Curated optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { - config.unsafe_optimizations = true; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; - config.fastmem_address_space_bits = 64; - config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; + // Paranoia mode for debugging optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) { + config.unsafe_optimizations = false; + config.optimizations = Dynarmic::no_optimizations; + } } return std::make_shared<Dynarmic::A64::Jit>(config); @@ -284,13 +289,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* void ARM_Dynarmic_64::Run() { while (true) { - jit->Run(); - if (!svc_called) { - break; + const auto hr = jit->Run(); + if (Has(hr, svc_call)) { + Kernel::Svc::Call(system, svc_swi); } - svc_called = false; - Kernel::Svc::Call(system, svc_swi); - if (shutdown) { + if (Has(hr, break_loop)) { break; } } @@ -381,8 +384,11 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { } void ARM_Dynarmic_64::PrepareReschedule() { - jit->HaltExecution(); - shutdown = true; + jit->HaltExecution(break_loop); +} + +void ARM_Dynarmic_64::SignalInterrupt() { + jit->HaltExecution(break_loop); } void ARM_Dynarmic_64::ClearInstructionCache() { diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index 0c4e46c64..58bc7fbec 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -51,6 +51,7 @@ public: void LoadContext(const ThreadContext64& ctx) override; void PrepareReschedule() override; + void SignalInterrupt() override; void ClearExclusiveState() override; void ClearInstructionCache() override; @@ -77,9 +78,6 @@ private: // SVC callback u32 svc_swi{}; - bool svc_called{}; - - bool shutdown{}; }; } // namespace Core diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp new file mode 100644 index 000000000..26c44f0c7 --- /dev/null +++ b/src/core/arm/symbols.cpp @@ -0,0 +1,190 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "core/arm/symbols.h" +#include "core/core.h" +#include "core/memory.h" + +namespace Core { +namespace { + +constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; +constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; +constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; +constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; + +enum class ELFSymbolType : u8 { + None = 0, + Object = 1, + Function = 2, + Section = 3, + File = 4, + Common = 5, + TLS = 6, +}; + +enum class ELFSymbolBinding : u8 { + Local = 0, + Global = 1, + Weak = 2, +}; + +enum class ELFSymbolVisibility : u8 { + Default = 0, + Internal = 1, + Hidden = 2, + Protected = 3, +}; + +struct ELF64Symbol { + u32 name_index; + union { + u8 info; + + BitField<0, 4, ELFSymbolType> type; + BitField<4, 4, ELFSymbolBinding> binding; + }; + ELFSymbolVisibility visibility; + u16 sh_index; + u64 value; + u64 size; +}; +static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size."); + +struct ELF32Symbol { + u32 name_index; + u32 value; + u32 size; + union { + u8 info; + + BitField<0, 4, ELFSymbolType> type; + BitField<4, 4, ELFSymbolBinding> binding; + }; + ELFSymbolVisibility visibility; + u16 sh_index; +}; +static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size."); + +} // Anonymous namespace + +namespace Symbols { + +template <typename Word, typename ELFSymbol, typename ByteReader> +static Symbols GetSymbols(ByteReader ReadBytes) { + const auto Read8{[&](u64 index) { + u8 ret; + ReadBytes(&ret, index, sizeof(u8)); + return ret; + }}; + + const auto Read32{[&](u64 index) { + u32 ret; + ReadBytes(&ret, index, sizeof(u32)); + return ret; + }}; + + const auto ReadWord{[&](u64 index) { + Word ret; + ReadBytes(&ret, index, sizeof(Word)); + return ret; + }}; + + const u32 mod_offset = Read32(4); + + if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) { + return {}; + } + + VAddr string_table_offset{}; + VAddr symbol_table_offset{}; + u64 symbol_entry_size{}; + + const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset; + + VAddr dynamic_index = dynamic_offset; + while (true) { + const Word tag = ReadWord(dynamic_index); + const Word value = ReadWord(dynamic_index + sizeof(Word)); + dynamic_index += 2 * sizeof(Word); + + if (tag == ELF_DYNAMIC_TAG_NULL) { + break; + } + + if (tag == ELF_DYNAMIC_TAG_STRTAB) { + string_table_offset = value; + } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { + symbol_table_offset = value; + } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { + symbol_entry_size = value; + } + } + + if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) { + return {}; + } + + Symbols out; + + VAddr symbol_index = symbol_table_offset; + while (symbol_index < string_table_offset) { + ELFSymbol symbol{}; + ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol)); + + VAddr string_offset = string_table_offset + symbol.name_index; + std::string name; + for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) { + name += static_cast<char>(c); + } + + symbol_index += symbol_entry_size; + out[name] = std::make_pair(symbol.value, symbol.size); + } + + return out; +} + +Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) { + const auto ReadBytes{ + [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }}; + + if (is_64) { + return GetSymbols<u64, ELF64Symbol>(ReadBytes); + } else { + return GetSymbols<u32, ELF32Symbol>(ReadBytes); + } +} + +Symbols GetSymbols(std::span<const u8> data, bool is_64) { + const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) { + std::memcpy(ptr, data.data() + offset, size); + }}; + + if (is_64) { + return GetSymbols<u64, ELF64Symbol>(ReadBytes); + } else { + return GetSymbols<u32, ELF32Symbol>(ReadBytes); + } +} + +std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) { + const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) { + const auto& [name, sym_info] = pair; + const auto& [start_address, size] = sym_info; + const auto end_address = start_address + size; + return addr >= start_address && addr < end_address; + }); + + if (iter == symbols.cend()) { + return std::nullopt; + } + + return iter->first; +} + +} // namespace Symbols +} // namespace Core diff --git a/src/core/arm/symbols.h b/src/core/arm/symbols.h new file mode 100644 index 000000000..99e6a9f8e --- /dev/null +++ b/src/core/arm/symbols.h @@ -0,0 +1,27 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <map> +#include <optional> +#include <span> +#include <string> +#include <utility> + +#include "common/common_types.h" + +namespace Core::Memory { +class Memory; +} // namespace Core::Memory + +namespace Core::Symbols { + +using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>; + +Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true); +Symbols GetSymbols(std::span<const u8> data, bool is_64 = true); +std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr); + +} // namespace Core::Symbols diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index c4e185757..73e724f3d 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -148,29 +148,33 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { // LayeredExeFS const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); + const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); + + std::vector<VirtualDir> patch_dirs = {sdmc_load_dir}; if (load_dir != nullptr && load_dir->GetSize() > 0) { - auto patch_dirs = load_dir->GetSubdirectories(); - std::sort( - patch_dirs.begin(), patch_dirs.end(), - [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); - - std::vector<VirtualDir> layers; - layers.reserve(patch_dirs.size() + 1); - for (const auto& subdir : patch_dirs) { - if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) - continue; + const auto load_patch_dirs = load_dir->GetSubdirectories(); + patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end()); + } - auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); - if (exefs_dir != nullptr) - layers.push_back(std::move(exefs_dir)); - } - layers.push_back(exefs); + std::sort(patch_dirs.begin(), patch_dirs.end(), + [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); - auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); - if (layered != nullptr) { - LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); - exefs = std::move(layered); - } + std::vector<VirtualDir> layers; + layers.reserve(patch_dirs.size() + 1); + for (const auto& subdir : patch_dirs) { + if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) + continue; + + auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); + if (exefs_dir != nullptr) + layers.push_back(std::move(exefs_dir)); + } + layers.push_back(exefs); + + auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); + if (layered != nullptr) { + LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); + exefs = std::move(layered); } if (Settings::values.dump_exefs) { @@ -536,11 +540,20 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u // SDMC mod directory (RomFS LayeredFS) const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); - if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 && - IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { - const auto mod_disabled = - std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); - out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS"); + if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) { + std::string types; + if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) { + AppendCommaIfNotEmpty(types, "LayeredExeFS"); + } + if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { + AppendCommaIfNotEmpty(types, "LayeredFS"); + } + + if (!types.empty()) { + const auto mod_disabled = + std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); + out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types); + } } // DLC diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 7a646b5f1..4ada4a69b 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -387,15 +387,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const { continue; for (const auto& nca_dir : d2_dir->GetSubdirectories()) { - if (!FollowsNcaIdFormat(nca_dir->GetName())) + if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) { continue; + } ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20))); } for (const auto& nca_file : d2_dir->GetFiles()) { - if (!FollowsNcaIdFormat(nca_file->GetName())) + if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) { continue; + } ids.push_back( Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20))); diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp index eef0ff493..de565048b 100644 --- a/src/core/hid/emulated_console.cpp +++ b/src/core/hid/emulated_console.cpp @@ -132,7 +132,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) { } void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; auto& raw_status = console.motion_values.raw_status; auto& emulated = console.motion_values.emulated; @@ -151,6 +151,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { emulated.UpdateOrientation(raw_status.delta_timestamp); if (is_configuring) { + lock.unlock(); TriggerOnChange(ConsoleTriggerType::Motion); return; } @@ -166,6 +167,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { // Find what is this value motion.verticalization_error = 0.0f; + lock.unlock(); TriggerOnChange(ConsoleTriggerType::Motion); } @@ -173,11 +175,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st if (index >= console.touch_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; console.touch_values[index] = TransformToTouch(callback); if (is_configuring) { + lock.unlock(); TriggerOnChange(ConsoleTriggerType::Touch); return; } @@ -189,26 +192,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st .pressed = console.touch_values[index].pressed.value, }; + lock.unlock(); TriggerOnChange(ConsoleTriggerType::Touch); } ConsoleMotionValues EmulatedConsole::GetMotionValues() const { + std::scoped_lock lock{mutex}; return console.motion_values; } TouchValues EmulatedConsole::GetTouchValues() const { + std::scoped_lock lock{mutex}; return console.touch_values; } ConsoleMotion EmulatedConsole::GetMotion() const { + std::scoped_lock lock{mutex}; return console.motion_state; } TouchFingerState EmulatedConsole::GetTouch() const { + std::scoped_lock lock{mutex}; return console.touch_state; } void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { + std::scoped_lock lock{callback_mutex}; for (const auto& poller_pair : callback_list) { const ConsoleUpdateCallback& poller = poller_pair.second; if (poller.on_change) { @@ -218,13 +227,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { } int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; callback_list.insert_or_assign(last_callback_key, update_callback); return last_callback_key++; } void EmulatedConsole::DeleteCallback(int key) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; const auto& iterator = callback_list.find(key); if (iterator == callback_list.end()) { LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h index 5eb170823..53677bdc5 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h @@ -183,6 +183,7 @@ private: TouchDevices touch_devices; mutable std::mutex mutex; + mutable std::mutex callback_mutex; std::unordered_map<int, ConsoleUpdateCallback> callback_list; int last_callback_key = 0; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 7e05666d6..c3f21066c 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -353,14 +353,17 @@ void EmulatedController::DisableConfiguration() { } void EmulatedController::EnableSystemButtons() { + std::scoped_lock lock{mutex}; system_buttons_enabled = true; } void EmulatedController::DisableSystemButtons() { + std::scoped_lock lock{mutex}; system_buttons_enabled = false; } void EmulatedController::ResetSystemButtons() { + std::scoped_lock lock{mutex}; controller.home_button_state.home.Assign(false); controller.capture_button_state.capture.Assign(false); } @@ -494,139 +497,141 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback if (index >= controller.button_values.size()) { return; } - { - std::lock_guard lock{mutex}; - bool value_changed = false; - const auto new_status = TransformToButton(callback); - auto& current_status = controller.button_values[index]; + std::unique_lock lock{mutex}; + bool value_changed = false; + const auto new_status = TransformToButton(callback); + auto& current_status = controller.button_values[index]; - // Only read button values that have the same uuid or are pressed once - if (current_status.uuid != uuid) { - if (!new_status.value) { - return; - } + // Only read button values that have the same uuid or are pressed once + if (current_status.uuid != uuid) { + if (!new_status.value) { + return; } + } - current_status.toggle = new_status.toggle; - current_status.uuid = uuid; - - // Update button status with current - if (!current_status.toggle) { - current_status.locked = false; - if (current_status.value != new_status.value) { - current_status.value = new_status.value; - value_changed = true; - } - } else { - // Toggle button and lock status - if (new_status.value && !current_status.locked) { - current_status.locked = true; - current_status.value = !current_status.value; - value_changed = true; - } + current_status.toggle = new_status.toggle; + current_status.uuid = uuid; - // Unlock button ready for next press - if (!new_status.value && current_status.locked) { - current_status.locked = false; - } + // Update button status with current + if (!current_status.toggle) { + current_status.locked = false; + if (current_status.value != new_status.value) { + current_status.value = new_status.value; + value_changed = true; } - - if (!value_changed) { - return; + } else { + // Toggle button and lock status + if (new_status.value && !current_status.locked) { + current_status.locked = true; + current_status.value = !current_status.value; + value_changed = true; } - if (is_configuring) { - controller.npad_button_state.raw = NpadButton::None; - controller.debug_pad_button_state.raw = 0; - TriggerOnChange(ControllerTriggerType::Button, false); - return; + // Unlock button ready for next press + if (!new_status.value && current_status.locked) { + current_status.locked = false; } + } - switch (index) { - case Settings::NativeButton::A: - controller.npad_button_state.a.Assign(current_status.value); - controller.debug_pad_button_state.a.Assign(current_status.value); - break; - case Settings::NativeButton::B: - controller.npad_button_state.b.Assign(current_status.value); - controller.debug_pad_button_state.b.Assign(current_status.value); - break; - case Settings::NativeButton::X: - controller.npad_button_state.x.Assign(current_status.value); - controller.debug_pad_button_state.x.Assign(current_status.value); - break; - case Settings::NativeButton::Y: - controller.npad_button_state.y.Assign(current_status.value); - controller.debug_pad_button_state.y.Assign(current_status.value); - break; - case Settings::NativeButton::LStick: - controller.npad_button_state.stick_l.Assign(current_status.value); - break; - case Settings::NativeButton::RStick: - controller.npad_button_state.stick_r.Assign(current_status.value); - break; - case Settings::NativeButton::L: - controller.npad_button_state.l.Assign(current_status.value); - controller.debug_pad_button_state.l.Assign(current_status.value); - break; - case Settings::NativeButton::R: - controller.npad_button_state.r.Assign(current_status.value); - controller.debug_pad_button_state.r.Assign(current_status.value); - break; - case Settings::NativeButton::ZL: - controller.npad_button_state.zl.Assign(current_status.value); - controller.debug_pad_button_state.zl.Assign(current_status.value); - break; - case Settings::NativeButton::ZR: - controller.npad_button_state.zr.Assign(current_status.value); - controller.debug_pad_button_state.zr.Assign(current_status.value); - break; - case Settings::NativeButton::Plus: - controller.npad_button_state.plus.Assign(current_status.value); - controller.debug_pad_button_state.plus.Assign(current_status.value); - break; - case Settings::NativeButton::Minus: - controller.npad_button_state.minus.Assign(current_status.value); - controller.debug_pad_button_state.minus.Assign(current_status.value); - break; - case Settings::NativeButton::DLeft: - controller.npad_button_state.left.Assign(current_status.value); - controller.debug_pad_button_state.d_left.Assign(current_status.value); - break; - case Settings::NativeButton::DUp: - controller.npad_button_state.up.Assign(current_status.value); - controller.debug_pad_button_state.d_up.Assign(current_status.value); - break; - case Settings::NativeButton::DRight: - controller.npad_button_state.right.Assign(current_status.value); - controller.debug_pad_button_state.d_right.Assign(current_status.value); - break; - case Settings::NativeButton::DDown: - controller.npad_button_state.down.Assign(current_status.value); - controller.debug_pad_button_state.d_down.Assign(current_status.value); - break; - case Settings::NativeButton::SL: - controller.npad_button_state.left_sl.Assign(current_status.value); - controller.npad_button_state.right_sl.Assign(current_status.value); - break; - case Settings::NativeButton::SR: - controller.npad_button_state.left_sr.Assign(current_status.value); - controller.npad_button_state.right_sr.Assign(current_status.value); - break; - case Settings::NativeButton::Home: - if (!system_buttons_enabled) { - break; - } - controller.home_button_state.home.Assign(current_status.value); + if (!value_changed) { + return; + } + + if (is_configuring) { + controller.npad_button_state.raw = NpadButton::None; + controller.debug_pad_button_state.raw = 0; + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Button, false); + return; + } + + switch (index) { + case Settings::NativeButton::A: + controller.npad_button_state.a.Assign(current_status.value); + controller.debug_pad_button_state.a.Assign(current_status.value); + break; + case Settings::NativeButton::B: + controller.npad_button_state.b.Assign(current_status.value); + controller.debug_pad_button_state.b.Assign(current_status.value); + break; + case Settings::NativeButton::X: + controller.npad_button_state.x.Assign(current_status.value); + controller.debug_pad_button_state.x.Assign(current_status.value); + break; + case Settings::NativeButton::Y: + controller.npad_button_state.y.Assign(current_status.value); + controller.debug_pad_button_state.y.Assign(current_status.value); + break; + case Settings::NativeButton::LStick: + controller.npad_button_state.stick_l.Assign(current_status.value); + break; + case Settings::NativeButton::RStick: + controller.npad_button_state.stick_r.Assign(current_status.value); + break; + case Settings::NativeButton::L: + controller.npad_button_state.l.Assign(current_status.value); + controller.debug_pad_button_state.l.Assign(current_status.value); + break; + case Settings::NativeButton::R: + controller.npad_button_state.r.Assign(current_status.value); + controller.debug_pad_button_state.r.Assign(current_status.value); + break; + case Settings::NativeButton::ZL: + controller.npad_button_state.zl.Assign(current_status.value); + controller.debug_pad_button_state.zl.Assign(current_status.value); + break; + case Settings::NativeButton::ZR: + controller.npad_button_state.zr.Assign(current_status.value); + controller.debug_pad_button_state.zr.Assign(current_status.value); + break; + case Settings::NativeButton::Plus: + controller.npad_button_state.plus.Assign(current_status.value); + controller.debug_pad_button_state.plus.Assign(current_status.value); + break; + case Settings::NativeButton::Minus: + controller.npad_button_state.minus.Assign(current_status.value); + controller.debug_pad_button_state.minus.Assign(current_status.value); + break; + case Settings::NativeButton::DLeft: + controller.npad_button_state.left.Assign(current_status.value); + controller.debug_pad_button_state.d_left.Assign(current_status.value); + break; + case Settings::NativeButton::DUp: + controller.npad_button_state.up.Assign(current_status.value); + controller.debug_pad_button_state.d_up.Assign(current_status.value); + break; + case Settings::NativeButton::DRight: + controller.npad_button_state.right.Assign(current_status.value); + controller.debug_pad_button_state.d_right.Assign(current_status.value); + break; + case Settings::NativeButton::DDown: + controller.npad_button_state.down.Assign(current_status.value); + controller.debug_pad_button_state.d_down.Assign(current_status.value); + break; + case Settings::NativeButton::SL: + controller.npad_button_state.left_sl.Assign(current_status.value); + controller.npad_button_state.right_sl.Assign(current_status.value); + break; + case Settings::NativeButton::SR: + controller.npad_button_state.left_sr.Assign(current_status.value); + controller.npad_button_state.right_sr.Assign(current_status.value); + break; + case Settings::NativeButton::Home: + if (!system_buttons_enabled) { break; - case Settings::NativeButton::Screenshot: - if (!system_buttons_enabled) { - break; - } - controller.capture_button_state.capture.Assign(current_status.value); + } + controller.home_button_state.home.Assign(current_status.value); + break; + case Settings::NativeButton::Screenshot: + if (!system_buttons_enabled) { break; } + controller.capture_button_state.capture.Assign(current_status.value); + break; } + + lock.unlock(); + if (!is_connected) { if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) { Connect(); @@ -643,7 +648,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, if (index >= controller.stick_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; const auto stick_value = TransformToStick(callback); // Only read stick values that have the same uuid or are over the threshold to avoid flapping @@ -659,6 +664,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, if (is_configuring) { controller.analog_stick_state.left = {}; controller.analog_stick_state.right = {}; + lock.unlock(); TriggerOnChange(ControllerTriggerType::Stick, false); return; } @@ -685,6 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback, break; } + lock.unlock(); TriggerOnChange(ControllerTriggerType::Stick, true); } @@ -693,7 +700,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac if (index >= controller.trigger_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; const auto trigger_value = TransformToTrigger(callback); // Only read trigger values that have the same uuid or are pressed once @@ -709,6 +716,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac if (is_configuring) { controller.gc_trigger_state.left = 0; controller.gc_trigger_state.right = 0; + lock.unlock(); TriggerOnChange(ControllerTriggerType::Trigger, false); return; } @@ -727,6 +735,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac break; } + lock.unlock(); TriggerOnChange(ControllerTriggerType::Trigger, true); } @@ -735,7 +744,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback if (index >= controller.motion_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; auto& raw_status = controller.motion_values[index].raw_status; auto& emulated = controller.motion_values[index].emulated; @@ -756,6 +765,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback force_update_motion = raw_status.force_update; if (is_configuring) { + lock.unlock(); TriggerOnChange(ControllerTriggerType::Motion, false); return; } @@ -767,6 +777,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback motion.orientation = emulated.GetOrientation(); motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); + lock.unlock(); TriggerOnChange(ControllerTriggerType::Motion, true); } @@ -775,10 +786,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac if (index >= controller.battery_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; controller.battery_values[index] = TransformToBattery(callback); if (is_configuring) { + lock.unlock(); TriggerOnChange(ControllerTriggerType::Battery, false); return; } @@ -835,6 +847,8 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac }; break; } + + lock.unlock(); TriggerOnChange(ControllerTriggerType::Battery, true); } @@ -932,6 +946,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles) } bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { + std::scoped_lock lock{mutex}; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { case NpadStyleIndex::ProController: @@ -947,6 +962,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { } bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { + std::scoped_lock lock{mutex}; const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; switch (type) { case NpadStyleIndex::ProController: @@ -982,40 +998,44 @@ void EmulatedController::Connect(bool use_temporary_value) { LOG_ERROR(Service_HID, "Controller type {} is not supported", type); return; } - { - std::lock_guard lock{mutex}; - if (is_configuring) { - tmp_is_connected = true; - TriggerOnChange(ControllerTriggerType::Connected, false); - return; - } - if (is_connected) { - return; - } - is_connected = true; + std::unique_lock lock{mutex}; + if (is_configuring) { + tmp_is_connected = true; + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Connected, false); + return; + } + + if (is_connected) { + return; } + is_connected = true; + + lock.unlock(); TriggerOnChange(ControllerTriggerType::Connected, true); } void EmulatedController::Disconnect() { - { - std::lock_guard lock{mutex}; - if (is_configuring) { - tmp_is_connected = false; - TriggerOnChange(ControllerTriggerType::Disconnected, false); - return; - } + std::unique_lock lock{mutex}; + if (is_configuring) { + tmp_is_connected = false; + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Disconnected, false); + return; + } - if (!is_connected) { - return; - } - is_connected = false; + if (!is_connected) { + return; } + is_connected = false; + + lock.unlock(); TriggerOnChange(ControllerTriggerType::Disconnected, true); } bool EmulatedController::IsConnected(bool get_temporary_value) const { + std::scoped_lock lock{mutex}; if (get_temporary_value && is_configuring) { return tmp_is_connected; } @@ -1029,10 +1049,12 @@ bool EmulatedController::IsVibrationEnabled() const { } NpadIdType EmulatedController::GetNpadIdType() const { + std::scoped_lock lock{mutex}; return npad_id_type; } NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { + std::scoped_lock lock{mutex}; if (get_temporary_value && is_configuring) { return tmp_npad_type; } @@ -1040,27 +1062,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c } void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { - { - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; - if (is_configuring) { - if (tmp_npad_type == npad_type_) { - return; - } - tmp_npad_type = npad_type_; - TriggerOnChange(ControllerTriggerType::Type, false); + if (is_configuring) { + if (tmp_npad_type == npad_type_) { return; } + tmp_npad_type = npad_type_; + lock.unlock(); + TriggerOnChange(ControllerTriggerType::Type, false); + return; + } - if (npad_type == npad_type_) { - return; - } - if (is_connected) { - LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", - NpadIdTypeToIndex(npad_id_type)); - } - npad_type = npad_type_; + if (npad_type == npad_type_) { + return; + } + if (is_connected) { + LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", + NpadIdTypeToIndex(npad_id_type)); } + npad_type = npad_type_; + + lock.unlock(); TriggerOnChange(ControllerTriggerType::Type, true); } @@ -1088,30 +1111,37 @@ LedPattern EmulatedController::GetLedPattern() const { } ButtonValues EmulatedController::GetButtonsValues() const { + std::scoped_lock lock{mutex}; return controller.button_values; } SticksValues EmulatedController::GetSticksValues() const { + std::scoped_lock lock{mutex}; return controller.stick_values; } TriggerValues EmulatedController::GetTriggersValues() const { + std::scoped_lock lock{mutex}; return controller.trigger_values; } ControllerMotionValues EmulatedController::GetMotionValues() const { + std::scoped_lock lock{mutex}; return controller.motion_values; } ColorValues EmulatedController::GetColorsValues() const { + std::scoped_lock lock{mutex}; return controller.color_values; } BatteryValues EmulatedController::GetBatteryValues() const { + std::scoped_lock lock{mutex}; return controller.battery_values; } HomeButtonState EmulatedController::GetHomeButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1119,6 +1149,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const { } CaptureButtonState EmulatedController::GetCaptureButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1126,6 +1157,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const { } NpadButtonState EmulatedController::GetNpadButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1133,6 +1165,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const { } DebugPadButton EmulatedController::GetDebugPadButtons() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1140,20 +1173,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const { } AnalogSticks EmulatedController::GetSticks() const { + std::unique_lock lock{mutex}; + if (is_configuring) { return {}; } + // Some drivers like stick from buttons need constant refreshing for (auto& device : stick_devices) { if (!device) { continue; } + lock.unlock(); device->SoftUpdate(); + lock.lock(); } + return controller.analog_stick_state; } NpadGcTriggerState EmulatedController::GetTriggers() const { + std::scoped_lock lock{mutex}; if (is_configuring) { return {}; } @@ -1161,26 +1201,35 @@ NpadGcTriggerState EmulatedController::GetTriggers() const { } MotionState EmulatedController::GetMotions() const { + std::unique_lock lock{mutex}; + + // Some drivers like mouse motion need constant refreshing if (force_update_motion) { for (auto& device : motion_devices) { if (!device) { continue; } + lock.unlock(); device->ForceUpdate(); + lock.lock(); } } + return controller.motion_state; } ControllerColors EmulatedController::GetColors() const { + std::scoped_lock lock{mutex}; return controller.colors_state; } BatteryLevelState EmulatedController::GetBattery() const { + std::scoped_lock lock{mutex}; return controller.battery_state; } void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { + std::scoped_lock lock{callback_mutex}; for (const auto& poller_pair : callback_list) { const ControllerUpdateCallback& poller = poller_pair.second; if (!is_npad_service_update && poller.is_npad_service) { @@ -1193,13 +1242,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa } int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); return last_callback_key++; } void EmulatedController::DeleteCallback(int key) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; const auto& iterator = callback_list.find(key); if (iterator == callback_list.end()) { LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index aa52f9572..1e224685d 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -400,7 +400,7 @@ private: */ void TriggerOnChange(ControllerTriggerType type, bool is_service_update); - NpadIdType npad_id_type; + const NpadIdType npad_id_type; NpadStyleIndex npad_type{NpadStyleIndex::None}; NpadStyleTag supported_style_tag{NpadStyleSet::All}; bool is_connected{false}; @@ -434,6 +434,7 @@ private: StickDevices tas_stick_devices; mutable std::mutex mutex; + mutable std::mutex callback_mutex; std::unordered_map<int, ControllerUpdateCallback> callback_list; int last_callback_key = 0; diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp index 708480f2d..cc0dcd931 100644 --- a/src/core/hid/emulated_devices.cpp +++ b/src/core/hid/emulated_devices.cpp @@ -169,7 +169,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal if (index >= device_status.keyboard_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; bool value_changed = false; const auto new_status = TransformToButton(callback); auto& current_status = device_status.keyboard_values[index]; @@ -201,6 +201,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal } if (is_configuring) { + lock.unlock(); TriggerOnChange(DeviceTriggerType::Keyboard); return; } @@ -208,6 +209,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal // Index should be converted from NativeKeyboard to KeyboardKeyIndex UpdateKey(index, current_status.value); + lock.unlock(); TriggerOnChange(DeviceTriggerType::Keyboard); } @@ -227,7 +229,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c if (index >= device_status.keyboard_moddifier_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; bool value_changed = false; const auto new_status = TransformToButton(callback); auto& current_status = device_status.keyboard_moddifier_values[index]; @@ -259,6 +261,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c } if (is_configuring) { + lock.unlock(); TriggerOnChange(DeviceTriggerType::KeyboardModdifier); return; } @@ -289,6 +292,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c break; } + lock.unlock(); TriggerOnChange(DeviceTriggerType::KeyboardModdifier); } @@ -297,7 +301,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba if (index >= device_status.mouse_button_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; bool value_changed = false; const auto new_status = TransformToButton(callback); auto& current_status = device_status.mouse_button_values[index]; @@ -329,6 +333,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba } if (is_configuring) { + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); return; } @@ -351,6 +356,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba break; } + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); } @@ -359,13 +365,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba if (index >= device_status.mouse_analog_values.size()) { return; } - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; const auto analog_value = TransformToAnalog(callback); device_status.mouse_analog_values[index] = analog_value; if (is_configuring) { device_status.mouse_position_state = {}; + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); return; } @@ -379,17 +386,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba break; } + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); } void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) { - std::lock_guard lock{mutex}; + std::unique_lock lock{mutex}; const auto touch_value = TransformToTouch(callback); device_status.mouse_stick_value = touch_value; if (is_configuring) { device_status.mouse_position_state = {}; + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); return; } @@ -397,42 +406,52 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac device_status.mouse_position_state.x = touch_value.x.value; device_status.mouse_position_state.y = touch_value.y.value; + lock.unlock(); TriggerOnChange(DeviceTriggerType::Mouse); } KeyboardValues EmulatedDevices::GetKeyboardValues() const { + std::scoped_lock lock{mutex}; return device_status.keyboard_values; } KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { + std::scoped_lock lock{mutex}; return device_status.keyboard_moddifier_values; } MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { + std::scoped_lock lock{mutex}; return device_status.mouse_button_values; } KeyboardKey EmulatedDevices::GetKeyboard() const { + std::scoped_lock lock{mutex}; return device_status.keyboard_state; } KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { + std::scoped_lock lock{mutex}; return device_status.keyboard_moddifier_state; } MouseButton EmulatedDevices::GetMouseButtons() const { + std::scoped_lock lock{mutex}; return device_status.mouse_button_state; } MousePosition EmulatedDevices::GetMousePosition() const { + std::scoped_lock lock{mutex}; return device_status.mouse_position_state; } AnalogStickState EmulatedDevices::GetMouseWheel() const { + std::scoped_lock lock{mutex}; return device_status.mouse_wheel_state; } void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { + std::scoped_lock lock{callback_mutex}; for (const auto& poller_pair : callback_list) { const InterfaceUpdateCallback& poller = poller_pair.second; if (poller.on_change) { @@ -442,13 +461,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { } int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); return last_callback_key++; } void EmulatedDevices::DeleteCallback(int key) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{callback_mutex}; const auto& iterator = callback_list.find(key); if (iterator == callback_list.end()) { LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h index 790d3b411..73e9f0293 100644 --- a/src/core/hid/emulated_devices.h +++ b/src/core/hid/emulated_devices.h @@ -200,6 +200,7 @@ private: MouseStickDevice mouse_stick_device; mutable std::mutex mutex; + mutable std::mutex callback_mutex; std::unordered_map<int, InterfaceUpdateCallback> callback_list; int last_callback_key = 0; diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 42d1b0e31..b547a3463 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -24,8 +24,15 @@ namespace Kernel { -SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_) - : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {} +SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_, + ServiceThreadType thread_type) + : kernel{kernel_} { + if (thread_type == ServiceThreadType::CreateNew) { + service_thread = kernel.CreateServiceThread(service_name_); + } else { + service_thread = kernel.GetDefaultServiceThread(); + } +} SessionRequestHandler::~SessionRequestHandler() { kernel.ReleaseServiceThread(service_thread); diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 670cc741c..640146137 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -33,6 +33,11 @@ namespace Service { class ServiceFrameworkBase; } +enum class ServiceThreadType { + Default, + CreateNew, +}; + namespace Kernel { class Domain; @@ -57,7 +62,8 @@ enum class ThreadWakeupReason; */ class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { public: - SessionRequestHandler(KernelCore& kernel, const char* service_name_); + SessionRequestHandler(KernelCore& kernel_, const char* service_name_, + ServiceThreadType thread_type); virtual ~SessionRequestHandler(); /** diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index caa91a509..481a0d7cb 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -61,6 +61,7 @@ struct KernelCore::Impl { global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); global_handle_table->Initialize(KHandleTable::MaxTableSize); + default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread"); is_phantom_mode_for_singlecore = false; @@ -98,7 +99,7 @@ struct KernelCore::Impl { // Close all open server ports. std::unordered_set<KServerPort*> server_ports_; { - std::lock_guard lk(server_ports_lock); + std::scoped_lock lk{server_ports_lock}; server_ports_ = server_ports; server_ports.clear(); } @@ -156,7 +157,7 @@ struct KernelCore::Impl { // Close kernel objects that were not freed on shutdown { - std::lock_guard lk(registered_in_use_objects_lock); + std::scoped_lock lk{registered_in_use_objects_lock}; if (registered_in_use_objects.size()) { for (auto& object : registered_in_use_objects) { object->Close(); @@ -177,7 +178,7 @@ struct KernelCore::Impl { // Track kernel objects that were not freed on shutdown { - std::lock_guard lk(registered_objects_lock); + std::scoped_lock lk{registered_objects_lock}; if (registered_objects.size()) { LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!", registered_objects.size()); @@ -659,7 +660,7 @@ struct KernelCore::Impl { KClientPort* port = &search->second(system.ServiceManager(), system); { - std::lock_guard lk(server_ports_lock); + std::scoped_lock lk{server_ports_lock}; server_ports.insert(&port->GetParent()->GetServerPort()); } return port; @@ -677,6 +678,12 @@ struct KernelCore::Impl { void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { if (auto strong_ptr = service_thread.lock()) { + if (strong_ptr == default_service_thread.lock()) { + // Nothing to do here, the service is using default_service_thread, which will be + // released on shutdown. + return; + } + service_threads_manager.QueueWork( [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); } @@ -739,7 +746,8 @@ struct KernelCore::Impl { std::unique_ptr<KMemoryLayout> memory_layout; // Threads used for services - std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; + std::unordered_set<std::shared_ptr<ServiceThread>> service_threads; + std::weak_ptr<ServiceThread> default_service_thread; Common::ThreadWorker service_threads_manager; std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; @@ -921,22 +929,22 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) { } void KernelCore::RegisterKernelObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_objects_lock); + std::scoped_lock lk{impl->registered_objects_lock}; impl->registered_objects.insert(object); } void KernelCore::UnregisterKernelObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_objects_lock); + std::scoped_lock lk{impl->registered_objects_lock}; impl->registered_objects.erase(object); } void KernelCore::RegisterInUseObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_in_use_objects_lock); + std::scoped_lock lk{impl->registered_in_use_objects_lock}; impl->registered_in_use_objects.insert(object); } void KernelCore::UnregisterInUseObject(KAutoObject* object) { - std::lock_guard lk(impl->registered_in_use_objects_lock); + std::scoped_lock lk{impl->registered_in_use_objects_lock}; impl->registered_in_use_objects.erase(object); } @@ -1065,6 +1073,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std:: return impl->CreateServiceThread(*this, name); } +std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const { + return impl->default_service_thread; +} + void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { impl->ReleaseServiceThread(service_thread); } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 4c68e96df..24e26fa44 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -271,9 +271,11 @@ public: void ExitSVCProfile(); /** - * Creates an HLE service thread, which are used to execute service routines asynchronously. - * While these are allocated per ServerSession, these need to be owned and managed outside - * of ServerSession to avoid a circular dependency. + * Creates a host thread to execute HLE service requests, which are used to execute service + * routines asynchronously. While these are allocated per ServerSession, these need to be owned + * and managed outside of ServerSession to avoid a circular dependency. In general, most + * services can just use the default service thread, and not need their own host service thread. + * See GetDefaultServiceThread. * @param name String name for the ServerSession creating this thread, used for debug * purposes. * @returns The a weak pointer newly created service thread. @@ -281,6 +283,14 @@ public: std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); /** + * Gets the default host service thread, which executes HLE service requests. Unless service + * requests need to block on the host, the default service thread should be used in favor of + * creating a new service thread. + * @returns The a weak pointer for the default service thread. + */ + std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const; + + /** * Releases a HLE service thread, instructing KernelCore to free it. This should be called when * the ServerSession associated with the thread is destroyed. * @param service_thread Service thread to release. diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 7477668e4..18a5f40f8 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -58,6 +58,7 @@ bool PhysicalCore::IsInterrupted() const { void PhysicalCore::Interrupt() { guard->lock(); interrupts[core_index].SetInterrupt(true); + arm_interface->SignalInterrupt(); guard->unlock(); } diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index aa985d820..5b8fe8eae 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -24,7 +24,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { } void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; if (nanoseconds > 0) { ASSERT(thread); ASSERT(thread->GetState() != ThreadState::Runnable); @@ -35,7 +35,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { } void TimeManager::UnscheduleTimeEvent(KThread* thread) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; system.CoreTiming().UnscheduleEvent(time_manager_event_type, reinterpret_cast<uintptr_t>(thread)); } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 420de3c54..4d7e5ecd3 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1337,7 +1337,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {200, nullptr, "GetLastApplicationExitReason"}, {500, nullptr, "StartContinuousRecordingFlushForDebug"}, {1000, nullptr, "CreateMovieMaker"}, - {1001, nullptr, "PrepareForJit"}, + {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"}, }; // clang-format on @@ -1787,6 +1787,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent()); } +void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto message_queue = std::make_shared<AppletMessageQueue>(system); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index fdd937b82..11a3c0459 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -336,6 +336,7 @@ private: void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx); void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); + void PrepareForJit(Kernel::HLERequestContext& ctx); KernelHelpers::ServiceContext service_context; diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index affa7971c..a72956a28 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -41,9 +41,10 @@ public: explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, AudioCore::AudioOut& audio_core_, std::string&& device_name_, std::string&& unique_name) - : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, - device_name{std::move(device_name_)}, audio_params{audio_params_}, - main_memory{system.Memory()}, service_context{system_, "IAudioOut"} { + : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, + audio_core{audio_core_}, device_name{std::move(device_name_)}, + audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_, + "IAudioOut"} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index f45e5cecc..d4ffeb21d 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -24,7 +24,8 @@ public: explicit IAudioRenderer(Core::System& system_, const AudioCommon::AudioRendererParameter& audren_params, const std::size_t instance_number) - : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} { + : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioRenderer"} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index b087e7bba..c07929ab8 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -58,7 +58,8 @@ enum class FileSystemType : u8 { class IStorage final : public ServiceFramework<IStorage> { public: explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { + : ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew}, + backend(std::move(backend_)) { static const FunctionInfo functions[] = { {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, @@ -116,7 +117,8 @@ private: class IFile final : public ServiceFramework<IFile> { public: explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { + : ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew}, + backend(std::move(backend_)) { static const FunctionInfo functions[] = { {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, @@ -252,7 +254,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec class IDirectory final : public ServiceFramework<IDirectory> { public: explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_) - : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { + : ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew}, + backend(std::move(backend_)) { static const FunctionInfo functions[] = { {0, &IDirectory::Read, "Read"}, {1, &IDirectory::GetEntryCount, "GetEntryCount"}, @@ -308,8 +311,8 @@ private: class IFileSystem final : public ServiceFramework<IFileSystem> { public: explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) - : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( - size_)} { + : ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew}, + backend{std::move(backend_)}, size{std::move(size_)} { static const FunctionInfo functions[] = { {0, &IFileSystem::CreateFile, "CreateFile"}, {1, &IFileSystem::DeleteFile, "DeleteFile"}, diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index aa6cb34b7..4e17a952e 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -318,7 +318,7 @@ void Controller_NPad::OnRelease() { } void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; auto& controller = GetControllerFromNpadIdType(npad_id); const auto controller_type = controller.device->GetNpadStyleIndex(); if (!controller.device->IsConnected()) { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 92e6bf889..b2cec2253 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -63,6 +63,10 @@ IAppletResource::IAppletResource(Core::System& system_, MakeController<Controller_Gesture>(HidController::Gesture); MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); + // Homebrew doesn't try to activate some controllers, so we activate them by default + GetController<Controller_NPad>(HidController::NPad).ActivateController(); + GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController(); + GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000); diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp new file mode 100644 index 000000000..c8ebd2e3f --- /dev/null +++ b/src/core/hle/service/jit/jit.cpp @@ -0,0 +1,53 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/ipc_helpers.h" +#include "core/hle/result.h" +#include "core/hle/service/jit/jit.h" +#include "core/hle/service/service.h" + +namespace Service::JIT { + +class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { +public: + explicit IJitEnvironment(Core::System& system_) : ServiceFramework{system_, "IJitEnvironment"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GenerateCode"}, + {1, nullptr, "Control"}, + {1000, nullptr, "LoadPlugin"}, + {1001, nullptr, "GetCodeAddress"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class JITU final : public ServiceFramework<JITU> { +public: + explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + + void CreateJitEnvironment(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_JIT, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IJitEnvironment>(system); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { + std::make_shared<JITU>(system)->InstallAsService(sm); +} + +} // namespace Service::JIT diff --git a/src/core/hle/service/jit/jit.h b/src/core/hle/service/jit/jit.h new file mode 100644 index 000000000..8fbf504a1 --- /dev/null +++ b/src/core/hle/service/jit/jit.h @@ -0,0 +1,20 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Core { +class System; +} + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::JIT { + +/// Registers all JIT services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); + +} // namespace Service::JIT diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index 1ce2a856b..8467b50e4 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -230,7 +230,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) { } NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name) - : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} { + : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} { static const FunctionInfo functions[] = { {0, &NVDRV::Open, "Open"}, {1, &NVDRV::Ioctl1, "Ioctl"}, diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp index 93fa1ec10..d7ee5362b 100644 --- a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp @@ -21,7 +21,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco return Status::BadValue; } - std::scoped_lock lock(mutex); + std::scoped_lock lock{mutex}; if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) { if (status != Status::NoBufferAvailable) { @@ -40,7 +40,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco } Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) { - std::scoped_lock lock(mutex); + std::scoped_lock lock{mutex}; if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); status != Status::NoError) { diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp index 41fbba219..3ab9a8c05 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp @@ -18,9 +18,8 @@ BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_) BufferQueueConsumer::~BufferQueueConsumer() = default; Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, - std::chrono::nanoseconds expected_present, - u64 max_frame_number) { - std::scoped_lock lock(core->mutex); + std::chrono::nanoseconds expected_present) { + std::scoped_lock lock{core->mutex}; // Check that the consumer doesn't currently have the maximum number of buffers acquired. const s32 num_acquired_buffers{ @@ -50,12 +49,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { const auto& buffer_item{core->queue[1]}; - // If dropping entry[0] would leave us with a buffer that the consumer is not yet ready - // for, don't drop it. - if (max_frame_number && buffer_item.frame_number > max_frame_number) { - break; - } - // If entry[1] is timely, drop entry[0] (and repeat). const auto desired_present = buffer_item.timestamp; if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC || @@ -127,7 +120,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc std::shared_ptr<IProducerListener> listener; { - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; // If the frame number has changed because the buffer has been reallocated, we can ignore // this ReleaseBuffer for the old buffer. @@ -187,7 +180,7 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_ LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app); - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; if (core->is_abandoned) { LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); @@ -200,4 +193,39 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_ return Status::NoError; } +Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { + if (out_slot_mask == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr"); + return Status::BadValue; + } + + std::scoped_lock lock{core->mutex}; + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + u64 mask = 0; + for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (!slots[s].acquire_called) { + mask |= (1ULL << s); + } + } + + // Remove from the mask queued buffers for which acquire has been called, since the consumer + // will not receive their buffer addresses and so must retain their cached information + auto current(core->queue.begin()); + while (current != core->queue.end()) { + if (current->acquire_called) { + mask &= ~(1ULL << current->slot); + } + ++current; + } + + LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask); + *out_slot_mask = mask; + return Status::NoError; +} + } // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h index f22854394..8a047fe06 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h @@ -24,10 +24,10 @@ public: explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); ~BufferQueueConsumer(); - Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present, - u64 max_frame_number = 0); + Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); + Status GetReleasedBuffers(u64* out_slot_mask); private: std::shared_ptr<BufferQueueCore> core; diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp index 6082610e0..ec5aabaeb 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp @@ -15,7 +15,7 @@ BufferQueueCore::BufferQueueCore() = default; BufferQueueCore::~BufferQueueCore() = default; void BufferQueueCore::NotifyShutdown() { - std::scoped_lock lock(mutex); + std::scoped_lock lock{mutex}; is_shutting_down = true; @@ -95,7 +95,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) { } void BufferQueueCore::FreeAllBuffersLocked() { - queue.clear(); buffer_has_been_queued = false; for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h index 4dfd53387..e4e0937cb 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h @@ -73,8 +73,6 @@ private: u32 transform_hint{}; bool is_allocating{}; mutable std::condition_variable_any is_allocating_condition; - bool allow_allocation{true}; - u64 buffer_age{}; bool is_shutting_down{}; }; diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 0833be57a..6f604a88e 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -38,7 +38,7 @@ BufferQueueProducer::~BufferQueueProducer() { Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) { LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; if (core->is_abandoned) { LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); @@ -62,11 +62,12 @@ Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffe Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count); - std::shared_ptr<IConsumerListener> listener; + std::shared_ptr<IConsumerListener> listener; { - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; core->WaitWhileAllocatingLocked(); + if (core->is_abandoned) { LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); return Status::NoInit; @@ -120,7 +121,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { } Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, - Status* returnFlags) const { + Status* return_flags) const { bool try_again = true; while (try_again) { @@ -142,10 +143,12 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, ASSERT(slots[s].buffer_state == BufferState::Free); if (slots[s].graphic_buffer != nullptr) { core->FreeBufferLocked(s); - *returnFlags |= Status::ReleaseAllBuffers; + *return_flags |= Status::ReleaseAllBuffers; } } + // Look for a free buffer to give to the client + *found = BufferQueueCore::INVALID_BUFFER_SLOT; s32 dequeued_count{}; s32 acquired_count{}; for (s32 s{}; s < max_buffer_count; ++s) { @@ -233,70 +236,52 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool Status return_flags = Status::NoError; bool attached_by_consumer = false; { - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; core->WaitWhileAllocatingLocked(); + if (format == PixelFormat::NoFormat) { format = core->default_buffer_format; } // Enable the usage bits the consumer requested usage |= core->consumer_usage_bit; - const bool use_default_size = !width && !height; - if (use_default_size) { - width = core->default_width; - height = core->default_height; + + s32 found{}; + Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); + if (status != Status::NoError) { + return status; } - s32 found = BufferItem::INVALID_BUFFER_SLOT; - while (found == BufferItem::INVALID_BUFFER_SLOT) { - Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); - if (status != Status::NoError) { - return status; - } + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + LOG_ERROR(Service_NVFlinger, "no available buffer slots"); + return Status::Busy; + } - // This should not happen - if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { - LOG_DEBUG(Service_NVFlinger, "no available buffer slots"); - return Status::Busy; - } + *out_slot = found; - const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer); + attached_by_consumer = slots[found].attached_by_consumer; - // If we are not allowed to allocate new buffers, WaitForFreeSlotThenRelock must have - // returned a slot containing a buffer. If this buffer would require reallocation to - // meet the requested attributes, we free it and attempt to get another one. - if (!core->allow_allocation) { - if (buffer->NeedsReallocation(width, height, format, usage)) { - core->FreeBufferLocked(found); - found = BufferItem::INVALID_BUFFER_SLOT; - continue; - } - } + const bool use_default_size = !width && !height; + if (use_default_size) { + width = core->default_width; + height = core->default_height; } - *out_slot = found; - attached_by_consumer = slots[found].attached_by_consumer; slots[found].buffer_state = BufferState::Dequeued; const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer); - - if ((buffer == nullptr) || buffer->NeedsReallocation(width, height, format, usage)) { + if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) || + (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) { slots[found].acquire_called = false; slots[found].graphic_buffer = nullptr; slots[found].request_buffer_called = false; slots[found].fence = Fence::NoFence(); - core->buffer_age = 0; + return_flags |= Status::BufferNeedsReallocation; - } else { - // We add 1 because that will be the frame number when this buffer - // is queued - core->buffer_age = core->frame_counter + 1 - slots[found].frame_number; } - LOG_DEBUG(Service_NVFlinger, "setting buffer age to {}", core->buffer_age); - *out_fence = slots[found].fence; - slots[found].fence = Fence::NoFence(); } @@ -310,7 +295,8 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool } { - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; + if (core->is_abandoned) { LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); return Status::NoInit; @@ -327,13 +313,15 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot, slots[*out_slot].frame_number, return_flags); + return return_flags; } Status BufferQueueProducer::DetachBuffer(s32 slot) { LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; + if (core->is_abandoned) { LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); return Status::NoInit; @@ -368,8 +356,7 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out return Status::BadValue; } - std::scoped_lock lock(core->mutex); - + std::scoped_lock lock{core->mutex}; core->WaitWhileAllocatingLocked(); if (core->is_abandoned) { @@ -412,7 +399,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot, return Status::BadValue; } - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; core->WaitWhileAllocatingLocked(); Status return_flags = Status::NoError; @@ -423,6 +410,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot, return status; } + // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { LOG_ERROR(Service_NVFlinger, "No available buffer slots"); return Status::Busy; @@ -466,13 +454,13 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, return Status::BadValue; } - std::shared_ptr<IConsumerListener> frameAvailableListener; - std::shared_ptr<IConsumerListener> frameReplacedListener; + std::shared_ptr<IConsumerListener> frame_available_listener; + std::shared_ptr<IConsumerListener> frame_replaced_listener; s32 callback_ticket{}; BufferItem item; { - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; if (core->is_abandoned) { LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); @@ -541,12 +529,13 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, item.fence = fence; item.is_droppable = core->dequeue_buffer_cannot_block || async; item.swap_interval = swap_interval; + sticky_transform = sticky_transform_; if (core->queue.empty()) { // When the queue is empty, we can simply queue this buffer core->queue.push_back(item); - frameAvailableListener = core->consumer_listener; + frame_available_listener = core->consumer_listener; } else { // When the queue is not empty, we need to look at the front buffer // state to see if we need to replace it @@ -563,10 +552,10 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, } // Overwrite the droppable buffer with the incoming one *front = item; - frameReplacedListener = core->consumer_listener; + frame_replaced_listener = core->consumer_listener; } else { core->queue.push_back(item); - frameAvailableListener = core->consumer_listener; + frame_available_listener = core->consumer_listener; } } @@ -587,15 +576,15 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, // Call back without the main BufferQueue lock held, but with the callback lock held so we can // ensure that callbacks occur in order { - std::scoped_lock lock(callback_mutex); + std::scoped_lock lock{callback_mutex}; while (callback_ticket != current_callback_ticket) { callback_condition.wait(callback_mutex); } - if (frameAvailableListener != nullptr) { - frameAvailableListener->OnFrameAvailable(item); - } else if (frameReplacedListener != nullptr) { - frameReplacedListener->OnFrameReplaced(item); + if (frame_available_listener != nullptr) { + frame_available_listener->OnFrameAvailable(item); + } else if (frame_replaced_listener != nullptr) { + frame_replaced_listener->OnFrameReplaced(item); } ++current_callback_ticket; @@ -608,7 +597,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { LOG_DEBUG(Service_NVFlinger, "slot {}", slot); - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; if (core->is_abandoned) { LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); @@ -634,7 +623,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { } Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; if (out_value == nullptr) { LOG_ERROR(Service_NVFlinger, "outValue was nullptr"); @@ -669,13 +658,6 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { case NativeWindow::ConsumerUsageBits: value = core->consumer_usage_bit; break; - case NativeWindow::BufferAge: - if (core->buffer_age > INT32_MAX) { - value = 0; - } else { - value = static_cast<u32>(core->buffer_age); - } - break; default: UNREACHABLE(); return Status::BadValue; @@ -691,7 +673,7 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api, bool producer_controlled_by_app, QueueBufferOutput* output) { - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api, producer_controlled_by_app); @@ -737,7 +719,6 @@ Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& li core->buffer_has_been_queued = false; core->dequeue_buffer_cannot_block = core->consumer_controlled_by_app && producer_controlled_by_app; - core->allow_allocation = true; return status; } @@ -749,7 +730,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { std::shared_ptr<IConsumerListener> listener; { - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; core->WaitWhileAllocatingLocked(); @@ -770,7 +751,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) { core->SignalDequeueCondition(); buffer_wait_event->GetWritableEvent().Signal(); listener = core->consumer_listener; - } else if (core->connected_api != NativeWindowApi::NoConnectedApi) { + } else { LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", core->connected_api, api); status = Status::BadValue; @@ -799,7 +780,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, return Status::BadValue; } - std::scoped_lock lock(core->mutex); + std::scoped_lock lock{core->mutex}; slots[slot] = {}; slots[slot].graphic_buffer = buffer; diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h index 77fdcae8e..c4ca68fd3 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h @@ -66,7 +66,7 @@ public: private: BufferQueueProducer(const BufferQueueProducer&) = delete; - Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* returnFlags) const; + Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const; Kernel::KEvent* buffer_wait_event{}; Service::KernelHelpers::ServiceContext& service_context; diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp index be65a3f88..30fc21acc 100644 --- a/src/core/hle/service/nvflinger/consumer_base.cpp +++ b/src/core/hle/service/nvflinger/consumer_base.cpp @@ -18,7 +18,7 @@ ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_) : consumer{std::move(consumer_)} {} ConsumerBase::~ConsumerBase() { - std::scoped_lock lock(mutex); + std::scoped_lock lock{mutex}; ASSERT_MSG(is_abandoned, "consumer is not abandoned!"); } @@ -36,38 +36,41 @@ void ConsumerBase::FreeBufferLocked(s32 slot_index) { } void ConsumerBase::OnFrameAvailable(const BufferItem& item) { - std::scoped_lock lock(mutex); LOG_DEBUG(Service_NVFlinger, "called"); } void ConsumerBase::OnFrameReplaced(const BufferItem& item) { - std::scoped_lock lock(mutex); LOG_DEBUG(Service_NVFlinger, "called"); } void ConsumerBase::OnBuffersReleased() { - std::scoped_lock lock(mutex); - LOG_DEBUG(Service_NVFlinger, "called"); -} + std::scoped_lock lock{mutex}; -void ConsumerBase::OnSidebandStreamChanged() {} + LOG_DEBUG(Service_NVFlinger, "called"); -Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when, - u64 max_frame_number) { if (is_abandoned) { - LOG_ERROR(Service_NVFlinger, "consumer is abandoned!"); - return Status::NoInit; + // Nothing to do if we're already abandoned. + return; } - Status err = consumer->AcquireBuffer(item, present_when, max_frame_number); + u64 mask = 0; + consumer->GetReleasedBuffers(&mask); + for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) { + if (mask & (1ULL << i)) { + FreeBufferLocked(i); + } + } +} + +void ConsumerBase::OnSidebandStreamChanged() {} + +Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) { + Status err = consumer->AcquireBuffer(item, present_when); if (err != Status::NoError) { return err; } if (item->graphic_buffer != nullptr) { - if (slots[item->slot].graphic_buffer != nullptr) { - FreeBufferLocked(item->slot); - } slots[item->slot].graphic_buffer = item->graphic_buffer; } diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h index 9ab949420..736080e3a 100644 --- a/src/core/hle/service/nvflinger/consumer_base.h +++ b/src/core/hle/service/nvflinger/consumer_base.h @@ -35,8 +35,7 @@ protected: virtual void OnSidebandStreamChanged() override; void FreeBufferLocked(s32 slot_index); - Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when, - u64 max_frame_number = 0); + Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer); bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const; Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer, diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp index d4da98ddb..04068827e 100644 --- a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp +++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp @@ -4,8 +4,6 @@ // Parts of this implementation were base on: // https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp -#pragma once - #include "core/hle/service/nvflinger/graphic_buffer_producer.h" #include "core/hle/service/nvflinger/parcel.h" diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp index 0c937d682..094ba2542 100644 --- a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp +++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp @@ -14,7 +14,7 @@ HosBinderDriverServer::HosBinderDriverServer(Core::System& system_) HosBinderDriverServer::~HosBinderDriverServer() {} u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) { - std::lock_guard lk{lock}; + std::scoped_lock lk{lock}; last_id++; @@ -24,7 +24,7 @@ u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& } android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) { - std::lock_guard lk{lock}; + std::scoped_lock lk{lock}; if (auto search = producers.find(id); search != producers.end()) { return search->second.get(); diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 76ce1fbfd..6fb2cdff1 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -104,7 +104,7 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { const auto lock_guard = Lock(); - LOG_DEBUG(Service, "Opening \"{}\" display", name); + LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name); const auto itr = std::find_if(displays.begin(), displays.end(), @@ -219,7 +219,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) { auto* layer = display->FindLayer(layer_id); if (layer == nullptr) { - LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id); + LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id); CreateLayerAtId(*display, layer_id); return display->FindLayer(layer_id); } diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index ab3286db9..0f59a03c5 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -32,6 +32,7 @@ #include "core/hle/service/glue/glue.h" #include "core/hle/service/grc/grc.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/jit/jit.h" #include "core/hle/service/lbl/lbl.h" #include "core/hle/service/ldn/ldn.h" #include "core/hle/service/ldr/ldr.h" @@ -91,8 +92,9 @@ namespace Service { } ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, - u32 max_sessions_, InvokerFn* handler_invoker_) - : SessionRequestHandler(system_.Kernel(), service_name_), system{system_}, + ServiceThreadType thread_type, u32 max_sessions_, + InvokerFn* handler_invoker_) + : SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_}, service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {} ServiceFrameworkBase::~ServiceFrameworkBase() { @@ -261,6 +263,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system Glue::InstallInterfaces(system); GRC::InstallInterfaces(*sm, system); HID::InstallInterfaces(*sm, system); + JIT::InstallInterfaces(*sm, system); LBL::InstallInterfaces(*sm, system); LDN::InstallInterfaces(*sm, system); LDR::InstallInterfaces(*sm, system); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index b9ab2c465..c78b2baeb 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -114,7 +114,8 @@ private: Kernel::HLERequestContext& ctx); explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_, - u32 max_sessions_, InvokerFn* handler_invoker_); + ServiceThreadType thread_type, u32 max_sessions_, + InvokerFn* handler_invoker_); ~ServiceFrameworkBase() override; void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); @@ -176,14 +177,17 @@ protected: /** * Initializes the handler with no functions installed. * - * @param system_ The system context to construct this service under. + * @param system_ The system context to construct this service under. * @param service_name_ Name of the service. - * @param max_sessions_ Maximum number of sessions that can be - * connected to this service at the same time. + * @param thread_type Specifies the thread type for this service. If this is set to CreateNew, + * it creates a new thread for it, otherwise this uses the default thread. + * @param max_sessions_ Maximum number of sessions that can be connected to this service at the + * same time. */ explicit ServiceFramework(Core::System& system_, const char* service_name_, + ServiceThreadType thread_type = ServiceThreadType::Default, u32 max_sessions_ = ServerSessionCountMax) - : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} + : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {} /// Registers handlers in the service. template <std::size_t N> diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 695a1faa6..97f895852 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -206,7 +206,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) { } SM::SM(ServiceManager& service_manager_, Core::System& system_) - : ServiceFramework{system_, "sm:", 4}, + : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4}, service_manager{service_manager_}, kernel{system_.Kernel()} { RegisterHandlers({ {0, &SM::Initialize, "Initialize"}, diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index fc93fb743..d25b050e2 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -689,6 +689,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con case OptName::REUSEADDR: ASSERT(value == 0 || value == 1); return Translate(socket->SetReuseAddr(value != 0)); + case OptName::KEEPALIVE: + ASSERT(value == 0 || value == 1); + return Translate(socket->SetKeepAlive(value != 0)); case OptName::BROADCAST: ASSERT(value == 0 || value == 1); return Translate(socket->SetBroadcast(value != 0)); @@ -837,7 +840,8 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co rb.PushEnum(bsd_errno); } -BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { +BSD::BSD(Core::System& system_, const char* name) + : ServiceFramework{system_, name, ServiceThreadType::CreateNew} { // clang-format off static const FunctionInfo functions[] = { {0, &BSD::RegisterClient, "RegisterClient"}, diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index fb6142c49..a193fb578 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -2,8 +2,28 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <string_view> +#include <utility> +#include <vector> + +#include "common/string_util.h" +#include "common/swap.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/sockets/sfdnsres.h" +#include "core/memory.h" + +#ifdef _WIN32 +#include <ws2tcpip.h> +#elif YUZU_UNIX +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> +#ifndef EAI_NODATA +#define EAI_NODATA EAI_NONAME +#endif +#endif namespace Service::Sockets { @@ -21,7 +41,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" {9, nullptr, "CancelRequest"}, {10, nullptr, "GetHostByNameRequestWithOptions"}, {11, nullptr, "GetHostByAddrRequestWithOptions"}, - {12, nullptr, "GetAddrInfoRequestWithOptions"}, + {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"}, {13, nullptr, "GetNameInfoRequestWithOptions"}, {14, nullptr, "ResolverSetOptionRequest"}, {15, nullptr, "ResolverGetOptionRequest"}, @@ -31,7 +51,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" SFDNSRES::~SFDNSRES() = default; -void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { +enum class NetDbError : s32 { + Internal = -1, + Success = 0, + HostNotFound = 1, + TryAgain = 2, + NoRecovery = 3, + NoData = 4, +}; + +static NetDbError AddrInfoErrorToNetDbError(s32 result) { + // Best effort guess to map errors + switch (result) { + case 0: + return NetDbError::Success; + case EAI_AGAIN: + return NetDbError::TryAgain; + case EAI_NODATA: + return NetDbError::NoData; + default: + return NetDbError::HostNotFound; + } +} + +static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code, + std::string_view host) { + // Adapted from + // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190 + std::vector<u8> data; + + auto* current = addrinfo; + while (current != nullptr) { + struct SerializedResponseHeader { + u32 magic; + s32 flags; + s32 family; + s32 socket_type; + s32 protocol; + u32 address_length; + }; + static_assert(sizeof(SerializedResponseHeader) == 0x18, + "Response header size must be 0x18 bytes"); + + constexpr auto header_size = sizeof(SerializedResponseHeader); + const auto addr_size = + current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4; + const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1; + + const auto last_size = data.size(); + data.resize(last_size + header_size + addr_size + canonname_size); + + // Header in network byte order + SerializedResponseHeader header{}; + + constexpr auto HEADER_MAGIC = 0xBEEFCAFE; + header.magic = htonl(HEADER_MAGIC); + header.family = htonl(current->ai_family); + header.flags = htonl(current->ai_flags); + header.socket_type = htonl(current->ai_socktype); + header.protocol = htonl(current->ai_protocol); + header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0; + + auto* header_ptr = data.data() + last_size; + std::memcpy(header_ptr, &header, header_size); + + if (header.address_length == 0) { + std::memset(header_ptr + header_size, 0, 4); + } else { + switch (current->ai_family) { + case AF_INET: { + struct SockAddrIn { + s16 sin_family; + u16 sin_port; + u32 sin_addr; + u8 sin_zero[8]; + }; + + SockAddrIn serialized_addr{}; + const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr); + serialized_addr.sin_port = htons(addr.sin_port); + serialized_addr.sin_family = htons(addr.sin_family); + serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr); + std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn)); + + char addr_string_buf[64]{}; + inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf)); + LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf); + break; + } + case AF_INET6: { + struct SockAddrIn6 { + s16 sin6_family; + u16 sin6_port; + u32 sin6_flowinfo; + u8 sin6_addr[16]; + u32 sin6_scope_id; + }; + + SockAddrIn6 serialized_addr{}; + const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr); + serialized_addr.sin6_family = htons(addr.sin6_family); + serialized_addr.sin6_port = htons(addr.sin6_port); + serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo); + serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id); + std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr, + sizeof(SockAddrIn6::sin6_addr)); + std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6)); + + char addr_string_buf[64]{}; + inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf)); + LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf); + break; + } + default: + std::memcpy(header_ptr + header_size, current->ai_addr, addr_size); + break; + } + } + if (current->ai_canonname) { + std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size); + } else { + *(header_ptr + header_size + addr_size) = 0; + } + + current = current->ai_next; + } + + // 4-byte sentinel value + data.push_back(0); + data.push_back(0); + data.push_back(0); + data.push_back(0); + + return data; +} + +static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) { struct Parameters { u8 use_nsd_resolve; u32 unknown; @@ -42,11 +197,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { const auto parameters = rp.PopRaw<Parameters>(); LOG_WARNING(Service, - "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}", + "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}", parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); - IPC::ResponseBuilder rb{ctx, 2}; + const auto host_buffer = ctx.ReadBuffer(0); + const std::string host = Common::StringFromBuffer(host_buffer); + + const auto service_buffer = ctx.ReadBuffer(1); + const std::string service = Common::StringFromBuffer(service_buffer); + + addrinfo* addrinfo; + // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now + s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo); + + u32 data_size = 0; + if (result_code == 0 && addrinfo != nullptr) { + const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host); + data_size = static_cast<u32>(data.size()); + freeaddrinfo(addrinfo); + + ctx.WriteBuffer(data, 0); + } + + return std::make_pair(data_size, result_code); +} + +void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { + auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode + rb.Push(result_code); // errno + rb.Push(data_size); // serialized size +} + +void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) { + // Additional options are ignored + auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + + IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); + rb.Push(data_size); // serialized size + rb.Push(result_code); // errno + rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode + rb.Push(0); } -} // namespace Service::Sockets +} // namespace Service::Sockets
\ No newline at end of file diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h index 5d3b4dc2d..f0c57377d 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -19,6 +19,7 @@ public: private: void GetAddrInfoRequest(Kernel::HLERequestContext& ctx); + void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx); }; } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index 02dbbae40..d69c86431 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -46,6 +46,7 @@ enum class Protocol : u32 { enum class OptName : u32 { REUSEADDR = 0x4, + KEEPALIVE = 0x8, BROADCAST = 0x20, LINGER = 0x80, SNDBUF = 0x1001, diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 430cbc546..a3436c8ea 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -77,7 +77,8 @@ static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size"); class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { public: explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_) - : ServiceFramework{system_, "IHOSBinderDriver"}, server(server_) { + : ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew}, + server(server_) { static const FunctionInfo functions[] = { {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index a3e0664b9..0784a165d 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp @@ -600,6 +600,10 @@ Errno Socket::SetReuseAddr(bool enable) { return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); } +Errno Socket::SetKeepAlive(bool enable) { + return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0); +} + Errno Socket::SetBroadcast(bool enable) { return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); } diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h index 5e39e7c54..caaefce7c 100644 --- a/src/core/network/sockets.h +++ b/src/core/network/sockets.h @@ -67,6 +67,8 @@ public: Errno SetReuseAddr(bool enable); + Errno SetKeepAlive(bool enable); + Errno SetBroadcast(bool enable); Errno SetSndBuf(u32 value); diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 52c43c857..6ef459b7a 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -53,13 +53,13 @@ PerfStats::~PerfStats() { } void PerfStats::BeginSystemFrame() { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; frame_begin = Clock::now(); } void PerfStats::EndSystemFrame() { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; auto frame_end = Clock::now(); const auto frame_time = frame_end - frame_begin; @@ -79,7 +79,7 @@ void PerfStats::EndGameFrame() { } double PerfStats::GetMeanFrametime() const { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; if (current_index <= IgnoreFrames) { return 0; @@ -91,7 +91,7 @@ double PerfStats::GetMeanFrametime() const { } PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; const auto now = Clock::now(); // Walltime elapsed since stats were reset @@ -120,7 +120,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us } double PerfStats::GetLastFrameTimeScale() const { - std::lock_guard lock{object_mutex}; + std::scoped_lock lock{object_mutex}; constexpr double FRAME_LENGTH = 1.0 / 60; return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 032c71aff..c81dc0e52 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -80,7 +80,7 @@ bool Freezer::IsActive() const { } void Freezer::Clear() { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; LOG_DEBUG(Common_Memory, "Clearing all frozen memory values."); @@ -88,7 +88,7 @@ void Freezer::Clear() { } u64 Freezer::Freeze(VAddr address, u32 width) { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; const auto current_value = MemoryReadWidth(memory, width, address); entries.push_back({address, width, current_value}); @@ -101,7 +101,7 @@ u64 Freezer::Freeze(VAddr address, u32 width) { } void Freezer::Unfreeze(VAddr address) { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); @@ -109,13 +109,13 @@ void Freezer::Unfreeze(VAddr address) { } bool Freezer::IsFrozen(VAddr address) const { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; return FindEntry(address) != entries.cend(); } void Freezer::SetFrozenValue(VAddr address, u64 value) { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; const auto iter = FindEntry(address); @@ -132,7 +132,7 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) { } std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; const auto iter = FindEntry(address); @@ -144,7 +144,7 @@ std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { } std::vector<Freezer::Entry> Freezer::GetEntries() const { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; return entries; } @@ -165,7 +165,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { return; } - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; for (const auto& entry : entries) { LOG_DEBUG(Common_Memory, @@ -178,7 +178,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { } void Freezer::FillEntryReads() { - std::lock_guard lock{entries_mutex}; + std::scoped_lock lock{entries_mutex}; LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values."); diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index c17ea305e..b3e4c3f64 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -62,7 +62,7 @@ public: bool UpdateMotion(SDL_ControllerSensorEvent event) { constexpr float gravity_constant = 9.80665f; - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const u64 time_difference = event.timestamp - last_motion_update; last_motion_update = event.timestamp; switch (event.sensor) { @@ -241,7 +241,7 @@ private: }; std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) { - std::lock_guard lock{joystick_map_mutex}; + std::scoped_lock lock{joystick_map_mutex}; const auto it = joystick_map.find(guid); if (it != joystick_map.end()) { @@ -263,7 +263,7 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); const std::string guid = GetGUID(sdl_joystick); - std::lock_guard lock{joystick_map_mutex}; + std::scoped_lock lock{joystick_map_mutex}; const auto map_it = joystick_map.find(guid); if (map_it == joystick_map.end()) { @@ -297,7 +297,7 @@ void SDLDriver::InitJoystick(int joystick_index) { const std::string guid = GetGUID(sdl_joystick); - std::lock_guard lock{joystick_map_mutex}; + std::scoped_lock lock{joystick_map_mutex}; if (joystick_map.find(guid) == joystick_map.end()) { auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); PreSetController(joystick->GetPadIdentifier()); @@ -326,7 +326,7 @@ void SDLDriver::InitJoystick(int joystick_index) { void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) { const std::string guid = GetGUID(sdl_joystick); - std::lock_guard lock{joystick_map_mutex}; + std::scoped_lock lock{joystick_map_mutex}; // This call to guid is safe since the joystick is guaranteed to be in the map const auto& joystick_guid_list = joystick_map[guid]; const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(), @@ -392,7 +392,7 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { } void SDLDriver::CloseJoysticks() { - std::lock_guard lock{joystick_map_mutex}; + std::scoped_lock lock{joystick_map_mutex}; joystick_map.clear(); } diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp index 738022ece..a16cca33d 100644 --- a/src/input_common/input_engine.cpp +++ b/src/input_common/input_engine.cpp @@ -8,37 +8,37 @@ namespace InputCommon { void InputEngine::PreSetController(const PadIdentifier& identifier) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; controller_list.try_emplace(identifier); } void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); controller.buttons.try_emplace(button, false); } void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); controller.hat_buttons.try_emplace(button, u8{0}); } void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); controller.axes.try_emplace(axis, 0.0f); } void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); controller.motions.try_emplace(motion); } void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.buttons.insert_or_assign(button, value); @@ -49,7 +49,7 @@ void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool va void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.hat_buttons.insert_or_assign(button, value); @@ -60,7 +60,7 @@ void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 v void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.axes.insert_or_assign(axis, value); @@ -71,7 +71,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.battery = value; @@ -82,7 +82,7 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; ControllerData& controller = controller_list.at(identifier); if (!configuring) { controller.motions.insert_or_assign(motion, value); @@ -92,7 +92,7 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B } bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), @@ -109,7 +109,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { } bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), @@ -126,7 +126,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d } f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), @@ -143,7 +143,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { } Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), @@ -155,7 +155,7 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif } BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto controller_iter = controller_list.find(identifier); if (controller_iter == controller_list.cend()) { LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), @@ -186,7 +186,7 @@ void InputEngine::ResetAnalogState() { } void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) { @@ -214,7 +214,7 @@ void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int but } void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) { @@ -243,7 +243,7 @@ void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int } void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) { @@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, [[maybe_unused]] Common::Input::BatteryLevel value) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) { @@ -284,7 +284,7 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, const BasicMotion& value) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; for (const auto& poller_pair : callback_list) { const InputIdentifier& poller = poller_pair.second; if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) { @@ -346,18 +346,18 @@ const std::string& InputEngine::GetEngineName() const { } int InputEngine::SetCallback(InputIdentifier input_identifier) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; callback_list.insert_or_assign(last_callback_key, std::move(input_identifier)); return last_callback_key++; } void InputEngine::SetMappingCallback(MappingCallback callback) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; mapping_callback = std::move(callback); } void InputEngine::DeleteCallback(int key) { - std::lock_guard lock{mutex_callback}; + std::scoped_lock lock{mutex_callback}; const auto& iterator = callback_list.find(key); if (iterator == callback_list.end()) { LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index 0c1fbc7b1..282668b36 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -35,6 +35,15 @@ std::string_view OutputVertexIndex(EmitContext& ctx) { return ctx.stage == Stage::TessellationControl ? "[gl_InvocationID]" : ""; } +std::string ChooseCbuf(EmitContext& ctx, const IR::Value& binding, std::string_view index) { + if (binding.IsImmediate()) { + return fmt::format("{}_cbuf{}[{}]", ctx.stage_name, binding.U32(), index); + } else { + const auto binding_var{ctx.var_alloc.Consume(binding)}; + return fmt::format("GetCbufIndirect({},{})", binding_var, index); + } +} + void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding, const IR::Value& offset, u32 num_bits, std::string_view cast = {}, std::string_view bit_offset = {}) { @@ -55,8 +64,8 @@ void GetCbuf(EmitContext& ctx, std::string_view ret, const IR::Value& binding, const auto swizzle{is_immediate ? fmt::format(".{}", OffsetSwizzle(offset.U32())) : fmt::format("[({}>>2)%4]", offset_var)}; - const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())}; - const auto cbuf_cast{fmt::format("{}({}[{}]{{}})", cast, cbuf, index)}; + const auto cbuf{ChooseCbuf(ctx, binding, index)}; + const auto cbuf_cast{fmt::format("{}({}{{}})", cast, cbuf)}; const auto extraction{num_bits == 32 ? cbuf_cast : fmt::format("bitfieldExtract({},int({}),{})", cbuf_cast, bit_offset, num_bits)}; @@ -140,9 +149,9 @@ void EmitGetCbufF32(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, const IR::Value& offset) { - const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())}; const auto cast{ctx.profile.has_gl_cbuf_ftou_bug ? "" : "ftou"}; if (offset.IsImmediate()) { + const auto cbuf{fmt::format("{}_cbuf{}", ctx.stage_name, binding.U32())}; static constexpr u32 cbuf_size{0x10000}; const u32 u32_offset{offset.U32()}; const s32 signed_offset{static_cast<s32>(offset.U32())}; @@ -162,17 +171,17 @@ void EmitGetCbufU32x2(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding return; } const auto offset_var{ctx.var_alloc.Consume(offset)}; + const auto cbuf{ChooseCbuf(ctx, binding, fmt::format("{}>>4", offset_var))}; if (!ctx.profile.has_gl_component_indexing_bug) { - ctx.AddU32x2("{}=uvec2({}({}[{}>>4][({}>>2)%4]),{}({}[({}+4)>>4][(({}+4)>>2)%4]));", inst, - cast, cbuf, offset_var, offset_var, cast, cbuf, offset_var, offset_var); + ctx.AddU32x2("{}=uvec2({}({}[({}>>2)%4]),{}({}[(({}+4)>>2)%4]));", inst, cast, cbuf, + offset_var, cast, cbuf, offset_var); return; } const auto ret{ctx.var_alloc.Define(inst, GlslVarType::U32x2)}; const auto cbuf_offset{fmt::format("{}>>2", offset_var)}; for (u32 swizzle = 0; swizzle < 4; ++swizzle) { - ctx.Add("if(({}&3)=={}){}=uvec2({}({}[{}>>4].{}),{}({}[({}+4)>>4].{}));", cbuf_offset, - swizzle, ret, cast, cbuf, offset_var, "xyzw"[swizzle], cast, cbuf, offset_var, - "xyzw"[(swizzle + 1) % 4]); + ctx.Add("if(({}&3)=={}){}=uvec2({}({}.{}),{}({}.{}));", cbuf_offset, swizzle, ret, cast, + cbuf, "xyzw"[swizzle], cast, cbuf, "xyzw"[(swizzle + 1) % 4]); } } diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index e816a93ec..17266f40d 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -359,6 +359,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile header += "layout(location=0) uniform vec4 scaling;"; } DefineConstantBuffers(bindings); + DefineConstantBufferIndirect(); DefineStorageBuffers(bindings); SetupImages(bindings); SetupTextures(bindings); @@ -436,6 +437,24 @@ void EmitContext::DefineConstantBuffers(Bindings& bindings) { } } +void EmitContext::DefineConstantBufferIndirect() { + if (!info.uses_cbuf_indirect) { + return; + } + + header += profile.has_gl_cbuf_ftou_bug ? "uvec4 " : "vec4 "; + header += "GetCbufIndirect(uint binding, uint offset){" + "switch(binding){" + "default:"; + + for (const auto& desc : info.constant_buffer_descriptors) { + header += + fmt::format("case {}:return {}_cbuf{}[offset];", desc.index, stage_name, desc.index); + } + + header += "}}"; +} + void EmitContext::DefineStorageBuffers(Bindings& bindings) { if (info.storage_buffers_descriptors.empty()) { return; diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.h b/src/shader_recompiler/backend/glsl/glsl_emit_context.h index d9b639d29..2b13db6e6 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.h +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.h @@ -162,6 +162,7 @@ public: private: void SetupExtensions(); void DefineConstantBuffers(Bindings& bindings); + void DefineConstantBufferIndirect(); void DefineStorageBuffers(Bindings& bindings); void DefineGenericOutput(size_t index, u32 invocations); void DefineHelperFunctions(); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 28f6a6184..9c83cd2e4 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -1043,15 +1043,15 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) { const Id merge_label{OpLabel()}; const Id uniform_type{uniform_types.*member_ptr}; - std::array<Id, Info::MAX_CBUFS> buf_labels; - std::array<Sirit::Literal, Info::MAX_CBUFS> buf_literals; - for (u32 i = 0; i < Info::MAX_CBUFS; i++) { + std::array<Id, Info::MAX_INDIRECT_CBUFS> buf_labels; + std::array<Sirit::Literal, Info::MAX_INDIRECT_CBUFS> buf_literals; + for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) { buf_labels[i] = OpLabel(); buf_literals[i] = Sirit::Literal{i}; } OpSelectionMerge(merge_label, spv::SelectionControlMask::MaskNone); OpSwitch(binding, buf_labels[0], buf_literals, buf_labels); - for (u32 i = 0; i < Info::MAX_CBUFS; i++) { + for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) { AddLabel(buf_labels[i]); const Id cbuf{cbufs[i].*member_ptr}; const Id access_chain{OpAccessChain(uniform_type, cbuf, u32_zero_value, offset)}; @@ -1064,22 +1064,23 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) { return func; }}; IR::Type types{info.used_indirect_cbuf_types}; - if (True(types & IR::Type::U8)) { + bool supports_aliasing = profile.support_descriptor_aliasing; + if (supports_aliasing && True(types & IR::Type::U8)) { load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8); } - if (True(types & IR::Type::U16)) { + if (supports_aliasing && True(types & IR::Type::U16)) { load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16); } - if (True(types & IR::Type::F32)) { + if (supports_aliasing && True(types & IR::Type::F32)) { load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32); } - if (True(types & IR::Type::U32)) { + if (supports_aliasing && True(types & IR::Type::U32)) { load_const_func_u32 = make_accessor(U32[1], &UniformDefinitions::U32); } - if (True(types & IR::Type::U32x2)) { + if (supports_aliasing && True(types & IR::Type::U32x2)) { load_const_func_u32x2 = make_accessor(U32[2], &UniformDefinitions::U32x2); } - if (True(types & IR::Type::U32x4)) { + if (!supports_aliasing || True(types & IR::Type::U32x4)) { load_const_func_u32x4 = make_accessor(U32[4], &UniformDefinitions::U32x4); } } 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 0b2c60842..16278faab 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp @@ -32,13 +32,8 @@ void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) { void AddRegisterIndexedLdc(Info& info) { info.uses_cbuf_indirect = true; - // The shader can use any possible constant buffer - info.constant_buffer_mask = (1 << Info::MAX_CBUFS) - 1; - - auto& cbufs{info.constant_buffer_descriptors}; - cbufs.clear(); - for (u32 i = 0; i < Info::MAX_CBUFS; i++) { - cbufs.push_back(ConstantBufferDescriptor{.index = i, .count = 1}); + for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) { + AddConstantBufferDescriptor(info, i, 1); // The shader can use any possible access size info.constant_buffer_used_sizes[i] = 0x10'000; diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index 9d36bd9eb..a3a09c71c 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -105,6 +105,7 @@ struct ImageDescriptor { using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; struct Info { + static constexpr size_t MAX_INDIRECT_CBUFS{14}; static constexpr size_t MAX_CBUFS{18}; static constexpr size_t MAX_SSBOS{32}; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index ba9ba082f..789af452d 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -230,7 +230,7 @@ struct GPU::Impl { void IncrementSyncPoint(u32 syncpoint_id) { auto& syncpoint = syncpoints.at(syncpoint_id); syncpoint++; - std::lock_guard lock{sync_mutex}; + std::scoped_lock lock{sync_mutex}; sync_cv.notify_all(); auto& interrupt = syncpt_interrupts.at(syncpoint_id); if (!interrupt.empty()) { @@ -252,7 +252,7 @@ struct GPU::Impl { } void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) { - std::lock_guard lock{sync_mutex}; + std::scoped_lock lock{sync_mutex}; auto& interrupt = syncpt_interrupts.at(syncpoint_id); bool contains = std::any_of(interrupt.begin(), interrupt.end(), [value](u32 in_value) { return in_value == value; }); @@ -263,7 +263,7 @@ struct GPU::Impl { } [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) { - std::lock_guard lock{sync_mutex}; + std::scoped_lock lock{sync_mutex}; auto& interrupt = syncpt_interrupts.at(syncpoint_id); const auto iter = std::find_if(interrupt.begin(), interrupt.end(), diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 9547f277a..4e8999915 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -56,7 +56,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system, if (next.block) { // We have to lock the write_lock to ensure that the condition_variable wait not get a // race between the check and the lock itself. - std::lock_guard lk(state.write_lock); + std::scoped_lock lk{state.write_lock}; state.cv.notify_all(); } } diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index af05d47d1..190fc6aea 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -18,6 +18,7 @@ set(SHADER_FILES full_screen_triangle.vert fxaa.frag fxaa.vert + opengl_convert_s8d24.comp opengl_copy_bc4.comp opengl_present.frag opengl_present.vert diff --git a/src/video_core/host_shaders/opengl_convert_s8d24.comp b/src/video_core/host_shaders/opengl_convert_s8d24.comp new file mode 100644 index 000000000..83e1ab176 --- /dev/null +++ b/src/video_core/host_shaders/opengl_convert_s8d24.comp @@ -0,0 +1,18 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 430 core + +layout(local_size_x = 16, local_size_y = 8) in; + +layout(binding = 0, rgba8ui) restrict uniform uimage2D destination; +layout(location = 0) uniform uvec3 size; + +void main() { + if (any(greaterThanEqual(gl_GlobalInvocationID, size))) { + return; + } + uvec4 components = imageLoad(destination, ivec2(gl_GlobalInvocationID.xy)); + imageStore(destination, ivec2(gl_GlobalInvocationID.xy), components.wxyz); +} diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 9e6732abd..fd40966d5 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -253,7 +253,7 @@ GraphicsPipeline::GraphicsPipeline( } } if (in_parallel) { - std::lock_guard lock{built_mutex}; + std::scoped_lock lock{built_mutex}; built_fence.Create(); // Flush this context to ensure compilation commands and fence are in the GPU pipe. glFlush(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index e6f9ece8b..8ef79753f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -520,6 +520,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different"); // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different"); + screen_info.texture.width = image_view->size.width; + screen_info.texture.height = image_view->size.height; screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D); screen_info.display_srgb = VideoCore::Surface::IsPixelFormatSRGB(image_view->format); return true; @@ -557,12 +559,19 @@ void RasterizerOpenGL::SyncViewport() { const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports; const bool dirty_clip_control = flags[Dirty::ClipControl]; - if (dirty_clip_control || flags[Dirty::FrontFace]) { + if (dirty_viewport || dirty_clip_control || flags[Dirty::FrontFace]) { flags[Dirty::FrontFace] = false; GLenum mode = MaxwellToGL::FrontFace(regs.front_face); + bool flip_faces = false; if (regs.screen_y_control.triangle_rast_flip != 0 && regs.viewport_transform[0].scale_y < 0.0f) { + flip_faces = !flip_faces; + } + if (regs.viewport_transform[0].scale_z < 0.0f) { + flip_faces = !flip_faces; + } + if (flip_faces) { switch (mode) { case GL_CW: mode = GL_CCW; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 6423992c3..05c5e702c 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -258,7 +258,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { ctx->pools.ReleaseContents(); auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; - std::lock_guard lock{state.mutex}; + std::scoped_lock lock{state.mutex}; if (pipeline) { compute_cache.emplace(key, std::move(pipeline)); } @@ -280,7 +280,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, } ctx->pools.ReleaseContents(); auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; - std::lock_guard lock{state.mutex}; + std::scoped_lock lock{state.mutex}; if (pipeline) { graphics_cache.emplace(key, std::move(pipeline)); } diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 8f9a65beb..d12076358 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -409,8 +409,8 @@ ImageBufferMap::~ImageBufferMap() { TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager& program_manager, StateTracker& state_tracker_) - : device{device_}, state_tracker{state_tracker_}, - util_shaders(program_manager), resolution{Settings::values.resolution_info} { + : device{device_}, state_tracker{state_tracker_}, util_shaders(program_manager), + format_conversion_pass{util_shaders}, resolution{Settings::values.resolution_info} { static constexpr std::array TARGETS{GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D}; for (size_t i = 0; i < TARGETS.size(); ++i) { const GLenum target = TARGETS[i]; @@ -1325,6 +1325,9 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM Framebuffer::~Framebuffer() = default; +FormatConversionPass::FormatConversionPass(UtilShaders& util_shaders_) + : util_shaders{util_shaders_} {} + void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image, std::span<const VideoCommon::ImageCopy> copies) { const GLenum dst_target = ImageTarget(dst_image.info); @@ -1357,6 +1360,12 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image, dst_origin.z, region.width, region.height, region.depth, dst_image.GlFormat(), dst_image.GlType(), nullptr); } + + // Swap component order of S8D24 to ABGR8 reinterprets + if (src_image.info.format == PixelFormat::D24_UNORM_S8_UINT && + dst_image.info.format == PixelFormat::A8B8G8R8_UNORM) { + util_shaders.ConvertS8D24(dst_image, copies); + } } } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 53088b66e..672fa8dde 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -55,13 +55,14 @@ struct FormatProperties { class FormatConversionPass { public: - FormatConversionPass() = default; + explicit FormatConversionPass(UtilShaders& util_shaders); ~FormatConversionPass() = default; void ConvertImage(Image& dst_image, Image& src_image, std::span<const VideoCommon::ImageCopy> copies); private: + UtilShaders& util_shaders; OGLBuffer intermediate_pbo; size_t pbo_size{}; }; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index f8f29013a..3a3c213bb 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -208,6 +208,8 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf // Framebuffer orientation handling framebuffer_transform_flags = framebuffer.transform_flags; framebuffer_crop_rect = framebuffer.crop_rect; + framebuffer_width = framebuffer.width; + framebuffer_height = framebuffer.height; const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; screen_info.was_accelerated = @@ -480,9 +482,12 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { ASSERT_MSG(framebuffer_crop_rect.top == 0, "Unimplemented"); ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented"); + f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); + f32 scale_v = + static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); + // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering // (e.g. handheld mode) on a 1920x1080 framebuffer. - f32 scale_u = 1.f, scale_v = 1.f; if (framebuffer_crop_rect.GetWidth() > 0) { scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / static_cast<f32>(screen_info.texture.width); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index aa206878b..ae9558a33 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -137,6 +137,8 @@ private: /// Used for transforming the framebuffer orientation Service::android::BufferTransformFlags framebuffer_transform_flags{}; Common::Rectangle<int> framebuffer_crop_rect; + u32 framebuffer_width; + u32 framebuffer_height; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp index 897c380b3..04c482a09 100644 --- a/src/video_core/renderer_opengl/util_shaders.cpp +++ b/src/video_core/renderer_opengl/util_shaders.cpp @@ -13,6 +13,7 @@ #include "video_core/host_shaders/astc_decoder_comp.h" #include "video_core/host_shaders/block_linear_unswizzle_2d_comp.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_comp.h" +#include "video_core/host_shaders/opengl_convert_s8d24_comp.h" #include "video_core/host_shaders/opengl_copy_bc4_comp.h" #include "video_core/host_shaders/pitch_unswizzle_comp.h" #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -50,7 +51,8 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_) block_linear_unswizzle_2d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_2D_COMP)), block_linear_unswizzle_3d_program(MakeProgram(BLOCK_LINEAR_UNSWIZZLE_3D_COMP)), pitch_unswizzle_program(MakeProgram(PITCH_UNSWIZZLE_COMP)), - copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)) { + copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)), + convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)) { const auto swizzle_table = Tegra::Texture::MakeSwizzleTable(); swizzle_table_buffer.Create(); glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0); @@ -248,6 +250,26 @@ void UtilShaders::CopyBC4(Image& dst_image, Image& src_image, std::span<const Im program_manager.RestoreGuestCompute(); } +void UtilShaders::ConvertS8D24(Image& dst_image, std::span<const ImageCopy> copies) { + static constexpr GLuint BINDING_DESTINATION = 0; + static constexpr GLuint LOC_SIZE = 0; + + program_manager.BindComputeProgram(convert_s8d24_program.handle); + for (const ImageCopy& copy : copies) { + ASSERT(copy.src_subresource.base_layer == 0); + ASSERT(copy.src_subresource.num_layers == 1); + ASSERT(copy.dst_subresource.base_layer == 0); + ASSERT(copy.dst_subresource.num_layers == 1); + + glUniform3ui(LOC_SIZE, copy.extent.width, copy.extent.height, copy.extent.depth); + glBindImageTexture(BINDING_DESTINATION, dst_image.StorageHandle(), + copy.dst_subresource.base_level, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA8UI); + glDispatchCompute(Common::DivCeil(copy.extent.width, 16u), + Common::DivCeil(copy.extent.height, 8u), copy.extent.depth); + } + program_manager.RestoreGuestCompute(); +} + GLenum StoreFormat(u32 bytes_per_block) { switch (bytes_per_block) { case 1: diff --git a/src/video_core/renderer_opengl/util_shaders.h b/src/video_core/renderer_opengl/util_shaders.h index 5de95ea7a..5c132e67f 100644 --- a/src/video_core/renderer_opengl/util_shaders.h +++ b/src/video_core/renderer_opengl/util_shaders.h @@ -39,6 +39,8 @@ public: void CopyBC4(Image& dst_image, Image& src_image, std::span<const VideoCommon::ImageCopy> copies); + void ConvertS8D24(Image& dst_image, std::span<const VideoCommon::ImageCopy> copies); + private: ProgramManager& program_manager; @@ -49,6 +51,7 @@ private: OGLProgram block_linear_unswizzle_3d_program; OGLProgram pitch_unswizzle_program; OGLProgram copy_bc4_program; + OGLProgram convert_s8d24_program; }; GLenum StoreFormat(u32 bytes_per_block); diff --git a/src/video_core/renderer_vulkan/pipeline_statistics.cpp b/src/video_core/renderer_vulkan/pipeline_statistics.cpp index bfec931a6..7ccadf084 100644 --- a/src/video_core/renderer_vulkan/pipeline_statistics.cpp +++ b/src/video_core/renderer_vulkan/pipeline_statistics.cpp @@ -57,7 +57,7 @@ void PipelineStatistics::Collect(VkPipeline pipeline) { stage_stats.basic_block_count = GetUint64(statistic); } } - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; collected_stats.push_back(stage_stats); } } @@ -66,7 +66,7 @@ void PipelineStatistics::Report() const { double num{}; Stats total; { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; for (const Stats& stats : collected_stats) { total.code_size += stats.code_size; total.register_count += stats.register_count; diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index d893c1952..b866e9103 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -1406,8 +1406,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0); UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); - f32 scale_u = 1.0f; - f32 scale_v = 1.0f; + f32 scale_u = static_cast<f32>(framebuffer.width) / static_cast<f32>(screen_info.width); + f32 scale_v = static_cast<f32>(framebuffer.height) / static_cast<f32>(screen_info.height); + // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering // (e.g. handheld mode) on a 1920x1080 framebuffer. if (!fsr) { diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index de36bcdb7..97b3594c2 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -77,7 +77,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript if (pipeline_statistics) { pipeline_statistics->Collect(*pipeline); } - std::lock_guard lock{build_mutex}; + std::scoped_lock lock{build_mutex}; is_built = true; build_condvar.notify_one(); if (shader_notify) { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index d514b71d0..8959d6059 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -258,7 +258,7 @@ GraphicsPipeline::GraphicsPipeline( pipeline_statistics->Collect(*pipeline); } - std::lock_guard lock{build_mutex}; + std::scoped_lock lock{build_mutex}; is_built = true; build_condvar.notify_one(); if (shader_notify) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 27e59df73..336d1e9dc 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -404,7 +404,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { ShaderPools pools; auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)}; - std::lock_guard lock{state.mutex}; + std::scoped_lock lock{state.mutex}; if (pipeline) { compute_cache.emplace(key, std::move(pipeline)); } @@ -434,7 +434,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), state.statistics.get(), false)}; - std::lock_guard lock{state.mutex}; + std::scoped_lock lock{state.mutex}; graphics_cache.emplace(key, std::move(pipeline)); ++state.built; if (state.has_loaded) { diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp index 451ffe019..d22bb6694 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -36,7 +36,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {} VkRenderPass RenderPassCache::Get(const RenderPassKey& key) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; const auto [pair, is_new] = cache.try_emplace(key); if (!is_new) { return *pair->second; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index ad320991b..6a9416457 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -73,7 +73,7 @@ void VKScheduler::DispatchWork() { return; } { - std::lock_guard lock{work_mutex}; + std::scoped_lock lock{work_mutex}; work_queue.push(std::move(chunk)); } work_cv.notify_one(); @@ -157,7 +157,7 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) { if (has_submit) { AllocateWorkerCommandBuffer(); } - std::lock_guard reserve_lock{reserve_mutex}; + std::scoped_lock reserve_lock{reserve_mutex}; chunk_reserve.push_back(std::move(work)); } while (!stop_token.stop_requested()); } @@ -282,7 +282,7 @@ void VKScheduler::EndRenderPass() { } void VKScheduler::AcquireNewChunk() { - std::lock_guard lock{reserve_mutex}; + std::scoped_lock lock{reserve_mutex}; if (chunk_reserve.empty()) { chunk = std::make_unique<CommandChunk>(); return; diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index 87636857d..75031767a 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -25,7 +25,7 @@ void ShaderCache::InvalidateRegion(VAddr addr, size_t size) { } void ShaderCache::OnCPUWrite(VAddr addr, size_t size) { - std::lock_guard lock{invalidation_mutex}; + std::scoped_lock lock{invalidation_mutex}; InvalidatePagesInRegion(addr, size); } diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 4208bd044..58b0c2f10 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -32,7 +32,7 @@ constexpr std::size_t TIMEOUT_SECONDS = 30; struct Client::Impl { Impl(std::string host, std::string username, std::string token) : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} { - std::lock_guard lock{jwt_cache.mutex}; + std::scoped_lock lock{jwt_cache.mutex}; if (this->username == jwt_cache.username && this->token == jwt_cache.token) { jwt = jwt_cache.jwt; } @@ -147,7 +147,7 @@ struct Client::Impl { if (result.result_code != WebResult::Code::Success) { LOG_ERROR(WebService, "UpdateJWT failed"); } else { - std::lock_guard lock{jwt_cache.mutex}; + std::scoped_lock lock{jwt_cache.mutex}; jwt_cache.username = username; jwt_cache.token = token; jwt_cache.jwt = jwt = result.returned_data; diff --git a/src/yuzu/configuration/configure_cpu.ui b/src/yuzu/configuration/configure_cpu.ui index 5d80a8c91..8ae569ee6 100644 --- a/src/yuzu/configuration/configure_cpu.ui +++ b/src/yuzu/configuration/configure_cpu.ui @@ -52,6 +52,11 @@ <string>Unsafe</string> </property> </item> + <item> + <property name="text"> + <string>Paranoid (disables most optimizations)</string> + </property> + </item> </widget> </item> </layout> diff --git a/src/yuzu/util/controller_navigation.cpp b/src/yuzu/util/controller_navigation.cpp index c2b13123d..9a1868cae 100644 --- a/src/yuzu/util/controller_navigation.cpp +++ b/src/yuzu/util/controller_navigation.cpp @@ -39,7 +39,7 @@ void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_b } void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) { - std::lock_guard lock{mutex}; + std::scoped_lock lock{mutex}; if (!Settings::values.controller_navigation) { return; } |