aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/shader_cache.cpp
diff options
context:
space:
mode:
authornéo Luigi <85024393+neoluigi4123@users.noreply.github.com>2026-11-03 00:49:36 +0100
committernéo Luigi <85024393+neoluigi4123@users.noreply.github.com>2026-11-03 00:49:36 +0100
commitc84e5caf82a786f748c233e27b7f4ed9aaad9f44 (patch)
tree3ec9cab90d74292445d0a076370933bac946fc4d /src/video_core/shader_cache.cpp
parent2b413a46a2820f0a646baaa8dbd683009f096f32 (diff)
parentefa7c19e59839f0d464c2e9bd41f9ca8f867fef0 (diff)
Merge remote-tracking branch 'origin/nx2' into neo.ai/vibration-fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> # Conflicts: # src/core/hle/service/hid/hid_server.cpp # src/core/hle/service/hid/hid_server.h
Diffstat (limited to 'src/video_core/shader_cache.cpp')
-rw-r--r--src/video_core/shader_cache.cpp61
1 files changed, 55 insertions, 6 deletions
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index dfe80abac3..5d41dc9697 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -6,6 +6,7 @@
#include <algorithm>
#include <cstring>
+#include <functional>
#include <string>
#include <unordered_set>
#include <fmt/ranges.h>
@@ -14,6 +15,7 @@
#include <boost/container/small_vector.hpp>
#include "common/assert.h"
+#include "common/cityhash.h"
#include "shader_recompiler/frontend/maxwell/control_flow.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/object_pool.h"
@@ -35,6 +37,17 @@ void ShaderCache::InvalidateRegion(VAddr addr, size_t size) {
RemovePendingShaders();
}
+void ShaderCache::InvalidateRegions(std::span<const std::pair<DAddr, size_t>> sequences) {
+ if (sequences.empty()) {
+ return;
+ }
+ std::scoped_lock lock{invalidation_mutex};
+ for (const auto& [addr, size] : sequences) {
+ InvalidatePagesInRegion(addr, size);
+ }
+ RemovePendingShaders();
+}
+
void ShaderCache::OnCacheInvalidation(VAddr addr, size_t size) {
std::scoped_lock lock{invalidation_mutex};
InvalidatePagesInRegion(addr, size);
@@ -48,6 +61,22 @@ void ShaderCache::SyncGuestHost() {
ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_)
: device_memory{device_memory_} {}
+// Task and mesh programs are translated with the mesh registers and the header of the pass-through
+// geometry program (per-primitive outputs), so pipelines are keyed by them too.
+u64 ShaderCache::MeshStateHash() const {
+ using Regs = Tegra::Engines::Maxwell3D::Regs;
+ const auto& regs{maxwell3d->regs};
+ std::array<u32, 3 + 20> state{regs.reg_array[Regs::MeshShaderA], regs.reg_array[Regs::MeshShaderB],
+ regs.reg_array[Regs::MeshInitShader]};
+ if (regs.IsShaderConfigEnabled(Regs::ShaderType::Geometry)) {
+ const auto& geometry{regs.pipelines[static_cast<size_t>(Regs::ShaderType::Geometry)]};
+ const GPUVAddr address{(static_cast<u64>(geometry.reserved[0] & 0xff) << 32) |
+ geometry.reserved[1]};
+ gpu_memory->ReadBlockUnsafe(address, state.data() + 3, 20 * sizeof(u32));
+ }
+ return Common::CityHash64(reinterpret_cast<const char*>(state.data()), sizeof(state)) | 1;
+}
+
bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) try {
auto& dirty{maxwell3d->dirty.flags};
if (!dirty[VideoCommon::Dirty::Shaders]) {
@@ -57,6 +86,7 @@ bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) try {
const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()};
const bool native = maxwell3d->GetNativeMacroEngine().native_turing;
+ const bool mesh{native && maxwell3d->regs.IsMeshEnabled()};
for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) {
if (!maxwell3d->regs.IsShaderConfigEnabled(index)) {
unique_hashes[index] = 0;
@@ -69,6 +99,11 @@ bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) try {
unique_hashes[index] = 0;
continue;
}
+ if (mesh && program == Tegra::Engines::Maxwell3D::Regs::ShaderType::Geometry) {
+ // A header without code: the mesh program's environment reads its output map.
+ unique_hashes[index] = 0;
+ continue;
+ }
const GPUVAddr shader_addr{native
? (static_cast<u64>(shader_config.reserved[0] & 0xff) << 32) | shader_config.reserved[1]
: base_addr + shader_config.offset};
@@ -89,7 +124,8 @@ bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) try {
if (!shader_info) {
const u32 start_address{native ? 0 : shader_config.offset};
try {
- // Native programs are lowered while the environment is built.
+ // Native programs are lowered while the environment is built (once: the
+ // ShaderInfo keeps the result for the pipeline's environments).
RethrowIfRejected(*cpu_shader_addr);
GraphicsEnvironment env{*maxwell3d, *gpu_memory, program, native ? shader_addr : base_addr, start_address};
shader_info = MakeShaderInfo(env, *cpu_shader_addr);
@@ -111,6 +147,10 @@ bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) try {
}
shader_infos[index] = shader_info;
unique_hashes[index] = shader_info->unique_hash;
+ if (mesh && (program == Tegra::Engines::Maxwell3D::Regs::ShaderType::VertexB ||
+ program == Tegra::Engines::Maxwell3D::Regs::ShaderType::Tessellation)) {
+ unique_hashes[index] ^= MeshStateHash();
+ }
}
last_shaders_valid = true;
return true;
@@ -202,7 +242,8 @@ void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result,
const GPUVAddr address = native
? (static_cast<u64>(config.reserved[0] & 0xff) << 32) | config.reserved[1]
: base_addr;
- env = GraphicsEnvironment{*maxwell3d, *gpu_memory, program, address, start_address};
+ env = GraphicsEnvironment{*maxwell3d, *gpu_memory, program, address, start_address,
+ shader_infos[index]->lowered_native};
env.SetCachedSize(shader_infos[index]->size_bytes);
result.env_ptrs[env_index++] = &env;
}
@@ -275,10 +316,17 @@ void ShaderCache::RemovePendingShaders() {
marked_for_removal.clear();
if (!removed_shaders.empty()) {
- // Remove the given shaders from the cache
- std::erase_if(storage, [&removed_shaders](const std::unique_ptr<ShaderInfo>& shader) {
- return std::ranges::find(removed_shaders, shader.get()) != removed_shaders.end();
- });
+ if (removed_shaders.size() > 16) {
+ std::ranges::sort(removed_shaders, std::less<ShaderInfo*>{});
+ std::erase_if(storage, [&removed_shaders](const std::unique_ptr<ShaderInfo>& shader) {
+ return std::binary_search(removed_shaders.begin(), removed_shaders.end(),
+ shader.get(), std::less<ShaderInfo*>{});
+ });
+ } else {
+ std::erase_if(storage, [&removed_shaders](const std::unique_ptr<ShaderInfo>& shader) {
+ return std::ranges::find(removed_shaders, shader.get()) != removed_shaders.end();
+ });
+ }
}
}
@@ -342,6 +390,7 @@ const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu
info->unique_hash = env.CalculateHash();
info->size_bytes = env.ReadSizeBytes();
}
+ info->lowered_native = env.LoweredNative();
const size_t size_bytes{env.SourceSizeBytes() ? env.SourceSizeBytes() : info->size_bytes};
const ShaderInfo* const result{info.get()};
Register(std::move(info), cpu_addr, size_bytes);