From 4a2961cdf90cda6fd3b43b720e13bb36a7067d25 Mon Sep 17 00:00:00 2001
From: "Mr. Big Britches" <111622360+MrBigBritches@users.noreply.github.com>
Date: Tue, 20 Oct 2026 07:26:30 +0200
Subject: [PATCH] shader: reflect tessellation strides across forward branches,
DS write widths, and packed patch masks
---
.../shader/recompiler/Tessellation.cpp | 43 ++++++++++++++-----
.../recompiler/frontend/decode/MemoryOps.cpp | 4 ++
.../frontend/decode/ShaderDecoder.h | 1 +
3 files changed, 38 insertions(+), 10 deletions(-)
diff --git a/src/graphics/shader/recompiler/Tessellation.cpp b/src/graphics/shader/recompiler/Tessellation.cpp
index bcfc7626c..8e2ffaa69 100644
--- a/src/graphics/shader/recompiler/Tessellation.cpp
+++ b/src/graphics/shader/recompiler/Tessellation.cpp
@@ -21,14 +21,17 @@ struct TessellationAddress {
Kind kind = Kind::Unknown;
uint32_t coefficient = 0;
uint32_t constant = 0;
+
+ bool operator==(const TessellationAddress&) const = default;
};
uint32_t ReflectTessellationStride(const Decoder::Program& program, bool local,
uint32_t control_points, uint32_t input_stride) {
using namespace Decoder;
- using Address = TessellationAddress;
- using Kind = Address::Kind;
- std::array
registers {};
+ using Address = TessellationAddress;
+ using Kind = Address::Kind;
+ using Registers = std::array;
+ Registers registers {};
registers[local ? 3u : 1u] =
local ? Address {Kind::Affine, 1u, 0u} : Address {Kind::PackedControlPoint};
const auto constant = [](uint32_t value) { return Address {Kind::Affine, 0u, value}; };
@@ -79,14 +82,28 @@ uint32_t ReflectTessellationStride(const Decoder::Program& program, bool local,
? value
: Address {};
};
+ // A forward branch joins its target with the fallthrough path. Definitions that
+ // differ between the paths are unknown there.
+ std::unordered_map joins;
+
+ const auto merge = [](Registers& into, const Registers& from) {
+ for (uint32_t index = 0; index < into.size(); index++) {
+ if (into[index] != from[index]) into[index] = {};
+ }
+ };
uint32_t stride = 0;
for (const auto& inst: program.instructions) {
- // Stage exits preserve the active path's definitions. An internal join
- // would require merging register definitions.
- EXIT_NOT_IMPLEMENTED(IsDirectBranch(inst.opcode) &&
- inst.branch_target != program.instructions.back().pc);
- const bool local_store =
- inst.opcode == Opcode::DS_WRITE_B32 || inst.opcode == Opcode::DS_WRITE2_B32;
+ if (const auto join = joins.find(inst.pc); join != joins.end()) {
+ merge(registers, join->second);
+ joins.erase(join);
+ }
+ if (IsDirectBranch(inst.opcode)) {
+ EXIT_NOT_IMPLEMENTED(inst.branch_target <= inst.pc);
+ if (const auto [join, inserted] = joins.try_emplace(inst.branch_target, registers);
+ !inserted)
+ merge(join->second, registers);
+ }
+ const bool local_store = IsDsWriteOpcode(inst.opcode);
const bool buffer_store = inst.opcode == Opcode::BUFFER_STORE_DWORD ||
inst.opcode == Opcode::BUFFER_STORE_DWORDX2 ||
inst.opcode == Opcode::BUFFER_STORE_DWORDX3 ||
@@ -123,6 +140,12 @@ uint32_t ReflectTessellationStride(const Decoder::Program& program, bool local,
if (rhs.constant == 0u && third.constant == 8u) value = constant(0u);
}
break;
+ case Opcode::V_AND_B32:
+ if ((lhs.kind == Kind::PackedControlPoint && rhs == constant(0xffu)) ||
+ (rhs.kind == Kind::PackedControlPoint && lhs == constant(0xffu)))
+ value = constant(0u);
+ break;
+ case Opcode::V_MUL_LO_U32: value = multiply(lhs, rhs); break;
case Opcode::V_MUL_U32_U24: value = multiply(low24(lhs), low24(rhs)); break;
case Opcode::V_MAD_U32_U24: value = add(multiply(low24(lhs), low24(rhs)), third); break;
case Opcode::V_LSHL_ADD_U32:
@@ -149,7 +172,7 @@ uint32_t ReflectTessellationStride(const Decoder::Program& program, bool local,
}
registers.at(inst.dst.reg) = value;
}
- EXIT_NOT_IMPLEMENTED(stride == 0u);
+ EXIT_NOT_IMPLEMENTED(!joins.empty() || stride == 0u);
return stride;
}
diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp
index 072692091..bf7f046d1 100644
--- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp
+++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp
@@ -166,6 +166,8 @@ void ApplyMemoryInfo(Instruction& inst, const MemoryOpcodeInfo* info) {
inst.formatted = info->formatted;
}
+} // namespace
+
bool IsDsWriteOpcode(Opcode opcode) {
switch (opcode) {
case Opcode::DS_WRITE_B8:
@@ -184,6 +186,8 @@ bool IsDsWriteOpcode(Opcode opcode) {
}
}
+namespace {
+
bool IsDsAtomicOpcode(Opcode opcode) {
switch (opcode) {
case Opcode::DS_ADD_U32:
diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h
index 4fb6c7b3e..25d3a8bcd 100644
--- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h
+++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h
@@ -882,6 +882,7 @@ Program DecodeFrontProgram(std::span front);
void DecodeProgram(std::span code, Program& program);
bool IsConditionalBranch(Opcode opcode);
bool IsDirectBranch(Opcode opcode);
+bool IsDsWriteOpcode(Opcode opcode);
void DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand);
void DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand);