aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 00:45:20 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 00:45:20 -0400
commitd56a30137df183de548ac49151ddeeecc1ab0f16 (patch)
treebf9179b177db21486138c333e4e500bebe391167 /src
parent322a5afce3d6c5fbcf16ed9a8914805087b33a55 (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.cpp55
-rw-r--r--src/video_core/engines/kepler_compute.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp6
-rw-r--r--src/video_core/shader_cache.cpp9
-rw-r--r--src/video_core/shader_cache.h4
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