diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/engines/kepler_compute.cpp | 17 | ||||
| -rw-r--r-- | src/video_core/shader_cache.cpp | 30 | ||||
| -rw-r--r-- | src/video_core/shader_cache.h | 6 |
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 |
