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.
This commit is contained in:
Steve Karolewics
2020-01-20 17:46:16 +00:00
committed by Baldur Karlsson
parent a5c54b3ce7
commit 8fb23b9010
5 changed files with 209 additions and 96 deletions
@@ -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;
@@ -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<uint32_t> &GetImmediateConstantBuffer() const { return m_Immediate; }
+164 -87
View File
@@ -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 <float3> 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++)
@@ -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<BindingSlot, UAVData> uavs;
typedef std::map<BindingSlot, UAVData>::iterator UAVIterator;
struct SRVData
{
@@ -112,6 +126,7 @@ public:
ViewFmt format;
};
std::map<BindingSlot, SRVData> srvs;
typedef std::map<BindingSlot, SRVData>::iterator SRVIterator;
struct groupsharedMem
{
@@ -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;