Populate constants in global state for vulkan debugging

This commit is contained in:
baldurk
2020-02-14 15:38:29 +00:00
parent 7f38c690d8
commit 013ea145d4
3 changed files with 158 additions and 1 deletions
@@ -36,6 +36,9 @@ public:
virtual ~DebugAPIWrapper() {}
virtual void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d) = 0;
// TODO handle arrays of cbuffers
virtual void ReadConstantBufferValue(uint32_t set, uint32_t bind, uint32_t offset,
uint32_t byteSize, void *dst) = 0;
virtual void FillInputValue(ShaderVariable &var, ShaderBuiltin builtin, uint32_t location,
uint32_t offset) = 0;
};
@@ -26,6 +26,12 @@
#include "common/formatting.h"
#include "spirv_op_helpers.h"
static uint32_t VarByteSize(const ShaderVariable &var)
{
return VarTypeByteSize(var.type) * RDCMAX(1U, (uint32_t)var.rows) *
RDCMAX(1U, (uint32_t)var.columns);
}
namespace rdcspv
{
void AssignValue(ShaderVariable &dst, const ShaderVariable &src)
@@ -83,7 +89,7 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader
for(auto it = constants.begin(); it != constants.end(); it++)
active.ids[it->first] = EvaluateConstant(it->first, specInfo);
rdcarray<Id> inputIDs, outputIDs;
rdcarray<Id> inputIDs, outputIDs, cbufferIDs;
// allocate storage for globals with opaque storage classes, and prepare to set up pointers to
// them for the global variables themselves
@@ -129,6 +135,48 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader
outputIDs.push_back(v.id);
}
}
// pick up uniform globals, which could be cbuffers
if(v.storage == StorageClass::Uniform && (decorations[v.id].flags & Decorations::BufferBlock) == 0)
{
ShaderVariable var;
var.name = GetRawName(v.id);
rdcstr sourceName = strings[v.id];
if(sourceName.empty())
sourceName = var.name;
const DataType &type = dataTypes[v.type];
// global variables should all be pointers into opaque storage
RDCASSERT(type.type == DataType::PointerType);
const DataType &innertype = dataTypes[type.InnerType()];
if(innertype.type == DataType::ArrayType)
{
RDCERR("uniform Arrays not supported yet");
}
else if(innertype.type == DataType::StructType)
{
uint32_t offset = 0;
AllocateVariable(decorations[v.id], decorations[v.id], DebugVariableType::Constant,
sourceName, 0, innertype, var);
global.constantBlocks.push_back(var);
cbufferIDs.push_back(v.id);
SourceVariableMapping sourceVar;
sourceVar.name = sourceName;
sourceVar.type = VarType::Unknown;
sourceVar.rows = 0;
sourceVar.columns = 0;
sourceVar.offset = 0;
sourceVar.variables.push_back(DebugVariableReference(DebugVariableType::Constant, var.name));
sourceVars.push_back(sourceVar);
}
}
}
// now that the globals are allocated and their storage won't move, we can take pointers to them
@@ -136,6 +184,8 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader
active.ids[inputIDs[i]] = MakePointerVariable(inputIDs[i], &active.inputs[i]);
for(size_t i = 0; i < active.outputs.size(); i++)
active.ids[outputIDs[i]] = MakePointerVariable(outputIDs[i], &active.outputs[i]);
for(size_t i = 0; i < global.constantBlocks.size(); i++)
active.ids[cbufferIDs[i]] = MakePointerVariable(cbufferIDs[i], &global.constantBlocks[i]);
// only outputs are considered mutable
active.live.append(outputIDs);
@@ -531,6 +581,60 @@ void Debugger::AllocateVariable(const Decorations &varDecorations, const Decorat
(curDecorations.flags & Decorations::HasLocation) ? curDecorations.location : 0,
(curDecorations.flags & Decorations::HasOffset) ? curDecorations.offset : 0);
}
else if(sourceVarType == DebugVariableType::Constant)
{
uint32_t set = 0, bind = 0;
if(varDecorations.flags & Decorations::HasDescriptorSet)
set = varDecorations.set;
if(varDecorations.flags & Decorations::HasBinding)
bind = varDecorations.binding;
// non-matrix case is simple, just read the size of the variable
if(sourceVar.rows == 1)
{
apiWrapper->ReadConstantBufferValue(set, bind, offset, VarByteSize(outVar), outVar.value.uv);
}
else
{
// matrix case is more complicated. Either read column by column or row by row depending on
// majorness
uint32_t matrixStride = curDecorations.matrixStride;
if(!(curDecorations.flags & Decorations::HasMatrixStride))
{
RDCWARN("Matrix without matrix stride - assuming legacy vec4 packed");
matrixStride = 16;
}
if(curDecorations.flags & Decorations::ColMajor)
{
ShaderValue tmp;
uint32_t colSize = VarTypeByteSize(sourceVar.type) * sourceVar.rows;
for(uint32_t c = 0; c < sourceVar.columns; c++)
{
// read the column
apiWrapper->ReadConstantBufferValue(set, bind, offset + c * matrixStride, colSize,
&tmp.uv[0]);
// now write it into the appropiate elements in the destination ShaderValue
for(uint32_t r = 0; r < sourceVar.rows; r++)
outVar.value.uv[r * sourceVar.columns + c] = tmp.uv[r];
}
}
else
{
// row major is easier, read row-by-row directly into the output variable
uint32_t rowSize = VarTypeByteSize(sourceVar.type) * sourceVar.columns;
for(uint32_t r = 0; r < sourceVar.rows; r++)
{
// read the column into the destination ShaderValue, which is tightly packed with rows
apiWrapper->ReadConstantBufferValue(set, bind, offset + r * matrixStride, rowSize,
&outVar.value.uv[r * sourceVar.columns]);
}
}
}
}
}
void Debugger::PreParse(uint32_t maxId)
@@ -38,6 +38,19 @@ public:
m_pDriver->AddDebugMessage(c, sv, src, d);
}
virtual void ReadConstantBufferValue(uint32_t set, uint32_t bind, uint32_t offset,
uint32_t byteSize, void *dst) override
{
auto it = cbuffers.find(make_rdcpair(set, bind));
if(it == cbuffers.end())
return;
bytebuf &data = it->second;
if(offset + byteSize <= data.size())
memcpy(dst, data.data() + offset, byteSize);
}
virtual void FillInputValue(ShaderVariable &var, ShaderBuiltin builtin, uint32_t location,
uint32_t offset) override
{
@@ -65,6 +78,7 @@ public:
RDCERR("Couldn't get input for location=%u, offset=%u", location, offset);
}
std::map<rdcpair<uint32_t, uint32_t>, bytebuf> cbuffers;
std::map<ShaderBuiltin, ShaderVariable> builtin_inputs;
rdcarray<ShaderVariable> location_inputs;
@@ -97,6 +111,42 @@ ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, u
shadRefl.PopulateDisassembly(shader.spirv);
VulkanAPIWrapper *apiWrapper = new VulkanAPIWrapper(m_pDriver);
for(uint32_t set = 0; set < state.graphics.descSets.size(); set++)
{
const VulkanStatePipeline::DescriptorAndOffsets &src = state.graphics.descSets[set];
const WrappedVulkan::DescriptorSetInfo &setInfo = m_pDriver->m_DescriptorSetState[src.descSet];
ResourceId layoutId = setInfo.layout;
uint32_t dynOffsetIdx = 0;
for(uint32_t bind = 0; bind < setInfo.currentBindings.size(); bind++)
{
DescriptorSetSlot *info = setInfo.currentBindings[bind];
const DescSetLayout::Binding &layoutBind = c.m_DescSetLayout[layoutId].bindings[bind];
if(layoutBind.stageFlags == 0)
continue;
uint32_t dynOffset = 0;
if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC ||
layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
dynOffset = src.offsets[dynOffsetIdx++];
// TODO handle arrays of bindings
const uint32_t arrayIdx = 0;
if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
{
const DescriptorSetSlotBufferInfo &bufInfo = info[arrayIdx].bufferInfo;
GetDebugManager()->GetBufferData(bufInfo.buffer, bufInfo.offset + dynOffset, bufInfo.range,
apiWrapper->cbuffers[make_rdcpair(set, bind)]);
}
}
}
std::map<ShaderBuiltin, ShaderVariable> &builtins = apiWrapper->builtin_inputs;
builtins[ShaderBuiltin::BaseInstance] = ShaderVariable(rdcstr(), draw->instanceOffset, 0U, 0U, 0U);
builtins[ShaderBuiltin::BaseVertex] = ShaderVariable(