diff options
| author | Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> | 2026-11-02 11:29:42 -0500 |
|---|---|---|
| committer | Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> | 2026-11-02 14:53:47 -0500 |
| commit | 0ed4a3a9e795896a4751d0330c78958a309d1aa0 (patch) | |
| tree | 0bbec5130c05f6cde7b6b1ab7b21c7b38aa4a9f9 /src/shader_recompiler | |
| parent | 8b0666b74868da8221d9e3ef3995f3a862ee24b6 (diff) | |
shader: drop the slots of a failed handle subtree with its code
When BuildHandleProgram failed on a subtree it truncated the code back to where
the subtree started, which removed the Tee ops of the values the subtree had
kept, but their entries stayed in g_handle_slots. A Phi tries its other inputs
after the first one fails; if one of them reused a value from the dropped
subtree, it emitted a Load of a slot that nothing wrote, so the handle read 0.
Number slots with a counter instead of counting map entries, and on failure
erase the entries numbered from the subtree's first slot on and rewind the
counter, so such a value is built again.
Follow-up to 561f87abec.
Assisted-by: Claude Sonnet 5.5 (implementation and commit message)
Diffstat (limited to 'src/shader_recompiler')
| -rw-r--r-- | src/shader_recompiler/ir_opt/texture_pass.cpp | 17 |
1 files changed, 13 insertions, 4 deletions
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index b33700cabd..b6aca0a118 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -438,9 +438,10 @@ bool IsUndefined(IR::Opcode opcode) { opcode == IR::Opcode::UndefU64; } -// Values already emitted by the program being built, by the slot that keeps them, and how deep -// in nested BuildHandleProgram calls the build is. +// Values already emitted by the program being built, by the slot that keeps them, how many slots +// are in use, and how deep in nested BuildHandleProgram calls the build is. thread_local std::unordered_map<const IR::Inst*, u32> g_handle_slots; +thread_local u32 g_handle_slot_count{}; thread_local int g_handle_depth{}; bool BuildHandleNode(const IR::Value& value, std::vector<u32>& code, int& budget, @@ -455,11 +456,13 @@ bool BuildHandleProgram(const IR::Value& value, std::vector<u32>& code, int& bud const bool outermost = g_handle_depth++ == 0; if (outermost) { g_handle_slots.clear(); + g_handle_slot_count = 0; } SCOPE_EXIT { --g_handle_depth; if (outermost) { g_handle_slots.clear(); + g_handle_slot_count = 0; } }; const IR::Inst* const inst{value.InstRecursive()}; @@ -478,14 +481,20 @@ bool BuildHandleProgram(const IR::Value& value, std::vector<u32>& code, int& bud } else { g_handle_slots.emplace(inst, ~0U); const size_t start = code.size(); + const u32 first_slot = g_handle_slot_count; result = BuildHandleNode(value, code, budget, block); g_handle_slots.erase(inst); if (!result) { + // The Tee of every value the dropped code kept goes with it: a later use of one of + // them (another Phi input) must build it again, not load a slot nothing wrote. code.resize(start); + std::erase_if(g_handle_slots, [first_slot](const auto& entry) { + return entry.second != ~0U && entry.second >= first_slot; + }); + g_handle_slot_count = first_slot; } if (result && inst->UseCount() > 1 && g_handle_slots.size() < 256) { - const u32 slot = static_cast<u32>( - std::ranges::count_if(g_handle_slots, [](const auto& entry) { return entry.second != ~0U; })); + const u32 slot = g_handle_slot_count++; g_handle_slots.emplace(inst, slot); code.push_back(static_cast<u32>(Op::Tee)); code.push_back(slot); |
