From 6b0604f735db842ac5e8f032f2189df82af6e9a0 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 18 Mar 2025 13:58:40 +0000 Subject: [PATCH] Refactor how builtins are gathered & provided for DXIL debugging * Buitins can either be thread-varying or global, we pick the narrowest type to use --- renderdoc/driver/d3d11/d3d11_shaderdebug.cpp | 10 ++-- renderdoc/driver/d3d12/d3d12_shaderdebug.cpp | 43 +++++++++------ renderdoc/driver/shaders/dxbc/dx_debug.cpp | 50 +++++++++++++----- renderdoc/driver/shaders/dxbc/dx_debug.h | 14 +++-- renderdoc/driver/shaders/dxil/dxil_debug.cpp | 55 ++++++++------------ renderdoc/driver/shaders/dxil/dxil_debug.h | 15 +++--- 6 files changed, 108 insertions(+), 79 deletions(-) diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 003e6673e..4176a8b4a 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -2209,8 +2209,8 @@ ShaderDebugTrace *D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t ins = interpreter->workgroup[hit->quadLaneIndex].inputs; state.semantics.coverage = lane->coverage; - state.semantics.primID = hit->primitive; - state.semantics.isFrontFace = hit->isFrontFace; + state.semantics.primID = lane->primitive; + state.semantics.isFrontFace = lane->isFrontFace; if(!ins.empty() && ins.back().name == dxbc->GetDXBCByteCode()->GetRegisterName( DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0)) @@ -2229,11 +2229,11 @@ ShaderDebugTrace *D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t if(fetcher.inputs[i].sysattribute == ShaderBuiltin::PrimitiveIndex) { - invar.value.u32v[fetcher.inputs[i].elem] = hit->primitive; + invar.value.u32v[fetcher.inputs[i].elem] = lane->primitive; } else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::MSAASampleIndex) { - invar.value.u32v[fetcher.inputs[i].elem] = hit->sample; + invar.value.u32v[fetcher.inputs[i].elem] = lane->sample; } else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::MSAACoverage) { @@ -2241,7 +2241,7 @@ ShaderDebugTrace *D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t } else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::IsFrontFace) { - invar.value.u32v[fetcher.inputs[i].elem] = hit->isFrontFace ? ~0U : 0; + invar.value.u32v[fetcher.inputs[i].elem] = lane->isFrontFace ? ~0U : 0; } else { diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 101b5bdc2..1aa1ce7b3 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -2261,6 +2261,11 @@ ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui } default: RDCERR("Unhandled system value semantic on VS input"); break; } + + if(sigParam.systemValue != ShaderBuiltin::Undefined) + { + activeState.m_Builtins[sigParam.systemValue] = inputs[i]; + } } debugger->InitialiseWorkgroup(workgroupProperties); @@ -2820,8 +2825,8 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t ins = interpreter->workgroup[hit->quadLaneIndex].inputs; state.semantics.coverage = lane->coverage; - state.semantics.primID = hit->primitive; - state.semantics.isFrontFace = hit->isFrontFace; + state.semantics.primID = lane->primitive; + state.semantics.isFrontFace = lane->isFrontFace; if(!ins.empty() && ins.back().name == dxbc->GetDXBCByteCode()->GetRegisterName( DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0)) @@ -2840,11 +2845,11 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t if(fetcher.inputs[i].sysattribute == ShaderBuiltin::PrimitiveIndex) { - invar.value.u32v[fetcher.inputs[i].elem] = hit->primitive; + invar.value.u32v[fetcher.inputs[i].elem] = lane->primitive; } else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::MSAASampleIndex) { - invar.value.u32v[fetcher.inputs[i].elem] = hit->sample; + invar.value.u32v[fetcher.inputs[i].elem] = lane->sample; } else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::MSAACoverage) { @@ -2852,7 +2857,7 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t } else if(fetcher.inputs[i].sysattribute == ShaderBuiltin::IsFrontFace) { - invar.value.u32v[fetcher.inputs[i].elem] = hit->isFrontFace ? ~0U : 0; + invar.value.u32v[fetcher.inputs[i].elem] = lane->isFrontFace ? ~0U : 0; } else { @@ -2956,9 +2961,12 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t data += inputElement.numwords * sizeof(uint32_t); } - state.m_Semantics.coverage = lane->coverage; - state.m_Semantics.primID = hit->primitive; - state.m_Semantics.isFrontFace = hit->isFrontFace; + state.m_Builtins[ShaderBuiltin::PrimitiveIndex] = + ShaderVariable(rdcstr(), lane->primitive, 0U, 0U, 0U); + state.m_Builtins[ShaderBuiltin::MSAACoverage] = + ShaderVariable(rdcstr(), lane->coverage, 0U, 0U, 0U); + state.m_Builtins[ShaderBuiltin::IsFrontFace] = + ShaderVariable(rdcstr(), lane->isFrontFace, 0U, 0U, 0U); for(const DXILDebug::PSInputData &psInput : psInputDatas) { @@ -2969,11 +2977,11 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t if(psInput.sysattribute == ShaderBuiltin::PrimitiveIndex) { - invar.value.u32v[outElement] = hit->primitive; + invar.value.u32v[outElement] = lane->primitive; } else if(psInput.sysattribute == ShaderBuiltin::MSAASampleIndex) { - invar.value.u32v[outElement] = hit->sample; + invar.value.u32v[outElement] = lane->sample; } else if(psInput.sysattribute == ShaderBuiltin::MSAACoverage) { @@ -2981,7 +2989,7 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t } else if(psInput.sysattribute == ShaderBuiltin::IsFrontFace) { - invar.value.u32v[outElement] = hit->isFrontFace ? ~0U : 0; + invar.value.u32v[outElement] = lane->isFrontFace ? ~0U : 0; } else { @@ -2992,6 +3000,9 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t memcpy(rawout, psInput.data, psInput.numwords * 4); } + + if(psInput.sysattribute != ShaderBuiltin::Undefined) + state.m_Builtins[psInput.sysattribute] = invar; } // TODO: UPDATE INPUTS FROM SAMPLE CACHE @@ -3168,7 +3179,7 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId, ret = debugger->BeginDebug(eventId, dxbc, refl, 0, 1); DXILDebug::GlobalState &globalState = debugger->GetGlobalState(); - std::map &builtins = globalState.builtinInputs; + rdcflatmap &globalBuiltins = globalState.builtins; rdcarray workgroupProperties; workgroupProperties.resize(1); @@ -3181,20 +3192,20 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId, workgroupProperties[0][DXILDebug::ThreadProperty::Active] = 1; // SV_DispatchThreadID - builtins[ShaderBuiltin::DispatchThreadIndex] = ShaderVariable( + globalBuiltins[ShaderBuiltin::DispatchThreadIndex] = ShaderVariable( rdcstr(), groupid[0] * threadDim[0] + threadid[0], groupid[1] * threadDim[1] + threadid[1], groupid[2] * threadDim[2] + threadid[2], 0U); // SV_GroupID - builtins[ShaderBuiltin::GroupIndex] = + globalBuiltins[ShaderBuiltin::GroupIndex] = ShaderVariable(rdcstr(), groupid[0], groupid[1], groupid[2], 0U); // SV_GroupThreadID - builtins[ShaderBuiltin::GroupThreadIndex] = + globalBuiltins[ShaderBuiltin::GroupThreadIndex] = ShaderVariable(rdcstr(), threadid[0], threadid[1], threadid[2], 0U); // SV_GroupIndex - builtins[ShaderBuiltin::GroupFlatIndex] = ShaderVariable( + globalBuiltins[ShaderBuiltin::GroupFlatIndex] = ShaderVariable( rdcstr(), threadid[2] * threadDim[0] * threadDim[1] + threadid[1] * threadDim[0] + threadid[0], 0U, 0U, 0U); diff --git a/renderdoc/driver/shaders/dxbc/dx_debug.cpp b/renderdoc/driver/shaders/dxbc/dx_debug.cpp index b153d8e86..8bf867583 100644 --- a/renderdoc/driver/shaders/dxbc/dx_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dx_debug.cpp @@ -394,6 +394,11 @@ struct LaneData uint quadLane; uint coverage; + uint sample; + uint primitive; + uint isFrontFace; + uint pad2; + Inputs IN; }; @@ -404,14 +409,13 @@ struct DebugHit float3 pos_depth; // xy position and depth float derivValid; - uint primitive; - uint isFrontFace; - uint sample; - uint quadLaneIndex; uint laneIndex; uint subgroupSize; - uint pad; + + uint sample; + uint primitive; + uint2 pad; uint4 globalBallot; uint4 helperBallot; @@ -462,7 +466,6 @@ void ExtractInputs(Inputs IN HitBuffer[idx].primitive = primitive; - HitBuffer[idx].isFrontFace = isFrontFace; HitBuffer[idx].sample = sample; HitBuffer[idx].quadLaneIndex = quadLaneIndex; @@ -472,6 +475,22 @@ void ExtractInputs(Inputs IN HitBuffer[idx].globalBallot = 0; HitBuffer[idx].helperBallot = 0; + // replicate these across the quad, we assume they do not vary + HitBuffer[idx].lanes[0].primitive = primitive; + HitBuffer[idx].lanes[1].primitive = primitive; + HitBuffer[idx].lanes[2].primitive = primitive; + HitBuffer[idx].lanes[3].primitive = primitive; + + HitBuffer[idx].lanes[0].isFrontFace = isFrontFace; + HitBuffer[idx].lanes[1].isFrontFace = isFrontFace; + HitBuffer[idx].lanes[2].isFrontFace = isFrontFace; + HitBuffer[idx].lanes[3].isFrontFace = isFrontFace; + + HitBuffer[idx].lanes[0].sample = sample; + HitBuffer[idx].lanes[1].sample = sample; + HitBuffer[idx].lanes[2].sample = sample; + HitBuffer[idx].lanes[3].sample = sample; + // quad pixelPos will be set with other derivatives for float inputs // for the simple quad case, only the desired thread is considered non-helper @@ -773,6 +792,11 @@ struct PSLaneData uint quadId; uint quadLane; uint coverage; + + uint sample; + uint primitive; + uint isFrontFace; + uint pad2; #endif }; @@ -804,14 +828,13 @@ struct DebugHit float3 pos_depth; // xy position and depth float derivValid; - uint primitive; - uint isFrontFace; - uint sample; - uint quadLaneIndex; uint laneIndex; uint subgroupSize; - uint pad; + + uint sample; + uint primitive; + uint2 pad; uint4 globalBallot; uint4 helperBallot; @@ -956,6 +979,10 @@ void ExtractInputs(Inputs IN ps.quadId = quadId; ps.quadLane = quadLaneIndex; ps.coverage = coverage; + + ps.sample = sample; + ps.primitive = primitive; + ps.isFrontFace = isFrontFace; #elif STAGE == STAGE_CS bool candidateThread = (threadid.x == DESTX && threadid.y == DESTY && threadid.z == DESTZ); @@ -979,7 +1006,6 @@ void ExtractInputs(Inputs IN HitBuffer[idx].pos_depth = debug_pixelPos.xyz; HitBuffer[idx].derivValid = derivValid; HitBuffer[idx].primitive = primitive; - HitBuffer[idx].isFrontFace = isFrontFace; HitBuffer[idx].sample = sample; HitBuffer[idx].laneIndex = laneIndex; HitBuffer[idx].quadLaneIndex = quadLaneIndex; diff --git a/renderdoc/driver/shaders/dxbc/dx_debug.h b/renderdoc/driver/shaders/dxbc/dx_debug.h index 5269ba95e..5c2c89b34 100644 --- a/renderdoc/driver/shaders/dxbc/dx_debug.h +++ b/renderdoc/driver/shaders/dxbc/dx_debug.h @@ -59,6 +59,11 @@ struct PSLaneData uint32_t quadLane; uint32_t coverage; + uint32_t sample; + uint32_t primitive; + uint32_t isFrontFace; + uint32_t pad2; + // user data PSInput below here }; @@ -95,14 +100,13 @@ struct DebugHit float depth; float derivValid; - uint32_t primitive; - uint32_t isFrontFace; - uint32_t sample; - uint32_t quadLaneIndex; uint32_t laneIndex; uint32_t subgroupSize; - uint32_t pad; + + uint32_t sample; + uint32_t primitive; + uint32_t pad[2]; Vec4u globalBallot; Vec4u helperBallot; diff --git a/renderdoc/driver/shaders/dxil/dxil_debug.cpp b/renderdoc/driver/shaders/dxil/dxil_debug.cpp index bd0fcd48c..d2a8db5d9 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debug.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_debug.cpp @@ -2688,9 +2688,8 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper, RDCASSERTEQUAL(arg.type, VarType::SInt); RDCASSERTEQUAL(result.type, VarType::SInt); uint32_t component = arg.value.u32v[0]; - RDCASSERT(m_GlobalState.builtinInputs.count(ShaderBuiltin::DispatchThreadIndex) != 0); result.value.u32v[0] = - m_GlobalState.builtinInputs.at(ShaderBuiltin::DispatchThreadIndex).value.u32v[component]; + GetBuiltin(ShaderBuiltin::DispatchThreadIndex).value.u32v[component]; break; } case DXOp::GroupId: @@ -2701,9 +2700,7 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper, RDCASSERTEQUAL(arg.type, VarType::SInt); RDCASSERTEQUAL(result.type, VarType::SInt); uint32_t component = arg.value.u32v[0]; - RDCASSERT(m_GlobalState.builtinInputs.count(ShaderBuiltin::GroupIndex) != 0); - result.value.u32v[0] = - m_GlobalState.builtinInputs.at(ShaderBuiltin::GroupIndex).value.u32v[component]; + result.value.u32v[0] = GetBuiltin(ShaderBuiltin::GroupIndex).value.u32v[component]; break; } case DXOp::ThreadIdInGroup: @@ -2714,18 +2711,14 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper, RDCASSERTEQUAL(arg.type, VarType::SInt); RDCASSERTEQUAL(result.type, VarType::SInt); uint32_t component = arg.value.u32v[0]; - RDCASSERT(m_GlobalState.builtinInputs.count(ShaderBuiltin::GroupThreadIndex) != 0); - result.value.u32v[0] = - m_GlobalState.builtinInputs.at(ShaderBuiltin::GroupThreadIndex).value.u32v[component]; + result.value.u32v[0] = GetBuiltin(ShaderBuiltin::GroupThreadIndex).value.u32v[component]; break; } case DXOp::FlattenedThreadIdInGroup: { // FlattenedThreadIdInGroup()->SV_GroupIndex RDCASSERTEQUAL(result.type, VarType::SInt); - RDCASSERT(m_GlobalState.builtinInputs.count(ShaderBuiltin::GroupFlatIndex) != 0); - result.value.u32v[0] = - m_GlobalState.builtinInputs.at(ShaderBuiltin::GroupFlatIndex).value.u32v[0]; + result.value.u32v[0] = GetBuiltin(ShaderBuiltin::GroupFlatIndex).value.u32v[0]; break; } case DXOp::DerivCoarseX: @@ -3234,41 +3227,31 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper, case DXOp::SampleIndex: { // SV_SampleIndex - RDCASSERT(m_GlobalState.builtinInputs.count(ShaderBuiltin::MSAASampleIndex) != 0); - result.value.u32v[0] = - m_GlobalState.builtinInputs.at(ShaderBuiltin::MSAASampleIndex).value.u32v[0]; + result.value.u32v[0] = GetBuiltin(ShaderBuiltin::MSAASampleIndex).value.u32v[0]; break; } case DXOp::Coverage: { // SV_Coverage - RDCASSERT(m_GlobalState.builtinInputs.count(ShaderBuiltin::MSAACoverage) != 0); - result.value.u32v[0] = - m_GlobalState.builtinInputs.at(ShaderBuiltin::MSAACoverage).value.u32v[0]; + result.value.u32v[0] = GetBuiltin(ShaderBuiltin::MSAACoverage).value.u32v[0]; break; } case DXOp::InnerCoverage: { // SV_InnerCoverage - RDCASSERT(m_GlobalState.builtinInputs.count(ShaderBuiltin::IsFullyCovered) != 0); - result.value.u32v[0] = - m_GlobalState.builtinInputs.at(ShaderBuiltin::IsFullyCovered).value.u32v[0]; + result.value.u32v[0] = GetBuiltin(ShaderBuiltin::IsFullyCovered).value.u32v[0]; break; } case DXOp::ViewID: { // SV_ViewportArrayIndex - RDCASSERT(m_GlobalState.builtinInputs.count(ShaderBuiltin::ViewportIndex) != 0); - result.value.u32v[0] = - m_GlobalState.builtinInputs.at(ShaderBuiltin::ViewportIndex).value.u32v[0]; + result.value.u32v[0] = GetBuiltin(ShaderBuiltin::ViewportIndex).value.u32v[0]; break; } case DXOp::PrimitiveID: { // SV_PrimitiveID - RDCASSERT(m_GlobalState.builtinInputs.count(ShaderBuiltin::PrimitiveIndex) != 0); - result.value.u32v[0] = - m_GlobalState.builtinInputs.at(ShaderBuiltin::PrimitiveIndex).value.u32v[0]; + result.value.u32v[0] = GetBuiltin(ShaderBuiltin::PrimitiveIndex).value.u32v[0]; break; } case DXOp::IsHelperLane: @@ -5512,6 +5495,20 @@ bool ThreadState::IsVariableAssigned(const Id id) const } } +ShaderVariable ThreadState::GetBuiltin(ShaderBuiltin builtin) +{ + auto local = m_Builtins.find(builtin); + if(local != m_Builtins.end()) + return local->second; + + auto global = m_GlobalState.builtins.find(builtin); + if(global != m_GlobalState.builtins.end()) + return global->second; + + RDCERR("Couldn't find data for builtin %s", ToStr(builtin).c_str()); + return {}; +} + bool ThreadState::GetLiveVariable(const Id &id, Operation op, DXOp dxOpCode, ShaderVariable &var) const { if(id < m_Live.size()) @@ -8066,11 +8063,6 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain if(FindSigParameter(dxbcInParams, sig, sigParam)) { v.name = sigParam.semanticIdxName; - if(sigParam.systemValue != ShaderBuiltin::Undefined) - { - RDCASSERT(m_GlobalState.builtinInputs.count(sigParam.systemValue) == 0); - m_GlobalState.builtinInputs[sigParam.systemValue] = v; - } } else { @@ -8328,7 +8320,6 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain if(i != m_ActiveLaneIndex) { lane.m_Input = state.m_Input; - lane.m_Semantics = state.m_Semantics; lane.m_Variables = state.m_Variables; lane.m_Assigned = state.m_Assigned; lane.m_Live = state.m_Live; diff --git a/renderdoc/driver/shaders/dxil/dxil_debug.h b/renderdoc/driver/shaders/dxil/dxil_debug.h index 4d01e4ff0..190104b6b 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debug.h +++ b/renderdoc/driver/shaders/dxil/dxil_debug.h @@ -212,6 +212,8 @@ struct MemoryTracking std::map m_Pointers; }; +typedef rdcflatmap BuiltinInputs; + struct ThreadState { ThreadState(Debugger &debugger, const GlobalState &globalState, uint32_t maxSSAId); @@ -279,6 +281,8 @@ struct ThreadState ShaderVariable &var, bool flushDenormInput, bool isLive) const; bool IsVariableAssigned(const Id id) const; + ShaderVariable GetBuiltin(ShaderBuiltin builtin); + struct AnnotationProperties { DXIL::ResourceKind resKind; @@ -286,12 +290,7 @@ struct ThreadState uint32_t structStride; }; - struct - { - uint32_t coverage = ~0U; - uint32_t primID = ~0U; - uint32_t isFrontFace = false; - } m_Semantics; + BuiltinInputs m_Builtins; Debugger &m_Debugger; const DXIL::Program &m_Program; @@ -355,11 +354,9 @@ struct ThreadState struct GlobalState { - typedef std::map BuiltinInputs; - GlobalState() = default; ~GlobalState(); - BuiltinInputs builtinInputs; + BuiltinInputs builtins; struct ViewFmt {