diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 00:45:20 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 00:45:20 -0400 |
| commit | d56a30137df183de548ac49151ddeeecc1ab0f16 (patch) | |
| tree | bf9179b177db21486138c333e4e500bebe391167 /src | |
| parent | 322a5afce3d6c5fbcf16ed9a8914805087b33a55 (diff) | |
nx2: memo for compute macros; one lock per inline upload; cheaper shader invalidation
- Compute engine macros replay repeated calls like 3D ones (title 1's
compute macro path fell from ~27% to ~14% of the GPU thread).
- AccelerateInlineToMemory takes the buffer and texture cache mutexes
together instead of one after the other.
- ShaderCache::InvalidatePagesInRegion looks at the rejected programs
only when the write can reach one (bounds kept as they are added);
it ran a locked scan for every inline upload and pre-draw
invalidation.
Inline uploads went from ~10% to ~4% of the GPU thread.
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/engines/kepler_compute.cpp | 55 | ||||
| -rw-r--r-- | src/video_core/engines/kepler_compute.h | 1 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_rasterizer.cpp | 6 | ||||
| -rw-r--r-- | src/video_core/shader_cache.cpp | 9 | ||||
| -rw-r--r-- | src/video_core/shader_cache.h | 4 |
5 files changed, 62 insertions, 13 deletions
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 39c033d989..7792bc9b5d 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -147,18 +147,57 @@ bool KeplerCompute::ProcessNativeMethod(Core::System& system, u32 method, u32 ar } if (native_data_ram) native_macro->native_interpreter.active_dram = native_data_ram; const u32 dram0_before = (*native_macro->native_interpreter.active_dram)[0x27]; + const u32 start = native_macro_positions[entry]; + const auto emit = [&](u32 address, u32 value) { + static u32 diagnostic_writes{}; + static const bool log_emits = std::getenv("EDEN_NX2_SEM_WATCH_EMITS") != nullptr; + if (watched && log_emits) LOG_INFO(HW_GPU, "Semaphore watch: compute macro emit {:#x} = {:#x}", address, value); + if (diagnostic_writes++ < 4096) LOG_DEBUG(HW_GPU, "Native compute MME emit method={:#x}, value={:#x}", address, value); + if (address >= 0xe00) return false; + CallMethod(system, address, value, true); + return true; + }; + // Repeated calls replay their writes, as for 3D macros (macro_memo.h). + static const bool use_memo = [] { + const char* value = std::getenv("EDEN_NX2_MME_MEMO"); + return value == nullptr || value[0] != '0'; + }(); + auto& memo = native_macro->native_memo; + if (use_memo && !watched) { + memo.Synchronize(native_macro->native_code_generation); + if (std::remove_reference_t<decltype(memo)>::Effects effects; + memo.Find(start, native_parameters, *native_macro->native_interpreter.active_dram, + effects)) { + auto& data_ram = *native_macro->native_interpreter.active_dram; + for (size_t i = 0; i < effects.data_ram_writes.size(); i += 2) { + data_ram[effects.data_ram_writes[i]] = effects.data_ram_writes[i + 1]; + } + for (size_t i = 0; i < effects.writes.size(); i += 2) { + emit(effects.writes[i], effects.writes[i + 1]); + } + native_parameters.clear(); + return true; + } + native_macro->native_writes.clear(); + } + // The interpreter consumes native_parameters as its FIFO: keep the call's own parameters. + native_memo_parameters.assign(native_parameters.begin(), native_parameters.end()); + const u64 code_generation = native_macro->native_code_generation; const auto result = native_macro->native_interpreter.Execute(native_macro->native_code, - native_macro_positions[entry], native_parameters, + start, native_parameters, [&](u32 address) { return address < native_registers.size() ? native_registers[address] : 0U; }, [&](u32 address, u32 value) { - static u32 diagnostic_writes{}; - static const bool log_emits = std::getenv("EDEN_NX2_SEM_WATCH_EMITS") != nullptr; - if (watched && log_emits) LOG_INFO(HW_GPU, "Semaphore watch: compute macro emit {:#x} = {:#x}", address, value); - if (diagnostic_writes++ < 4096) LOG_DEBUG(HW_GPU, "Native compute MME emit method={:#x}, value={:#x}", address, value); - if (address >= 0xe00) return false; - CallMethod(system, address, value, true); - return true; + if (use_memo) { + native_macro->native_writes.emplace_back(address, value); + } + return emit(address, value); }); + const auto& interpreter = native_macro->native_interpreter; + if (use_memo && !watched && result.success && !interpreter.read_registers && + !interpreter.reread_data_ram && code_generation == native_macro->native_code_generation) { + memo.Store(start, native_memo_parameters, interpreter.data_ram_reads, + interpreter.data_ram_writes, native_macro->native_writes); + } if (static const bool mme_dump = std::getenv("EDEN_NX2_MME_DUMP") != nullptr; mme_dump && (*native_macro->native_interpreter.active_dram)[0x27] != dram0_before) { LOG_INFO(HW_GPU, "MME data RAM[0x27] {:#x} -> {:#x} by engine {} macro start={:#x}", diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 7cecab378b..5ebf8edf38 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h @@ -254,6 +254,7 @@ private: std::array<u32, 0x1000> native_registers{}; std::span<u32> native_macro_positions; std::vector<u32> native_parameters; + std::vector<u32> native_memo_parameters; ///< The current call's parameters, for the memo std::optional<u32> native_executing_macro; u32 native_bind_pointer{}; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index decfb0d136..6bd267e566 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1251,13 +1251,11 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si } gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); { - std::unique_lock<std::recursive_mutex> lock{buffer_cache.mutex}; + // Both caches under one lock (title 1 makes thousands of inline uploads a second). + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { buffer_cache.WriteMemory(*cpu_addr, copy_size); } - } - { - std::scoped_lock lock_texture{texture_cache.mutex}; texture_cache.WriteMemory(*cpu_addr, copy_size); } pipeline_cache.InvalidateRegion(*cpu_addr, copy_size); diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp index 73a2bf6646..dfe80abac3 100644 --- a/src/video_core/shader_cache.cpp +++ b/src/video_core/shader_cache.cpp @@ -180,6 +180,10 @@ bool ShaderCache::IsRejected(VAddr addr) { void ShaderCache::RememberRejection(VAddr addr) { std::scoped_lock lock{rejected_mutex}; rejected_native.emplace(addr, std::current_exception()); + rejected_lowest.store((std::min)(rejected_lowest.load(std::memory_order_relaxed), addr), + std::memory_order_relaxed); + rejected_highest.store((std::max)(rejected_highest.load(std::memory_order_relaxed), addr), + std::memory_order_relaxed); } void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result, @@ -232,7 +236,10 @@ void ShaderCache::Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) { const VAddr addr_end = addr + size; - { + // Rejected programs are few and in a few places: look at them only for writes that can + // reach them (this runs for every inline upload and pre-draw invalidation). + if (addr_end > rejected_lowest.load(std::memory_order_relaxed) && + addr <= rejected_highest.load(std::memory_order_relaxed)) { std::scoped_lock lock{rejected_mutex}; boost::unordered::erase_if(rejected_native, [&](const auto& rejected) { return rejected.first >= addr && rejected.first < addr_end; diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index 9ffb7bb44d..f4179dd435 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h @@ -7,6 +7,7 @@ #pragma once #include <array> +#include <atomic> #include <exception> #include <memory> #include <mutex> @@ -165,6 +166,9 @@ private: /// first error instead of lowering again; dropped when guest memory there is written. std::mutex rejected_mutex; ::Common::unordered_map<u64, std::exception_ptr> rejected_native; + /// Bounds of every address ever rejected (never shrinks): a quick test for invalidations. + std::atomic<VAddr> rejected_lowest{~VAddr{}}; + std::atomic<VAddr> rejected_highest{}; }; } // namespace VideoCommon |
