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);