From 8fb23b901060d5afca8085b41466ad5d4ed7dbc6 Mon Sep 17 00:00:00 2001 From: Steve Karolewics Date: Sun, 19 Jan 2020 16:26:13 -0800 Subject: [PATCH] Add SM 5.1 resource binding support to shader debugging With SM 5.1, instructions specify a resource by a logical identifier, but root signatures specify them with a base register and register space. Find the appropriate declaration and retrieve the data needed for resource lookup. --- .../driver/shaders/dxbc/dxbc_bytecode.cpp | 12 +- renderdoc/driver/shaders/dxbc/dxbc_bytecode.h | 8 +- renderdoc/driver/shaders/dxbc/dxbc_debug.cpp | 251 ++++++++++++------ renderdoc/driver/shaders/dxbc/dxbc_debug.h | 15 ++ .../driver/shaders/dxbc/dxbc_disassemble.cpp | 19 ++ 5 files changed, 209 insertions(+), 96 deletions(-) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp index 16fa672b6..07e36d7d4 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp @@ -88,7 +88,7 @@ DXBC::Reflection *Program::GuessReflection() DXBC::ShaderInputBind desc; RDCASSERT(dcl.operand.type == TYPE_RESOURCE); - RDCASSERT(dcl.operand.indices.size() == 1); + RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3); RDCASSERT(dcl.operand.indices[0].absolute); uint32_t idx = (uint32_t)dcl.operand.indices[0].index; @@ -159,7 +159,7 @@ DXBC::Reflection *Program::GuessReflection() RDCASSERT(dcl.operand.type == TYPE_RESOURCE || dcl.operand.type == TYPE_UNORDERED_ACCESS_VIEW); - RDCASSERT(dcl.operand.indices.size() == 1); + RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3); RDCASSERT(dcl.operand.indices[0].absolute); uint32_t idx = (uint32_t)dcl.operand.indices[0].index; @@ -196,7 +196,7 @@ DXBC::Reflection *Program::GuessReflection() DXBC::ShaderInputBind desc; RDCASSERT(dcl.operand.type == TYPE_RESOURCE); - RDCASSERT(dcl.operand.indices.size() == 1); + RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3); RDCASSERT(dcl.operand.indices[0].absolute); uint32_t idx = (uint32_t)dcl.operand.indices[0].index; @@ -227,7 +227,7 @@ DXBC::Reflection *Program::GuessReflection() DXBC::ShaderInputBind desc; RDCASSERT(dcl.operand.type == TYPE_UNORDERED_ACCESS_VIEW); - RDCASSERT(dcl.operand.indices.size() == 1); + RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3); RDCASSERT(dcl.operand.indices[0].absolute); uint32_t idx = (uint32_t)dcl.operand.indices[0].index; @@ -263,7 +263,7 @@ DXBC::Reflection *Program::GuessReflection() DXBC::ShaderInputBind desc; RDCASSERT(dcl.operand.type == TYPE_UNORDERED_ACCESS_VIEW); - RDCASSERT(dcl.operand.indices.size() == 1); + RDCASSERT(dcl.operand.indices.size() == 1 || dcl.operand.indices.size() == 3); RDCASSERT(dcl.operand.indices[0].absolute); uint32_t idx = (uint32_t)dcl.operand.indices[0].index; @@ -326,7 +326,7 @@ DXBC::Reflection *Program::GuessReflection() DXBC::ShaderInputBind desc; RDCASSERT(dcl.operand.type == TYPE_CONSTANT_BUFFER); - RDCASSERT(dcl.operand.indices.size() == 2); + RDCASSERT(dcl.operand.indices.size() == 2 || dcl.operand.indices.size() == 3); RDCASSERT(dcl.operand.indices[0].absolute && dcl.operand.indices[1].absolute); uint32_t idx = (uint32_t)dcl.operand.indices[0].index; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h index 8988ab9a2..f72ce396d 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.h @@ -934,9 +934,10 @@ public: void SetReflection(const DXBC::Reflection *refl) { m_Reflection = refl; } void SetDebugInfo(const DXBC::IDebugInfo *debug) { m_DebugInfo = debug; } - DXBC::ShaderType GetShaderType() { return m_Type; } - uint32_t GetMajorVersion() { return m_Major; } - uint32_t GetMinorVersion() { return m_Minor; } + DXBC::ShaderType GetShaderType() const { return m_Type; } + uint32_t GetMajorVersion() const { return m_Major; } + uint32_t GetMinorVersion() const { return m_Minor; } + bool IsShaderModel51() const { return m_Major == 5 && m_Minor == 1; } D3D_PRIMITIVE_TOPOLOGY GetOutputTopology(); const rdcstr &GetDisassembly() { @@ -946,6 +947,7 @@ public: } size_t GetNumDeclarations() const { return m_Declarations.size(); } const Declaration &GetDeclaration(size_t i) const { return m_Declarations[i]; } + const Declaration *FindDeclaration(OperandType declType, uint32_t identifier) const; size_t GetNumInstructions() const { return m_Instructions.size(); } const Operation &GetInstruction(size_t i) const { return m_Instructions[i]; } const rdcarray &GetImmediateConstantBuffer() const { return m_Immediate; } diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index afa6d797e..1b517d419 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -2673,16 +2673,34 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua case OPCODE_IMM_ATOMIC_ALLOC: { - ShaderDebug::BindingSlot slot(srcOpers[0].value.u.x, 0); - uint32_t count = global.uavs[slot].hiddenCounter++; + ShaderDebug::BindingSlot slot = + GetBindingSlotForIdentifier(*program, TYPE_UNORDERED_ACCESS_VIEW, srcOpers[0].value.u.x); + GlobalState::UAVIterator uav = global.uavs.find(slot); + if(uav == global.uavs.end()) + { + // With on-demand buffer fetching, this is where we'd fetch + RDCERR("Invalid UAV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); + return s; + } + + uint32_t count = uav->second.hiddenCounter++; s.SetDst(op.operands[0], op, ShaderVariable("", count, count, count, count)); break; } case OPCODE_IMM_ATOMIC_CONSUME: { - ShaderDebug::BindingSlot slot(srcOpers[0].value.u.x, 0); - uint32_t count = --global.uavs[slot].hiddenCounter; + ShaderDebug::BindingSlot slot = + GetBindingSlotForIdentifier(*program, TYPE_UNORDERED_ACCESS_VIEW, srcOpers[0].value.u.x); + GlobalState::UAVIterator uav = global.uavs.find(slot); + if(uav == global.uavs.end()) + { + // With on-demand buffer fetching, this is where we'd fetch + RDCERR("Invalid UAV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); + return s; + } + + uint32_t count = --uav->second.hiddenCounter; s.SetDst(op.operands[0], op, ShaderVariable("", count, count, count, count)); break; } @@ -2796,30 +2814,33 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua } else { - ShaderDebug::BindingSlot slot(resIndex, 0); - offset = global.uavs[slot].firstElement; - numElems = global.uavs[slot].numElements; - data = &global.uavs[slot].data[0]; - - for(size_t i = 0; i < s.program->GetNumDeclarations(); i++) + ShaderDebug::BindingSlot slot = + GetBindingSlotForIdentifier(*program, TYPE_UNORDERED_ACCESS_VIEW, resIndex); + GlobalState::UAVIterator uav = global.uavs.find(slot); + if(uav == global.uavs.end()) { - const DXBCBytecode::Declaration &decl = program->GetDeclaration(i); + // With on-demand buffer fetching, this is where we'd fetch + RDCERR("Invalid UAV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); + return s; + } - if(decl.operand.type == TYPE_UNORDERED_ACCESS_VIEW && - decl.operand.indices[0].index == resIndex) + offset = uav->second.firstElement; + numElems = uav->second.numElements; + data = &uav->second.data[0]; + + const DXBCBytecode::Declaration *pDecl = + program->FindDeclaration(TYPE_UNORDERED_ACCESS_VIEW, resIndex); + if(pDecl) + { + if(pDecl->declaration == OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW) { - if(decl.declaration == OPCODE_DCL_UNORDERED_ACCESS_VIEW_RAW) - { - stride = 4; - structured = false; - break; - } - else if(decl.declaration == OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED) - { - stride = decl.stride; - structured = true; - break; - } + stride = 4; + structured = false; + } + else if(pDecl->declaration == OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED) + { + stride = pDecl->stride; + structured = true; } } } @@ -2951,25 +2972,12 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua } else if(!gsm) { - for(size_t i = 0; i < s.program->GetNumDeclarations(); i++) - { - const DXBCBytecode::Declaration &decl = program->GetDeclaration(i); - - if(decl.operand.type == TYPE_UNORDERED_ACCESS_VIEW && !srv && - decl.operand.indices[0].index == resIndex && - decl.declaration == OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED) - { - stride = decl.stride; - break; - } - if(decl.operand.type == TYPE_RESOURCE && srv && - decl.operand.indices[0].index == resIndex && - decl.declaration == OPCODE_DCL_RESOURCE_STRUCTURED) - { - stride = decl.stride; - break; - } - } + OperandType declType = srv ? TYPE_RESOURCE : TYPE_UNORDERED_ACCESS_VIEW; + OpcodeType declOpcode = + srv ? OPCODE_DCL_RESOURCE_STRUCTURED : OPCODE_DCL_UNORDERED_ACCESS_VIEW_STRUCTURED; + const DXBCBytecode::Declaration *pDecl = program->FindDeclaration(declType, resIndex); + if(pDecl && pDecl->declaration == declOpcode) + stride = pDecl->stride; } } @@ -3022,10 +3030,34 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua RDCASSERT(stride != 0); - ShaderDebug::BindingSlot slot(resIndex, 0); - uint32_t offset = srv ? global.srvs[slot].firstElement : global.uavs[slot].firstElement; - uint32_t numElems = srv ? global.srvs[slot].numElements : global.uavs[slot].numElements; - GlobalState::ViewFmt fmt = srv ? global.srvs[slot].format : global.uavs[slot].format; + ShaderDebug::BindingSlot slot = GetBindingSlotForIdentifier( + *program, srv ? TYPE_RESOURCE : TYPE_UNORDERED_ACCESS_VIEW, resIndex); + GlobalState::SRVIterator srvIter; + GlobalState::UAVIterator uavIter; + if(srv) + { + srvIter = global.srvs.find(slot); + if(srvIter == global.srvs.end()) + { + // With on-demand buffer fetching, this is where we'd fetch + RDCERR("Invalid SRV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); + return s; + } + } + else + { + uavIter = global.uavs.find(slot); + if(uavIter == global.uavs.end()) + { + // With on-demand buffer fetching, this is where we'd fetch + RDCERR("Invalid UAV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); + return s; + } + } + + uint32_t offset = srv ? srvIter->second.firstElement : uavIter->second.firstElement; + uint32_t numElems = srv ? srvIter->second.numElements : uavIter->second.numElements; + GlobalState::ViewFmt fmt = srv ? srvIter->second.format : uavIter->second.format; // indexing for raw views is in bytes, but firstElement/numElements is in format-sized // units. Multiply up by stride @@ -3035,10 +3067,10 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua numElems *= RDCMIN(4, fmt.byteWidth); } - byte *data = srv ? &global.srvs[slot].data[0] : &global.uavs[slot].data[0]; - bool texData = srv ? false : global.uavs[slot].tex; - uint32_t rowPitch = srv ? 0 : global.uavs[slot].rowPitch; - uint32_t depthPitch = srv ? 0 : global.uavs[slot].depthPitch; + byte *data = srv ? &srvIter->second.data[0] : &uavIter->second.data[0]; + bool texData = srv ? false : uavIter->second.tex; + uint32_t rowPitch = srv ? 0 : uavIter->second.rowPitch; + uint32_t depthPitch = srv ? 0 : uavIter->second.depthPitch; if(gsm) { @@ -3074,7 +3106,7 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua } if(!data || (!texData && elemIdx >= numElems) || - (texData && texOffset >= global.uavs[slot].data.size())) + (texData && texOffset >= uavIter->second.data.size())) { if(load) s.SetDst(op.operands[0], op, ShaderVariable("", 0U, 0U, 0U, 0U)); @@ -3447,32 +3479,27 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua // search for the declaration if(dim == 0) { - for(size_t i = 0; i < s.program->GetNumDeclarations(); i++) + const Declaration *pDecl = + program->FindDeclaration(TYPE_RESOURCE, (uint32_t)op.operands[2].indices[0].index); + if(pDecl && pDecl->declaration == OPCODE_DCL_RESOURCE) { - const DXBCBytecode::Declaration &decl = s.program->GetDeclaration(i); - - if(decl.declaration == OPCODE_DCL_RESOURCE && decl.operand.type == TYPE_RESOURCE && - decl.operand.indices.size() == 1 && - decl.operand.indices[0] == op.operands[2].indices[0]) + switch(pDecl->dim) { - switch(decl.dim) - { - case RESOURCE_DIMENSION_UNKNOWN: - case NUM_DIMENSIONS: - case RESOURCE_DIMENSION_BUFFER: - case RESOURCE_DIMENSION_RAW_BUFFER: - case RESOURCE_DIMENSION_STRUCTURED_BUFFER: - case RESOURCE_DIMENSION_TEXTURE1D: - case RESOURCE_DIMENSION_TEXTURE1DARRAY: dim = 1; break; - case RESOURCE_DIMENSION_TEXTURE2D: - case RESOURCE_DIMENSION_TEXTURE2DMS: - case RESOURCE_DIMENSION_TEXTURE2DARRAY: - case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: - case RESOURCE_DIMENSION_TEXTURECUBE: - case RESOURCE_DIMENSION_TEXTURECUBEARRAY: dim = 2; break; - case RESOURCE_DIMENSION_TEXTURE3D: dim = 3; break; - } - break; + default: + case RESOURCE_DIMENSION_UNKNOWN: + case NUM_DIMENSIONS: + case RESOURCE_DIMENSION_BUFFER: + case RESOURCE_DIMENSION_RAW_BUFFER: + case RESOURCE_DIMENSION_STRUCTURED_BUFFER: + case RESOURCE_DIMENSION_TEXTURE1D: + case RESOURCE_DIMENSION_TEXTURE1DARRAY: dim = 1; break; + case RESOURCE_DIMENSION_TEXTURE2D: + case RESOURCE_DIMENSION_TEXTURE2DMS: + case RESOURCE_DIMENSION_TEXTURE2DARRAY: + case RESOURCE_DIMENSION_TEXTURE2DMSARRAY: + case RESOURCE_DIMENSION_TEXTURECUBE: + case RESOURCE_DIMENSION_TEXTURECUBEARRAY: dim = 2; break; + case RESOURCE_DIMENSION_TEXTURE3D: dim = 3; break; } } } @@ -3573,24 +3600,32 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua const Declaration &decl = program->GetDeclaration(i); if(decl.declaration == OPCODE_DCL_SAMPLER && op.operands.size() > 3 && - decl.operand.indices == op.operands[3].indices) + op.operands[3].indices[0] == decl.operand.indices[0]) { samplerMode = decl.samplerMode; - samplerBinding.shaderRegister = (uint32_t)op.operands[3].indices[0].index; + samplerBinding = ShaderDebug::GetBindingSlotForDeclaration(*program, decl); } if(decl.dim == RESOURCE_DIMENSION_BUFFER && op.operation == OPCODE_LD && decl.declaration == OPCODE_DCL_RESOURCE && decl.operand.type == TYPE_RESOURCE && - decl.operand.indices.size() == 1 && decl.operand.indices[0] == op.operands[2].indices[0]) + decl.operand.indices.size() > 0 && decl.operand.indices[0] == op.operands[2].indices[0]) { resourceDim = decl.dim; - resourceBinding.shaderRegister = (uint32_t)decl.operand.indices[0].index; + resourceBinding = ShaderDebug::GetBindingSlotForDeclaration(*program, decl); + GlobalState::SRVIterator srv = global.srvs.find(resourceBinding); + if(srv == global.srvs.end()) + { + // With on-demand buffer fetching, this is where we'd fetch + RDCERR("Invalid SRV reg=%u, space=%u", resourceBinding.shaderRegister, + resourceBinding.registerSpace); + return s; + } - const byte *data = &global.srvs[resourceBinding].data[0]; - uint32_t offset = global.srvs[resourceBinding].firstElement; - uint32_t numElems = global.srvs[resourceBinding].numElements; + const byte *data = &srv->second.data[0]; + uint32_t offset = srv->second.firstElement; + uint32_t numElems = srv->second.numElements; - GlobalState::ViewFmt fmt = global.srvs[resourceBinding].format; + GlobalState::ViewFmt fmt = srv->second.format; data += fmt.Stride() * offset; @@ -3626,13 +3661,13 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua return s; } if(decl.declaration == OPCODE_DCL_RESOURCE && decl.operand.type == TYPE_RESOURCE && - decl.operand.indices.size() == 1 && decl.operand.indices[0] == op.operands[2].indices[0]) + decl.operand.indices.size() > 0 && decl.operand.indices[0] == op.operands[2].indices[0]) { resourceDim = decl.dim; resourceRetType = decl.resType[0]; sampleCount = decl.sampleCount; - resourceBinding.shaderRegister = (uint32_t)decl.operand.indices[0].index; + resourceBinding = ShaderDebug::GetBindingSlotForDeclaration(*program, decl); // doesn't seem like these are ever less than four components, even if the texture is // declared for example. @@ -4000,6 +4035,48 @@ State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State qua return s; } +ShaderDebug::BindingSlot GetBindingSlotForDeclaration(const Program &program, + const DXBCBytecode::Declaration &decl) +{ + uint32_t baseRegister = program.IsShaderModel51() ? (uint32_t)decl.operand.indices[1].index + : (uint32_t)decl.operand.indices[0].index; + + return ShaderDebug::BindingSlot(baseRegister, decl.space); +} + +ShaderDebug::BindingSlot GetBindingSlotForIdentifier(const Program &program, OperandType declType, + uint32_t identifier) +{ + // A note on matching declarations: with SM 5.0 or lower, the declaration will have a single + // operand index, which corresponds to the bound slot (e.g., t0 for a SRV). With SM 5.1, the + // declaration has three operand indices: the logical binding slot, the start register, and + // the end register. In addition, the register space is specified. + + // In order to match a declaration, we use the logical binding slot. This is identical for + // all cases - with SM 5.1 the compiler translates each binding from shader register(s) & + // register space into a unique identifier that is used to reference it in other instructions. + // For example, an SRV specified with (t0, space2) could be given T1 as its identifier. + + // When matching declarations, use operand index 0 to match with the instruction operand. + // When fetching data for a resource, with SM 5.0 or lower, use operand 0 as the shader + // register, and decl.space (which will always be 0) for the register space. With SM 5.1, + // use operand index 1 and 2 to get the shader register and use decl.space for the + // register space (which can be any value, as specified in HLSL and the root signature). + + // TODO: Need to test resource arrays to ensure correct behavior with SM 5.1 here + + if(program.IsShaderModel51()) + { + const Declaration *pDecl = program.FindDeclaration(declType, identifier); + if(pDecl) + return GetBindingSlotForDeclaration(program, *pDecl); + + RDCERR("Unable to find matching declaration for identifier %u", identifier); + } + + return ShaderDebug::BindingSlot(identifier, 0); +} + void GlobalState::PopulateGroupshared(const DXBCBytecode::Program *pBytecode) { for(size_t i = 0; i < pBytecode->GetNumDeclarations(); i++) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.h b/renderdoc/driver/shaders/dxbc/dxbc_debug.h index 4e9a23782..d445940c2 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.h @@ -35,6 +35,13 @@ class DXBCContainer; struct CBufferVariable; } +namespace DXBCBytecode +{ +struct Declaration; +class Program; +enum OperandType; +} + class WrappedID3D11Device; enum DXGI_FORMAT; @@ -57,6 +64,12 @@ struct BindingSlot uint32_t registerSpace; }; +ShaderDebug::BindingSlot GetBindingSlotForDeclaration(const DXBCBytecode::Program &program, + const DXBCBytecode::Declaration &decl); +ShaderDebug::BindingSlot GetBindingSlotForIdentifier(const DXBCBytecode::Program &program, + DXBCBytecode::OperandType declType, + uint32_t identifier); + struct GlobalState { public: @@ -101,6 +114,7 @@ public: uint32_t hiddenCounter; }; std::map uavs; + typedef std::map::iterator UAVIterator; struct SRVData { @@ -112,6 +126,7 @@ public: ViewFmt format; }; std::map srvs; + typedef std::map::iterator SRVIterator; struct groupsharedMem { diff --git a/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp b/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp index e7a153f77..0c4a7be74 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp @@ -2046,6 +2046,25 @@ bool Program::ExtractDecl(uint32_t *&tokenStream, Declaration &retDecl, bool fri return true; } +const Declaration *Program::FindDeclaration(OperandType declType, uint32_t identifier) const +{ + // Given a declType and identifier (together defining a binding such as t0, s1, etc.), + // return the matching declaration if it exists. The logic for this is the same for all + // shader model versions. + size_t numDeclarations = m_Declarations.size(); + for(size_t i = 0; i < numDeclarations; ++i) + { + const Declaration &decl = m_Declarations[i]; + if(decl.operand.type == declType) + { + if(decl.operand.indices[0].index == identifier) + return &decl; + } + } + + return NULL; +} + bool Program::ExtractOperation(uint32_t *&tokenStream, Operation &retOp, bool friendlyName) { uint32_t *begin = tokenStream;