mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 01:50:38 +00:00
Support SPIR-V pointers with our own pointers
* When we need a SPIR-V pointer we box up a real pointer to a ShaderVariable, and optionally column/scalar selectors if it's a pointer to a scalar within a vector or matrix. * For globals in opaque storage classes we'll allocate our own ShaderVariables so that we can safely keep pointers to them. Otherwise IDs of temporary pointers (from OpAccessChain) should always be a strict subset of the lifetime of the ID it's pointing to, so we can keep the pointer around. The variables don't move and are persistently allocated so they won't be relocated from under us (we pre-allocate space for the IDs). * Before passing back a ShaderVariable to the outside world, we evaluate its current value. Thus pointers are presented as if they are just variables that happen to update when their parent does.
This commit is contained in:
@@ -61,6 +61,14 @@ public:
|
||||
// every ID's variable, if a pointer it may be pointing at a ShaderVariable stored elsewhere
|
||||
DenseIdMap<ShaderVariable> ids;
|
||||
|
||||
// for any allocated variables, a list of 'extra' pointers pointing to it. By default the actual
|
||||
// storage of allocated variables is not directly accessible (it's stored in e.g. inputs, outputs,
|
||||
// global constants, stack frame variables, etc). The ID for the allocating OpVariable is replaced
|
||||
// with a pointer pointing to that storage. However more pointers can be generated with
|
||||
// OpAccessChain etc, and these pointers must be listed as changed whenever the underlying Id
|
||||
// changes (and vice-versa - a change via any of those pointers must update all other pointers).
|
||||
SparseIdMap<rdcarray<Id>> pointersForId;
|
||||
|
||||
// the list of IDs that are currently valid and live
|
||||
rdcarray<Id> live;
|
||||
|
||||
@@ -90,6 +98,13 @@ private:
|
||||
virtual void PostParse();
|
||||
virtual void RegisterOp(Iter it);
|
||||
|
||||
ShaderVariable EvaluatePointerVariable(const ShaderVariable &v) const;
|
||||
ShaderVariable MakePointerVariable(Id id, const ShaderVariable *v, uint32_t scalar0 = ~0U,
|
||||
uint32_t scalar1 = ~0U) const;
|
||||
Id GetPointerBaseId(const ShaderVariable &v) const;
|
||||
void WriteThroughPointer(const ShaderVariable &ptr, const ShaderVariable &val);
|
||||
ShaderVariable MakeCompositePointer(const ShaderVariable &base, Id id, rdcarray<uint32_t> &indices);
|
||||
|
||||
DebugAPIWrapper *apiWrapper = NULL;
|
||||
|
||||
GlobalState global;
|
||||
@@ -114,11 +129,16 @@ private:
|
||||
|
||||
rdcarray<MemberName> memberNames;
|
||||
|
||||
rdcstr GetRawName(Id id);
|
||||
rdcstr GetRawName(Id id) const;
|
||||
rdcstr GetHumanName(Id id);
|
||||
|
||||
std::set<rdcstr> usedNames;
|
||||
std::map<Id, rdcstr> dynamicNames;
|
||||
};
|
||||
|
||||
// this does a 'safe' value assignment, by doing parallel depth-first iteration of both variables
|
||||
// and only copying the value itself. This ensures we don't change any locations that might be
|
||||
// pointed to. Assignments should only ever be between compatible types so this should be safe.
|
||||
void AssignValue(ShaderVariable &dst, const ShaderVariable &src);
|
||||
|
||||
}; // namespace rdcspv
|
||||
|
||||
@@ -28,6 +28,16 @@
|
||||
|
||||
namespace rdcspv
|
||||
{
|
||||
void AssignValue(ShaderVariable &dst, const ShaderVariable &src)
|
||||
{
|
||||
dst.value = src.value;
|
||||
|
||||
RDCASSERTEQUAL(dst.members.size(), src.members.size());
|
||||
|
||||
for(size_t i = 0; i < src.members.size(); i++)
|
||||
AssignValue(dst.members[i], src.members[i]);
|
||||
}
|
||||
|
||||
ThreadState::ThreadState(int workgroupIdx, GlobalState &globalState) : global(globalState)
|
||||
{
|
||||
workgroupIndex = workgroupIdx;
|
||||
@@ -105,7 +115,7 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
|
||||
ShaderDebugState initial;
|
||||
|
||||
for(const Id &v : active.live)
|
||||
initial.changes.push_back({ShaderVariable(), active.ids[v]});
|
||||
initial.changes.push_back({ShaderVariable(), EvaluatePointerVariable(active.ids[v])});
|
||||
|
||||
initial.sourceVars = sourceVars;
|
||||
|
||||
@@ -117,7 +127,191 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
|
||||
return ret;
|
||||
}
|
||||
|
||||
rdcstr Debugger::GetRawName(Id id)
|
||||
ShaderVariable Debugger::MakePointerVariable(Id id, const ShaderVariable *v, uint32_t scalar0,
|
||||
uint32_t scalar1) const
|
||||
{
|
||||
ShaderVariable var;
|
||||
var.rows = var.columns = 1;
|
||||
var.type = VarType::ULong;
|
||||
var.name = GetRawName(id);
|
||||
var.isPointer = true;
|
||||
// encode the pointer into the first u64v
|
||||
var.value.u64v[0] = (uint64_t)(uintptr_t)v;
|
||||
|
||||
// uv[1] overlaps with u64v[0], so start from [2] storing scalar indices
|
||||
var.value.uv[2] = scalar0;
|
||||
var.value.uv[3] = scalar1;
|
||||
// store the base ID of the allocated storage in [4]
|
||||
var.value.uv[4] = id.value();
|
||||
return var;
|
||||
}
|
||||
|
||||
ShaderVariable Debugger::MakeCompositePointer(const ShaderVariable &base, Id id,
|
||||
rdcarray<uint32_t> &indices)
|
||||
{
|
||||
const ShaderVariable *leaf = &base;
|
||||
|
||||
// if the base is a plain value, we just start walking down the chain. If the base is a pointer
|
||||
// though, we want to step down the chain in the underlying storage, so dereference first.
|
||||
if(base.isPointer)
|
||||
leaf = (const ShaderVariable *)(uintptr_t)base.value.u64v[0];
|
||||
|
||||
// first walk any struct member/array indices
|
||||
size_t i = 0;
|
||||
while(!leaf->members.empty())
|
||||
{
|
||||
RDCASSERT(i < indices.size(), i, indices.size());
|
||||
leaf = &leaf->members[indices[i++]];
|
||||
}
|
||||
|
||||
// apply any remaining scalar selectors
|
||||
uint32_t scalar0 = ~0U, scalar1 = ~0U;
|
||||
|
||||
size_t remaining = indices.size() - i;
|
||||
if(remaining == 2)
|
||||
{
|
||||
scalar0 = indices[i];
|
||||
scalar1 = indices[i + 1];
|
||||
}
|
||||
else if(remaining == 1)
|
||||
{
|
||||
scalar0 = indices[i];
|
||||
}
|
||||
|
||||
return MakePointerVariable(id, leaf, scalar0, scalar1);
|
||||
}
|
||||
|
||||
ShaderVariable Debugger::EvaluatePointerVariable(const ShaderVariable &ptr) const
|
||||
{
|
||||
if(!ptr.isPointer)
|
||||
return ptr;
|
||||
|
||||
ShaderVariable ret;
|
||||
ret = *(const ShaderVariable *)(uintptr_t)ptr.value.u64v[0];
|
||||
ret.name = ptr.name;
|
||||
|
||||
// we don't support pointers to scalars since our 'unit' of pointer is a ShaderVariable, so check
|
||||
// if we have scalar indices to apply:
|
||||
uint32_t scalar0 = ptr.value.uv[2];
|
||||
uint32_t scalar1 = ptr.value.uv[3];
|
||||
|
||||
ShaderValue val;
|
||||
|
||||
if(ret.rows > 1)
|
||||
{
|
||||
// matrix case
|
||||
|
||||
if(scalar0 != ~0U && scalar1 != ~0U)
|
||||
{
|
||||
// two indices - selecting a scalar. scalar0 is the first index in the chain so it chooses
|
||||
// column
|
||||
if(VarTypeByteSize(ret.type) == 8)
|
||||
val.u64v[0] = ret.value.u64v[scalar1 * ret.columns + scalar0];
|
||||
else
|
||||
val.uv[0] = ret.value.uv[scalar1 * ret.columns + scalar0];
|
||||
|
||||
// it's a scalar now, even if it was a matrix before
|
||||
ret.rows = ret.columns = 1;
|
||||
ret.value = val;
|
||||
}
|
||||
else if(scalar0 != ~0U)
|
||||
{
|
||||
// one index, selecting a column
|
||||
for(uint32_t row = 0; row < ret.rows; row++)
|
||||
{
|
||||
if(VarTypeByteSize(ret.type) == 8)
|
||||
val.u64v[0] = ret.value.u64v[row * ret.columns + scalar0];
|
||||
else
|
||||
val.uv[0] = ret.value.uv[row * ret.columns + scalar0];
|
||||
}
|
||||
|
||||
// it's a vector now, even if it was a matrix before
|
||||
ret.rows = 1;
|
||||
ret.value = val;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// vector case, selecting a scalar (if anything)
|
||||
if(scalar0 != ~0U)
|
||||
{
|
||||
if(VarTypeByteSize(ret.type) == 8)
|
||||
val.u64v[0] = ret.value.u64v[scalar0];
|
||||
else
|
||||
val.uv[0] = ret.value.uv[scalar0];
|
||||
|
||||
// it's a scalar now, even if it was a matrix before
|
||||
ret.columns = 1;
|
||||
ret.value = val;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Id Debugger::GetPointerBaseId(const ShaderVariable &ptr) const
|
||||
{
|
||||
RDCASSERT(ptr.isPointer);
|
||||
|
||||
// we stored the base ID in [4] so that it's always available regardless of access chains
|
||||
return Id::fromWord(ptr.value.uv[4]);
|
||||
}
|
||||
|
||||
void Debugger::WriteThroughPointer(const ShaderVariable &ptr, const ShaderVariable &val)
|
||||
{
|
||||
ShaderVariable *storage = (ShaderVariable *)(uintptr_t)ptr.value.u64v[0];
|
||||
|
||||
// we don't support pointers to scalars since our 'unit' of pointer is a ShaderVariable, so check
|
||||
// if we have scalar indices to apply:
|
||||
uint32_t scalar0 = ptr.value.uv[2];
|
||||
uint32_t scalar1 = ptr.value.uv[3];
|
||||
|
||||
// in the common case we don't have scalar selectors. In this case just assign the value
|
||||
if(scalar0 == ~0U && scalar1 == ~0U)
|
||||
{
|
||||
AssignValue(*storage, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise we need to store only the selected part of this pointer. We assume by SPIR-V
|
||||
// validity rules that the incoming value matches the pointed value
|
||||
if(storage->rows > 1)
|
||||
{
|
||||
// matrix case
|
||||
|
||||
if(scalar0 != ~0U && scalar1 != ~0U)
|
||||
{
|
||||
// two indices - selecting a scalar. scalar0 is the first index in the chain so it chooses
|
||||
// column
|
||||
if(VarTypeByteSize(storage->type) == 8)
|
||||
storage->value.u64v[scalar1 * storage->columns + scalar0] = val.value.u64v[0];
|
||||
else
|
||||
storage->value.uv[scalar1 * storage->columns + scalar0] = val.value.uv[0];
|
||||
}
|
||||
else if(scalar0 != ~0U)
|
||||
{
|
||||
// one index, selecting a column
|
||||
for(uint32_t row = 0; row < storage->rows; row++)
|
||||
{
|
||||
if(VarTypeByteSize(storage->type) == 8)
|
||||
storage->value.u64v[row * storage->columns + scalar0] = val.value.u64v[0];
|
||||
else
|
||||
storage->value.uv[row * storage->columns + scalar0] = val.value.uv[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// vector case, selecting a scalar
|
||||
if(VarTypeByteSize(storage->type) == 8)
|
||||
storage->value.u64v[scalar0] = val.value.u64v[0];
|
||||
else
|
||||
storage->value.uv[scalar0] = val.value.uv[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rdcstr Debugger::GetRawName(Id id) const
|
||||
{
|
||||
return StringFormat::Fmt("_%u", id.value());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user