aboutsummaryrefslogtreecommitdiff
path: root/src/shader_recompiler/ir_opt/texture_pass.cpp
diff options
context:
space:
mode:
authorSssnowyyy <78863999+Sssnowyyy@users.noreply.github.com>2026-11-02 11:29:42 -0500
committerSssnowyyy <78863999+Sssnowyyy@users.noreply.github.com>2026-11-02 14:53:47 -0500
commit0ed4a3a9e795896a4751d0330c78958a309d1aa0 (patch)
tree0bbec5130c05f6cde7b6b1ab7b21c7b38aa4a9f9 /src/shader_recompiler/ir_opt/texture_pass.cpp
parent8b0666b74868da8221d9e3ef3995f3a862ee24b6 (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/ir_opt/texture_pass.cpp')
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp17
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);