Skip to content

Commit

Permalink
HACK: Start of DXIL Debugger
Browse files Browse the repository at this point in the history
  • Loading branch information
Zorro666 committed May 16, 2024
1 parent d43e97b commit f850930
Show file tree
Hide file tree
Showing 5 changed files with 1,199 additions and 5 deletions.
318 changes: 318 additions & 0 deletions renderdoc/driver/d3d12/d3d12_dxil_debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,321 @@
#pragma once

#include "d3d12_dxil_debug.h"
#include "d3d12_debug.h"
#include "d3d12_resources.h"

extern bool IsShaderParameterVisible(DXBC::ShaderType shaderType,
D3D12_SHADER_VISIBILITY shaderVisibility);

using namespace DXIL;
using namespace DXILDebug;

namespace DXILDebug
{
// ->dxbc, dxilDebugger->global, dxilDebugger->eventId);
D3D12APIWrapper::D3D12APIWrapper(WrappedID3D12Device *device, Debugger *debugger)
: m_Device(device), m_Debugger(debugger)
{
}

static void FlattenSingleVariable(const rdcstr &cbufferName, uint32_t byteOffset,
const rdcstr &basename, const ShaderVariable &v,
rdcarray<ShaderVariable> &outvars,
rdcarray<SourceVariableMapping> &sourcevars)
{
size_t outIdx = byteOffset / 16;
size_t outComp = (byteOffset % 16) / 4;

if(v.RowMajor())
outvars.resize(RDCMAX(outIdx + v.rows, outvars.size()));
else
outvars.resize(RDCMAX(outIdx + v.columns, outvars.size()));

if(outvars[outIdx].columns > 0)
{
// if we already have a variable in this slot, just copy the data for this variable and add the
// source mapping.
// We should not overlap into the next register as that's not allowed.
memcpy(&outvars[outIdx].value.u32v[outComp], &v.value.u32v[0], sizeof(uint32_t) * v.columns);

SourceVariableMapping mapping;
mapping.name = basename;
mapping.type = v.type;
mapping.rows = v.rows;
mapping.columns = v.columns;
mapping.offset = byteOffset;
mapping.variables.resize(v.columns);

for(int i = 0; i < v.columns; i++)
{
mapping.variables[i].type = DebugVariableType::Constant;
mapping.variables[i].name = StringFormat::Fmt("%s[%u]", cbufferName.c_str(), outIdx);
mapping.variables[i].component = uint16_t(outComp + i);
}

sourcevars.push_back(mapping);
}
else
{
const uint32_t numRegisters = v.RowMajor() ? v.rows : v.columns;
for(uint32_t reg = 0; reg < numRegisters; reg++)
{
outvars[outIdx + reg].rows = 1;
outvars[outIdx + reg].type = VarType::Unknown;
outvars[outIdx + reg].columns = v.columns;
outvars[outIdx + reg].flags = v.flags;
}

if(v.RowMajor())
{
for(size_t ri = 0; ri < v.rows; ri++)
memcpy(&outvars[outIdx + ri].value.u32v[0], &v.value.u32v[ri * v.columns],
sizeof(uint32_t) * v.columns);
}
else
{
// if we have a matrix stored in column major order, we need to transpose it back so we can
// unroll it into vectors.
for(size_t ci = 0; ci < v.columns; ci++)
for(size_t ri = 0; ri < v.rows; ri++)
outvars[outIdx + ci].value.u32v[ri] = v.value.u32v[ri * v.columns + ci];
}

SourceVariableMapping mapping;
mapping.name = basename;
mapping.type = v.type;
mapping.rows = v.rows;
mapping.columns = v.columns;
mapping.offset = byteOffset;
mapping.variables.resize(v.rows * v.columns);

RDCASSERT(outComp == 0 || v.rows == 1, outComp, v.rows);

size_t i = 0;
for(uint8_t r = 0; r < v.rows; r++)
{
for(uint8_t c = 0; c < v.columns; c++)
{
size_t regIndex = outIdx + (v.RowMajor() ? r : c);
size_t compIndex = outComp + (v.RowMajor() ? c : r);

mapping.variables[i].type = DebugVariableType::Constant;
mapping.variables[i].name = StringFormat::Fmt("%s[%zu]", cbufferName.c_str(), regIndex);
mapping.variables[i].component = uint16_t(compIndex);
i++;
}
}

sourcevars.push_back(mapping);
}
}

static void FlattenVariables(const rdcstr &cbufferName, const rdcarray<ShaderConstant> &constants,
const rdcarray<ShaderVariable> &invars,
rdcarray<ShaderVariable> &outvars, const rdcstr &prefix,
uint32_t baseOffset, rdcarray<SourceVariableMapping> &sourceVars)
{
RDCASSERTEQUAL(constants.size(), invars.size());

for(size_t i = 0; i < constants.size(); i++)
{
const ShaderConstant &c = constants[i];
const ShaderVariable &v = invars[i];

uint32_t byteOffset = baseOffset + c.byteOffset;

rdcstr basename = prefix + rdcstr(v.name);

if(v.type == VarType::Struct)
{
// check if this is an array of structs or not
if(c.type.elements == 1)
{
FlattenVariables(cbufferName, c.type.members, v.members, outvars, basename + ".",
byteOffset, sourceVars);
}
else
{
for(int m = 0; m < v.members.count(); m++)
{
FlattenVariables(cbufferName, c.type.members, v.members[m].members, outvars,
StringFormat::Fmt("%s[%zu].", basename.c_str(), m),
byteOffset + m * c.type.arrayByteStride, sourceVars);
}
}
}
else if(c.type.elements > 1 || (v.rows == 0 && v.columns == 0) || !v.members.empty())
{
for(int m = 0; m < v.members.count(); m++)
{
FlattenSingleVariable(cbufferName, byteOffset + m * c.type.arrayByteStride,
StringFormat::Fmt("%s[%zu]", basename.c_str(), m), v.members[m],
outvars, sourceVars);
}
}
else
{
FlattenSingleVariable(cbufferName, byteOffset, basename, v, outvars, sourceVars);
}
}
}
static void AddCBufferToGlobalState(const Program &program, GlobalState &global,
rdcarray<SourceVariableMapping> &sourceVars,
const ShaderReflection &refl, const BindingSlot &slot,
bytebuf &cbufData)
{
// Find the identifier
size_t numCBs = refl.constantBlocks.size();
for(size_t i = 0; i < numCBs; ++i)
{
const ConstantBlock &cb = refl.constantBlocks[i];
if(slot.registerSpace == (uint32_t)cb.fixedBindSetOrSpace &&
slot.shaderRegister >= (uint32_t)cb.fixedBindNumber &&
slot.shaderRegister < (uint32_t)(cb.fixedBindNumber + cb.bindArraySize))
{
uint32_t arrayIndex = slot.shaderRegister - cb.fixedBindNumber;

rdcarray<ShaderVariable> &targetVars =
cb.bindArraySize > 1 ? global.constantBlocks[i].members[arrayIndex].members
: global.constantBlocks[i].members;
RDCASSERTMSG("Reassigning previously filled cbuffer", targetVars.empty());

global.constantBlocks[i].name = program.GetResourceReferenceName(ResourceClass::CBuffer, slot);

SourceVariableMapping cbSourceMapping;
cbSourceMapping.name = refl.constantBlocks[i].name;
cbSourceMapping.variables.push_back(
DebugVariableReference(DebugVariableType::Constant, global.constantBlocks[i].name));
sourceVars.push_back(cbSourceMapping);

rdcstr identifierPrefix = global.constantBlocks[i].name;
rdcstr variablePrefix = refl.constantBlocks[i].name;
if(cb.bindArraySize > 1)
{
identifierPrefix =
StringFormat::Fmt("%s[%u]", global.constantBlocks[i].name.c_str(), arrayIndex);
variablePrefix = StringFormat::Fmt("%s[%u]", refl.constantBlocks[i].name.c_str(), arrayIndex);

// The above sourceVar is for the logical identifier, and FlattenVariables adds the
// individual elements of the constant buffer. For CB arrays, add an extra source
// var for the CB array index
SourceVariableMapping cbArrayMapping;
global.constantBlocks[i].members[arrayIndex].name = StringFormat::Fmt("[%u]", arrayIndex);
cbArrayMapping.name = variablePrefix;
cbArrayMapping.variables.push_back(
DebugVariableReference(DebugVariableType::Constant, identifierPrefix));
sourceVars.push_back(cbArrayMapping);
}
const rdcarray<ShaderConstant> &constants =
(cb.bindArraySize > 1) ? refl.constantBlocks[i].variables[0].type.members
: refl.constantBlocks[i].variables;

rdcarray<ShaderVariable> vars;
StandardFillCBufferVariables(refl.resourceId, constants, vars, cbufData);
FlattenVariables(identifierPrefix, constants, vars, targetVars, variablePrefix + ".", 0,
sourceVars);
for(size_t c = 0; c < targetVars.size(); c++)
targetVars[c].name = StringFormat::Fmt("[%u]", (uint32_t)c);

return;
}
}
}

void FetchConstantBufferData(WrappedID3D12Device *pDevice, const Program &program,
const D3D12RenderState::RootSignature &rootsig,
const ShaderReflection &refl, GlobalState &global,
rdcarray<SourceVariableMapping> &sourceVars)
{
WrappedID3D12RootSignature *pD3D12RootSig =
pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12RootSignature>(rootsig.rootsig);

size_t numParams = RDCMIN(pD3D12RootSig->sig.Parameters.size(), rootsig.sigelems.size());
for(size_t i = 0; i < numParams; i++)
{
const D3D12RootSignatureParameter &rootSigParam = pD3D12RootSig->sig.Parameters[i];
const D3D12RenderState::SignatureElement &element = rootsig.sigelems[i];
if(IsShaderParameterVisible(program.GetShaderType(), rootSigParam.ShaderVisibility))
{
if(rootSigParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS &&
element.type == eRootConst)
{
BindingSlot slot(rootSigParam.Constants.ShaderRegister, rootSigParam.Constants.RegisterSpace);
UINT sizeBytes = sizeof(uint32_t) * RDCMIN(rootSigParam.Constants.Num32BitValues,
(UINT)element.constants.size());
bytebuf cbufData((const byte *)element.constants.data(), sizeBytes);
AddCBufferToGlobalState(program, global, sourceVars, refl, slot, cbufData);
}
else if(rootSigParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_CBV && element.type == eRootCBV)
{
BindingSlot slot(rootSigParam.Descriptor.ShaderRegister,
rootSigParam.Descriptor.RegisterSpace);
ID3D12Resource *cbv = pDevice->GetResourceManager()->GetCurrentAs<ID3D12Resource>(element.id);
bytebuf cbufData;
pDevice->GetDebugManager()->GetBufferData(cbv, element.offset, 0, cbufData);
AddCBufferToGlobalState(program, global, sourceVars, refl, slot, cbufData);
}
else if(rootSigParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE &&
element.type == eRootTable)
{
UINT prevTableOffset = 0;
WrappedID3D12DescriptorHeap *heap =
pDevice->GetResourceManager()->GetCurrentAs<WrappedID3D12DescriptorHeap>(element.id);

size_t numRanges = rootSigParam.ranges.size();
for(size_t r = 0; r < numRanges; r++)
{
// For this traversal we only care about CBV descriptor ranges, but we still need to
// calculate the table offsets in case a descriptor table has a combination of
// different range types
const D3D12_DESCRIPTOR_RANGE1 &range = rootSigParam.ranges[r];

UINT offset = range.OffsetInDescriptorsFromTableStart;
if(range.OffsetInDescriptorsFromTableStart == D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
offset = prevTableOffset;

D3D12Descriptor *desc = (D3D12Descriptor *)heap->GetCPUDescriptorHandleForHeapStart().ptr;
desc += element.offset;
desc += offset;

UINT numDescriptors = range.NumDescriptors;
if(numDescriptors == UINT_MAX)
{
// Find out how many descriptors are left after
numDescriptors = heap->GetNumDescriptors() - offset - (UINT)element.offset;

// TODO: Look up the bind point in the D3D12 state to try to get
// a better guess at the number of descriptors
}

prevTableOffset = offset + numDescriptors;

if(range.RangeType != D3D12_DESCRIPTOR_RANGE_TYPE_CBV)
continue;

BindingSlot slot(range.BaseShaderRegister, range.RegisterSpace);

bytebuf cbufData;
for(UINT n = 0; n < numDescriptors; ++n, ++slot.shaderRegister)
{
const D3D12_CONSTANT_BUFFER_VIEW_DESC &cbv = desc->GetCBV();
ResourceId resId;
uint64_t byteOffset = 0;
WrappedID3D12Resource::GetResIDFromAddr(cbv.BufferLocation, resId, byteOffset);
ID3D12Resource *pCbvResource =
pDevice->GetResourceManager()->GetCurrentAs<ID3D12Resource>(resId);
cbufData.clear();

if(cbv.SizeInBytes > 0)
pDevice->GetDebugManager()->GetBufferData(pCbvResource, byteOffset, cbv.SizeInBytes,
cbufData);
AddCBufferToGlobalState(program, global, sourceVars, refl, slot, cbufData);

desc++;
}
}
}
}
}
}
};
25 changes: 25 additions & 0 deletions renderdoc/driver/d3d12/d3d12_dxil_debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,28 @@
******************************************************************************/

#pragma once

#include "driver/shaders/dxil/dxil_debug.h"
#include "d3d12_device.h"
#include "d3d12_state.h"

namespace DXILDebug
{
class Debugger;

void FetchConstantBufferData(WrappedID3D12Device *pDevice, const DXIL::Program &program,
const D3D12RenderState::RootSignature &rootsig,
const ShaderReflection &refl, DXILDebug::GlobalState &global,
rdcarray<SourceVariableMapping> &sourceVars);

class D3D12APIWrapper
{
public:
D3D12APIWrapper(WrappedID3D12Device *device, Debugger *debugger);
~D3D12APIWrapper() = default;
private:
const WrappedID3D12Device *m_Device;
const Debugger *m_Debugger;
};

};
Loading

0 comments on commit f850930

Please sign in to comment.