mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-28 01:16:31 +00:00
Spirv Debugger GroupShared simulation changes
On the active thread * GSM reads come from the local GSM cache (not the global GSM data) * GSM writes go to the local GSM cache and the global GSM data * Workgroup Memory barrier populates the local GSM cache with the data from the global GSM data On the non-active threads: * GSM reads/write always operate on the global GSM data * Workgroup Memory barrier is ignored
This commit is contained in:
@@ -425,6 +425,11 @@ void ThreadState::WritePointerValue(Id pointer, const ShaderVariable &val)
|
||||
|
||||
for(size_t i = 0; i < pointers.size(); i++)
|
||||
lastWrite[pointers[i]] = m_State ? m_State->stepIndex : nextInstruction;
|
||||
|
||||
// For GSM memory update the global data as well as the local cache, do not send the changes to the UI
|
||||
auto gsmPtrIt = gsmPointers.find(pointer);
|
||||
if(gsmPtrIt != gsmPointers.end())
|
||||
debugger.WriteThroughPointer(gsmPtrIt->second, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,8 +855,20 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray<ThreadState>
|
||||
for(Id id : chain.indexes)
|
||||
indices.push_back(uintComp(GetSrc(id), 0));
|
||||
|
||||
SetDst(chain.result, debugger.MakeCompositePointer(
|
||||
ids[chain.base], debugger.GetPointerBaseId(ids[chain.base]), indices));
|
||||
Id baseId = debugger.GetPointerBaseId(ids[chain.base]);
|
||||
SetDst(chain.result, debugger.MakeCompositePointer(ids[chain.base], baseId, indices));
|
||||
|
||||
// create duplicate GSM pointers for the active thread which point to the global GSM not the local GSM cache
|
||||
if(m_State)
|
||||
{
|
||||
auto gsmPtrIt = gsmPointers.find(chain.base);
|
||||
if(gsmPtrIt != gsmPointers.end())
|
||||
{
|
||||
ShaderVariable gsmGlobal = debugger.MakeCompositePointer(gsmPtrIt->second, baseId, indices);
|
||||
gsmGlobal.name = GetRawName(chain.result);
|
||||
gsmPointers[chain.result] = gsmGlobal;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Op::PtrAccessChain:
|
||||
@@ -870,10 +887,26 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray<ThreadState>
|
||||
int32_t element = intComp(GetSrc(chain.element), 0);
|
||||
// adjust the address by the element. We should have the array stride since the base pointer
|
||||
// must point into an array and we can't go outside it.
|
||||
base.SetTypedPointer(val.pointer + element * debugger.GetPointerArrayStride(base), val.shader,
|
||||
val.pointerTypeID);
|
||||
SetDst(chain.result,
|
||||
debugger.MakeCompositePointer(base, debugger.GetPointerBaseId(base), indices));
|
||||
uint64_t byteOffset = element * debugger.GetPointerArrayStride(base);
|
||||
base.SetTypedPointer(val.pointer + byteOffset, val.shader, val.pointerTypeID);
|
||||
Id baseId = debugger.GetPointerBaseId(ids[chain.base]);
|
||||
SetDst(chain.result, debugger.MakeCompositePointer(base, baseId, indices));
|
||||
|
||||
// create duplicate GSM pointers for the active thread which point to the global GSM not the local GSM cache
|
||||
if(m_State)
|
||||
{
|
||||
auto gsmPtrIt = gsmPointers.find(chain.base);
|
||||
if(gsmPtrIt != gsmPointers.end())
|
||||
{
|
||||
ShaderVariable gsmBase = gsmPtrIt->second;
|
||||
PointerVal gsmVal = gsmBase.GetPointer();
|
||||
gsmBase.SetTypedPointer(gsmVal.pointer + byteOffset, gsmVal.shader, gsmVal.pointerTypeID);
|
||||
|
||||
ShaderVariable gsmGlobal = debugger.MakeCompositePointer(gsmBase, baseId, indices);
|
||||
gsmGlobal.name = GetRawName(chain.result);
|
||||
gsmPointers[chain.result] = gsmGlobal;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Op::ArrayLength:
|
||||
@@ -3780,11 +3813,14 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray<ThreadState>
|
||||
|
||||
case Op::MemoryBarrier:
|
||||
{
|
||||
// do nothing for now
|
||||
OpMemoryBarrier barrier(it);
|
||||
ExecuteMemoryBarrier(barrier.semantics);
|
||||
break;
|
||||
}
|
||||
case Op::ControlBarrier:
|
||||
{
|
||||
OpControlBarrier barrier(it);
|
||||
ExecuteMemoryBarrier(barrier.semantics);
|
||||
// For thread barriers the threads must be converged
|
||||
RDCASSERT(!WorkgroupIsDiverged(workgroup));
|
||||
break;
|
||||
@@ -4935,4 +4971,44 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray<ThreadState>
|
||||
m_State = NULL;
|
||||
}
|
||||
|
||||
void ThreadState::ExecuteMemoryBarrier(Id semanticsId)
|
||||
{
|
||||
// ignore if not the acitve thread
|
||||
if(!m_State)
|
||||
return;
|
||||
|
||||
ShaderVariable var = GetSrc(semanticsId);
|
||||
MemorySemantics semantics = (MemorySemantics)var.value.u32v[0];
|
||||
// only workgroup memory barriers are supported
|
||||
if(!(semantics & MemorySemantics::WorkgroupMemory))
|
||||
return;
|
||||
|
||||
// copy the global GSM memory into the local GSM cache
|
||||
for(const GSMIndex &gsmIndex : gsmIndexes)
|
||||
{
|
||||
const int32_t globalIndex = gsmIndex.global;
|
||||
const int32_t localIndex = gsmIndex.local;
|
||||
if(globalIndex < global.workgroups.count())
|
||||
{
|
||||
if(localIndex < privates.count())
|
||||
{
|
||||
ShaderVariableChange change;
|
||||
const ShaderVariable &globalData = global.workgroups[globalIndex];
|
||||
change.before = privates[localIndex];
|
||||
AssignValue(privates[localIndex], globalData);
|
||||
change.after = privates[localIndex];
|
||||
if(!(change.after == change.before))
|
||||
m_State->changes.push_back(change);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Invalid GSM local index %u MAX %u", localIndex, privates.count());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Invalid GSM index %u MAX %u", globalIndex, global.workgroups.count());
|
||||
}
|
||||
}
|
||||
}
|
||||
}; // namespace rdcspv
|
||||
|
||||
@@ -225,6 +225,14 @@ struct ThreadState
|
||||
// changes (and vice-versa - a change via any of those pointers must update all other pointers).
|
||||
SparseIdMap<rdcarray<Id>> pointersForId;
|
||||
|
||||
SparseIdMap<ShaderVariable> gsmPointers;
|
||||
struct GSMIndex
|
||||
{
|
||||
int32_t global;
|
||||
int32_t local;
|
||||
};
|
||||
rdcarray<GSMIndex> gsmIndexes;
|
||||
|
||||
// the id of the merge block that the last branch targetted
|
||||
Id mergeBlock;
|
||||
uint32_t convergenceInstruction;
|
||||
@@ -270,6 +278,7 @@ private:
|
||||
void SkipIgnoredInstructions();
|
||||
void SetConvergencePoint(Id block);
|
||||
|
||||
void ExecuteMemoryBarrier(Id semanticsId);
|
||||
static bool WorkgroupIsDiverged(const rdcarray<ThreadState> &workgroup);
|
||||
|
||||
ShaderDebugState *m_State = NULL;
|
||||
|
||||
@@ -1006,30 +1006,48 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s
|
||||
struct PointerId
|
||||
{
|
||||
PointerId(Id i, rdcarray<ShaderVariable> GlobalState::*th, rdcarray<ShaderVariable> &storage)
|
||||
: id(i), globalStorage(th), index(storage.size() - 1)
|
||||
: id(i), globalStorage(th), globalIndex(storage.size() - 1)
|
||||
{
|
||||
}
|
||||
PointerId(Id i, rdcarray<ShaderVariable> ThreadState::*th, rdcarray<ShaderVariable> &storage)
|
||||
: id(i), threadStorage(th), index(storage.size() - 1)
|
||||
: id(i), threadStorage(th), threadIndex(storage.size() - 1)
|
||||
{
|
||||
}
|
||||
PointerId(Id i, rdcarray<ShaderVariable> GlobalState::*global,
|
||||
rdcarray<ShaderVariable> &globalVars, rdcarray<ShaderVariable> ThreadState::*thread,
|
||||
rdcarray<ShaderVariable> &threadVars)
|
||||
: id(i),
|
||||
globalStorage(global),
|
||||
globalIndex(globalVars.size() - 1),
|
||||
threadStorage(thread),
|
||||
threadIndex(threadVars.size() - 1)
|
||||
{
|
||||
}
|
||||
|
||||
void Set(Debugger &d, const GlobalState &global, ThreadState &lane) const
|
||||
void Set(Debugger &d, const GlobalState &global, ThreadState &lane, bool forceLocalGSM) const
|
||||
{
|
||||
if(globalStorage)
|
||||
lane.ids[id] = d.MakePointerVariable(id, &(global.*globalStorage)[index]);
|
||||
const bool isGlobal = (globalIndex != UINT_MAX);
|
||||
const bool isGSM = isGlobal && (threadIndex != UINT_MAX);
|
||||
const bool useLocal = (forceLocalGSM && isGSM) || !isGlobal;
|
||||
|
||||
if(!useLocal)
|
||||
lane.ids[id] = d.MakePointerVariable(id, &(global.*globalStorage)[globalIndex]);
|
||||
else
|
||||
lane.ids[id] = d.MakePointerVariable(id, &(lane.*threadStorage)[index]);
|
||||
lane.ids[id] = d.MakePointerVariable(id, &(lane.*threadStorage)[threadIndex]);
|
||||
}
|
||||
|
||||
Id id;
|
||||
rdcarray<ShaderVariable> GlobalState::*globalStorage = NULL;
|
||||
rdcarray<ShaderVariable> ThreadState::*threadStorage = NULL;
|
||||
size_t index;
|
||||
size_t globalIndex = UINT_MAX;
|
||||
size_t threadIndex = UINT_MAX;
|
||||
};
|
||||
|
||||
#define GLOBAL_POINTER(id, list) PointerId(id, &GlobalState::list, global.list)
|
||||
#define THREAD_POINTER(id, list) PointerId(id, &ThreadState::list, active.list)
|
||||
#define GSM_POINTER(id, globalList, threadList) \
|
||||
PointerId(id, &GlobalState::globalList, global.globalList, &ThreadState::threadList, \
|
||||
active.threadList)
|
||||
|
||||
rdcarray<PointerId> pointerIDs;
|
||||
|
||||
@@ -1540,8 +1558,10 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s
|
||||
}
|
||||
else if(v.storage == StorageClass::Workgroup)
|
||||
{
|
||||
active.gsmIndexes.push_back({global.workgroups.count(), active.privates.count()});
|
||||
active.privates.push_back(var);
|
||||
global.workgroups.push_back(var);
|
||||
pointerIDs.push_back(GLOBAL_POINTER(v.id, workgroups));
|
||||
pointerIDs.push_back(GSM_POINTER(v.id, workgroups, privates));
|
||||
}
|
||||
|
||||
liveGlobals.push_back(v.id);
|
||||
@@ -1572,9 +1592,10 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s
|
||||
rdcarray<ThreadIndex> threadIds;
|
||||
for(uint32_t i = 0; i < threadsInWorkgroup; i++)
|
||||
{
|
||||
bool isActiveLane = (i == activeLaneIndex);
|
||||
ThreadState &lane = workgroup[i];
|
||||
lane.workgroupIndex = i;
|
||||
if(i != activeLaneIndex)
|
||||
if(!isActiveLane)
|
||||
{
|
||||
lane.nextInstruction = active.nextInstruction;
|
||||
lane.outputs = active.outputs;
|
||||
@@ -1597,7 +1618,21 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s
|
||||
|
||||
// now that the globals are allocated and their storage won't move, we can take pointers to them
|
||||
for(const PointerId &p : pointerIDs)
|
||||
p.Set(*this, global, lane);
|
||||
p.Set(*this, global, lane, isActiveLane);
|
||||
|
||||
if(isActiveLane)
|
||||
{
|
||||
for(const PointerId &p : pointerIDs)
|
||||
{
|
||||
// GSM pointers have a global and local index
|
||||
// Create a GSM global pointer, used for writing back
|
||||
if((p.globalIndex != UINT_MAX) && (p.threadIndex != UINT_MAX))
|
||||
{
|
||||
RDCASSERTEQUAL(lane.gsmPointers.count(p.id), 0);
|
||||
lane.gsmPointers[p.id] = MakePointerVariable(p.id, &global.workgroups[p.globalIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only add active lanes to control flow
|
||||
if(!lane.dead)
|
||||
|
||||
Reference in New Issue
Block a user