diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 14:28:34 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-29 20:49:48 -0400 |
| commit | 01fce03d24380ae647c07f02dd063981baae041a (patch) | |
| tree | 62423c07f89d7048c797985a526e69da44da9498 /src | |
| parent | 15e953cafd5cb348833cb373d7ddd996b81307f7 (diff) | |
vulkan: leave the alpha test reference out of pipelines that don't test alpha
FixedPipelineState stored alpha_test_ref whatever the alpha test
function. Title 1 animates the register with the test off (text fading
in and out), so every value made a new pipeline whose shaders were
lowered and translated on the GPU thread: bursts of 600-1,900 pipelines
in 10 s (up to 5 s of GPU thread) whenever text was on screen. The
reference is now kept only while the test compares with it (not Always
or Never). Driving title 1's menus: 400+ pipelines for already-known
shaders before, 0 after; one shader lowered 79 times before, 2 after.
EDEN_NX2_PIPELINE_DIFF=1 logs which fixed-state fields differ when a new
pipeline reuses known shaders.
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/renderer_vulkan/fixed_pipeline_state.cpp | 9 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 56 |
2 files changed, 64 insertions, 1 deletions
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 0ced890929..9dac98c830 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -179,7 +179,14 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe for (size_t i = 0; i < regs.rt.size(); ++i) { color_formats[i] = static_cast<u8>(regs.rt[i].format); } - alpha_test_ref = std::bit_cast<u32>(regs.alpha_test_ref); + // The reference is part of the pipeline only while the test compares with it: title 1 + // animates the register with the test off (text fading in and out), which made a new + // pipeline, translated on the GPU thread, for every value (~84% of its runtime pipelines). + const u32 packed_alpha_test = alpha_test_func.Value(); + alpha_test_ref = packed_alpha_test != PackComparisonOp(Maxwell::ComparisonOp::Never_GL) && + packed_alpha_test != PackComparisonOp(Maxwell::ComparisonOp::Always_GL) + ? std::bit_cast<u32>(regs.alpha_test_ref) + : 0; point_size = std::bit_cast<u32>(regs.point_size); if (maxwell3d.dirty.flags[Dirty::VertexInput]) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 60bb040984..17f57983e3 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -768,10 +768,66 @@ void PipelineCache::QueueVulkanPipelineCacheFlush() { }); } +namespace { +// EDEN_NX2_PIPELINE_DIFF=1: for a new graphics pipeline whose shaders an earlier one uses too, +// log which parts of the fixed state differ from that one (finds state that splits the same +// draws into many pipelines, each translated on the GPU thread). +void ReportNewPipelineKey(const GraphicsPipelineCacheKey& key) { + static const bool enabled = std::getenv("EDEN_NX2_PIPELINE_DIFF") != nullptr; + if (!enabled) { + return; + } + static std::unordered_map<u64, GraphicsPipelineCacheKey> last_by_shaders; + static u32 logged{}; + const u64 shaders = Common::CityHash64(reinterpret_cast<const char*>(key.unique_hashes.data()), + sizeof(key.unique_hashes)); + const auto [it, first] = last_by_shaders.try_emplace(shaders, key); + if (first) { + return; + } + const FixedPipelineState& now = key.state; + const FixedPipelineState& before = it->second.state; + std::string diffs; + if (now.raw1 != before.raw1) diffs += fmt::format(" raw1^{:#x}", now.raw1 ^ before.raw1); + if (now.raw2 != before.raw2) diffs += fmt::format(" raw2^{:#x}", now.raw2 ^ before.raw2); + if (now.color_formats != before.color_formats) { + diffs += fmt::format(" color_formats {:02x} -> {:02x}", fmt::join(before.color_formats, ""), + fmt::join(now.color_formats, "")); + } + if (now.alpha_test_ref != before.alpha_test_ref) { + diffs += fmt::format(" alpha_test_ref {:#x} -> {:#x} (func {})", before.alpha_test_ref, + now.alpha_test_ref, now.alpha_test_func.Value()); + } + if (now.point_size != before.point_size) { + diffs += fmt::format(" point_size {:#x} -> {:#x}", before.point_size, now.point_size); + } + if (now.viewport_swizzles != before.viewport_swizzles) diffs += " viewport_swizzles"; + if (now.attribute_types != before.attribute_types) { + diffs += fmt::format(" attribute_types^{:#x}", now.attribute_types ^ before.attribute_types); + } + if (std::memcmp(&now.dynamic_state, &before.dynamic_state, sizeof(now.dynamic_state)) != 0) { + diffs += " dynamic_state"; + } + if (now.driver_id != before.driver_id || now.driver_version != before.driver_version) { + diffs += " driver"; + } + const size_t size = now.Size(); + if (diffs.empty() && size > offsetof(FixedPipelineState, attachments)) { + diffs = fmt::format(" past attachments (key size {})", size); + } + it->second = key; + if (logged++ < 400) { + LOG_INFO(Render_Vulkan, "New pipeline for known shaders {:016x} differs in:{}", shaders, + diffs.empty() ? std::string{" nothing in the key prefix"} : diffs); + } +} +} // Anonymous namespace + GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)}; auto& pipeline{pair->second}; if (is_new) { + ReportNewPipelineKey(graphics_key); pipeline = CreateGraphicsPipeline(); } if (!pipeline) { |
