aboutsummaryrefslogtreecommitdiff
path: root/src/video_core
diff options
context:
space:
mode:
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