From 593e57f65b0312639d18604ad3e85b31d1d24720 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Wed, 19 Mar 2025 17:36:17 +0000 Subject: [PATCH] DXIL debugger discard should not terminate helper lanes Active lane is demoted to helper invocation which for pixel debug terminates the debug. Non-active lanes skip over a degenerate branch if that is the next instruction after the discard. This is a quick fix until maximal re-convergence style control flow is implemented, where quads reconverge at local uniform block --- renderdoc/driver/shaders/dxil/dxil_debug.cpp | 57 +++++++++++++++++++- renderdoc/driver/shaders/dxil/dxil_debug.h | 1 + 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/renderdoc/driver/shaders/dxil/dxil_debug.cpp b/renderdoc/driver/shaders/dxil/dxil_debug.cpp index 79b77dfdd..0cb243a66 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debug.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_debug.cpp @@ -2956,8 +2956,15 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper, RDCASSERT(GetShaderVariable(inst.args[1], opCode, dxOpCode, cond)); if(cond.value.u32v[0] != 0) { - m_Dead = true; - return true; + // Active lane is demoted to helper invocation which for pixel debug terminates the debug + if(m_State) + { + m_Dead = true; + return true; + } + // Quick fix : need maximal reconvergence style control flow to handle discard properly + // * If the next instruction is a de-generate jump then skip over it + StepOverDegenerateBranch(); } break; } @@ -5448,6 +5455,52 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper, return true; } +void ThreadState::StepOverDegenerateBranch() +{ + if(m_Ended) + return; + + uint32_t funcInstrIdx = m_FunctionInstructionIdx; + while(true) + { + RDCASSERT(funcInstrIdx < m_FunctionInfo->function->instructions.size()); + const Instruction *inst = m_FunctionInfo->function->instructions[funcInstrIdx]; + if(IsNopInstruction(*inst)) + { + funcInstrIdx++; + continue; + } + if(inst->op != Operation::Branch) + { + return; + } + const Block *target = cast(inst->args[0]); + RDCASSERT(target); + uint32_t blockId = target->id; + if(blockId < m_FunctionInfo->function->blocks.size()) + { + if(blockId == m_Block + 1) + { + m_PreviousBlock = m_Block; + m_PhiVariables.clear(); + auto it = m_FunctionInfo->phiReferencedIdsPerBlock.find(m_PreviousBlock); + if(it != m_FunctionInfo->phiReferencedIdsPerBlock.end()) + { + const FunctionInfo::ReferencedIds &phiIds = it->second; + for(Id id : phiIds) + m_PhiVariables[id] = m_Variables[id]; + } + m_Block = blockId; + m_FunctionInstructionIdx = m_FunctionInfo->function->blocks[m_Block]->startInstructionIdx; + m_ActiveGlobalInstructionIdx = + m_FunctionInfo->globalInstructionOffset + m_FunctionInstructionIdx; + return; + } + return; + } + } +} + void ThreadState::StepOverNopInstructions() { if(m_Ended) diff --git a/renderdoc/driver/shaders/dxil/dxil_debug.h b/renderdoc/driver/shaders/dxil/dxil_debug.h index 6e45d083b..d70ac4b9f 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debug.h +++ b/renderdoc/driver/shaders/dxil/dxil_debug.h @@ -237,6 +237,7 @@ struct ThreadState void StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, const rdcarray &workgroup, const rdcarray &activeMask); void StepOverNopInstructions(); + void StepOverDegenerateBranch(); bool Finished() const; bool InUniformBlock() const;