aboutsummaryrefslogtreecommitdiff
path: root/src/video_core
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 14:31:00 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 14:31:00 -0400
commite7cc2cea290280e519ce011049063a9e40e7cc9c (patch)
tree244e5360a955de2e9af5de135bcdf72c05f43a53 /src/video_core
parente52e389b542565338c342a8692f9466c11749ba8 (diff)
Cache rejected native shaders and log each rejection once
A rejected program was lowered again (and logged) on every draw or dispatch: 1.7M log lines in 13 minutes of title 1. The shader cache keeps the first exception per address until guest memory there is written, and the error logs are deduplicated.
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/engines/kepler_compute.cpp17
-rw-r--r--src/video_core/shader_cache.cpp30
-rw-r--r--src/video_core/shader_cache.h6
3 files changed, 49 insertions, 4 deletions
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 8a3855468f..39c033d989 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -10,6 +10,9 @@
#include <cstdlib>
#include <fstream>
#include <optional>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
#include "common/assert.h"
#include "common/logging.h"
#include "shader_recompiler/exception.h"
@@ -316,11 +319,18 @@ bool KeplerCompute::ProcessNativeMethod(Core::System& system, u32 method, u32 ar
return true;
}
native_macro->native_execution_failed = true;
- LOG_ERROR(HW_GPU, "Native QMD failed: {}", error);
std::array<u32, 128> qmd{};
if (memory_manager.IsFullyMappedRange(qmd_address, sizeof(qmd))) {
memory_manager.ReadBlockUnsafe(qmd_address, qmd.data(), sizeof(qmd));
}
+ // Log a failing program's first few dispatches only (QMD words 48-49 hold its code
+ // address); it fails the same way every frame.
+ static std::unordered_map<u64, u32> failures;
+ const u64 program = (u64{qmd[49]} << 32) | qmd[48];
+ if (failures[program ^ std::hash<std::string>{}(std::string{error})]++ >= 2) {
+ return true;
+ }
+ LOG_ERROR(HW_GPU, "Native QMD failed: {}", error);
if (qmd_diagnostics && memory_manager.IsFullyMappedRange(qmd_address, sizeof(qmd))) {
std::ofstream dump("native-qmd.bin", std::ios::binary);
dump.write(reinterpret_cast<const char*>(qmd.data()), sizeof(qmd));
@@ -438,7 +448,10 @@ bool KeplerCompute::DispatchNativeCompute(const NativeQmdScheduler::Descriptor&
try {
rasterizer->DispatchCompute();
} catch (const Shader::Exception& error) {
- LOG_ERROR(HW_GPU, "Native compute shader rejected: {}", error.what());
+ static std::unordered_set<std::string> logged;
+ if (logged.insert(error.what()).second) {
+ LOG_ERROR(HW_GPU, "Native compute shader rejected: {}", error.what());
+ }
if (std::string_view{error.what()}.find("matrix multiply") != std::string_view::npos) {
DescribeNativeResources(qmd);
// The console runs this on tensor cores; stand in with a plain upscale of its input.
diff --git a/src/video_core/shader_cache.cpp b/src/video_core/shader_cache.cpp
index 1f826313a1..f92fe02068 100644
--- a/src/video_core/shader_cache.cpp
+++ b/src/video_core/shader_cache.cpp
@@ -5,6 +5,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
+#include <string>
+#include <unordered_set>
#include <fmt/ranges.h>
#include <array>
#include <vector>
@@ -91,7 +93,11 @@ bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) try {
if (!native.native_turing) throw;
native.native_execution_failed = true;
last_shaders_valid = false;
- LOG_ERROR(HW_GPU, "Native graphics shader rejected: {}", error.what());
+ // Each rejected program is logged once (the draws that use it keep failing quietly).
+ static std::unordered_set<std::string> logged;
+ if (logged.insert(error.what()).second) {
+ LOG_ERROR(HW_GPU, "Native graphics shader rejected: {}", error.what());
+ }
// Once: show what the vertex stage has bound, to find where an unbound bank's data lives.
static std::vector<std::string> dumped_errors;
if (const std::string what{error.what()};
@@ -177,6 +183,12 @@ 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;
+ {
+ std::scoped_lock lock{rejected_mutex};
+ boost::unordered::erase_if(rejected_native, [&](const auto& rejected) {
+ return rejected.first >= addr && rejected.first < addr_end;
+ });
+ }
const u64 page_end = (addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
for (u64 page = addr >> YUZU_PAGEBITS; page < page_end; ++page) {
auto it = invalidation_cache.find(page);
@@ -262,8 +274,22 @@ ShaderCache::Entry* ShaderCache::NewEntry(VAddr addr, VAddr addr_end, ShaderInfo
}
const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu_addr) {
+ {
+ std::scoped_lock lock{rejected_mutex};
+ if (const auto it = rejected_native.find(cpu_addr); it != rejected_native.end()) {
+ std::rethrow_exception(it->second);
+ }
+ }
auto info = std::make_unique<ShaderInfo>();
- if (const std::optional<u64> cached_hash{env.Analyze()}) {
+ std::optional<u64> analyzed;
+ try {
+ analyzed = env.Analyze();
+ } catch (const Shader::Exception&) {
+ std::scoped_lock lock{rejected_mutex};
+ rejected_native.emplace(cpu_addr, std::current_exception());
+ throw;
+ }
+ if (const std::optional<u64> cached_hash{analyzed}) {
info->unique_hash = *cached_hash;
info->size_bytes = env.CachedSizeBytes();
} else {
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h
index 997f2b0371..81700ba0e6 100644
--- a/src/video_core/shader_cache.h
+++ b/src/video_core/shader_cache.h
@@ -7,6 +7,7 @@
#pragma once
#include <array>
+#include <exception>
#include <memory>
#include <mutex>
#include <span>
@@ -152,6 +153,11 @@ private:
::Common::unordered_map<u64, std::vector<Entry*>> invalidation_cache;
std::vector<std::unique_ptr<ShaderInfo>> storage;
std::vector<Entry*> marked_for_removal;
+
+ /// Native programs the lowering rejected, by address, so each draw or dispatch rethrows the
+ /// 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;
};
} // namespace VideoCommon