Add skeleton of SPIR-V shader debugging class

This commit is contained in:
baldurk
2020-02-12 14:43:22 +00:00
parent c23825292d
commit 5ef3a25f7f
11 changed files with 405 additions and 28 deletions
@@ -98,6 +98,8 @@ set(sources
spirv_op_helpers.h
spirv_compile.cpp
spirv_compile.h
spirv_debug_setup.cpp
spirv_debug.h
spirv_reflect.cpp
spirv_reflect.h
spirv_processor.cpp
@@ -156,6 +156,7 @@
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
<ForcedIncludeFiles>precompiled.h</ForcedIncludeFiles>
</ClCompile>
<ClCompile Include="spirv_debug_setup.cpp" />
<ClCompile Include="spirv_disassemble.cpp">
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
@@ -243,6 +244,7 @@
<ClInclude Include="precompiled.h" />
<ClInclude Include="spirv_common.h" />
<ClInclude Include="spirv_compile.h" />
<ClInclude Include="spirv_debug.h" />
<ClInclude Include="spirv_editor.h" />
<ClInclude Include="spirv_gen.h" />
<ClInclude Include="spirv_op_helpers.h" />
@@ -143,6 +143,7 @@
<ClCompile Include="spirv_reflect.cpp" />
<ClCompile Include="glslang_compile.cpp" />
<ClCompile Include="spirv_processor.cpp" />
<ClCompile Include="spirv_debug_setup.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\3rdparty\glslang\OGLCompilersDLL\InitializeDll.h">
@@ -295,5 +296,6 @@
<Filter>JSON-Generated helpers</Filter>
</ClInclude>
<ClInclude Include="spirv_processor.h" />
<ClInclude Include="spirv_debug.h" />
</ItemGroup>
</Project>
@@ -0,0 +1,114 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#pragma once
#include "api/replay/rdcarray.h"
#include "spirv_common.h"
#include "spirv_processor.h"
namespace rdcspv
{
struct GlobalState
{
public:
GlobalState() {}
// allocated storage for opaque uniform blocks, does not change over the course of debugging
rdcarray<ShaderVariable> constantBlocks;
};
class ThreadState
{
public:
ThreadState(int workgroupIdx, GlobalState &globalState);
bool Finished() const { return done; }
uint32_t nextInstruction;
GlobalState &global;
// thread-local inputs/outputs. This array does not change over the course of debugging
rdcarray<ShaderVariable> inputs, outputs;
// every ID's variable, if a pointer it may be pointing at a ShaderVariable stored elsewhere
DenseIdMap<ShaderVariable> ids;
// the list of IDs that are currently valid and live
rdcarray<Id> live;
private:
// index in the pixel quad
int workgroupIndex;
bool done;
};
class Debugger : public Processor, public ShaderDebugger
{
public:
Debugger();
~Debugger();
virtual void Parse(const rdcarray<uint32_t> &spirvWords);
ShaderDebugTrace *BeginDebug(const ShaderStage stage, const rdcstr &entryPoint,
const rdcarray<SpecConstant> &specInfo,
const std::map<size_t, uint32_t> &instructionLines,
uint32_t activeIndex);
rdcarray<ShaderDebugState> ContinueDebug();
GlobalState GetGlobal() { return global; }
ThreadState &GetActiveLane() { return workgroup[activeLaneIndex]; }
private:
virtual void PreParse(uint32_t maxId);
virtual void PostParse();
virtual void RegisterOp(Iter it);
GlobalState global;
rdcarray<ThreadState> workgroup;
rdcarray<SourceVariableMapping> sourceVars;
rdcarray<size_t> instructionOffsets;
uint32_t activeLaneIndex = 0;
int steps = 0;
DenseIdMap<rdcstr> strings;
struct MemberName
{
Id id;
uint32_t member;
rdcstr name;
};
rdcarray<MemberName> memberNames;
rdcstr GetRawName(Id id);
rdcstr GetHumanName(Id id);
std::set<rdcstr> usedNames;
std::map<Id, rdcstr> dynamicNames;
};
}; // namespace rdcspv
@@ -0,0 +1,196 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "spirv_debug.h"
#include "common/formatting.h"
#include "spirv_op_helpers.h"
namespace rdcspv
{
ThreadState::ThreadState(int workgroupIdx, GlobalState &globalState) : global(globalState)
{
workgroupIndex = workgroupIdx;
nextInstruction = 0;
done = false;
}
Debugger::Debugger()
{
}
Debugger::~Debugger()
{
}
void Debugger::Parse(const rdcarray<uint32_t> &spirvWords)
{
Processor::Parse(spirvWords);
}
ShaderDebugTrace *Debugger::BeginDebug(const ShaderStage stage, const rdcstr &entryPoint,
const rdcarray<SpecConstant> &specInfo,
const std::map<size_t, uint32_t> &instructionLines,
uint32_t activeIndex)
{
ShaderDebugTrace *ret = new ShaderDebugTrace;
ret->debugger = this;
this->activeLaneIndex = activeIndex;
int workgroupSize = stage == ShaderStage::Pixel ? 4 : 1;
for(int i = 0; i < workgroupSize; i++)
workgroup.push_back(ThreadState(i, global));
ThreadState &active = GetActiveLane();
active.ids.resize(idOffsets.size());
// evaluate all constants
for(auto it = constants.begin(); it != constants.end(); it++)
active.ids[it->first] = EvaluateConstant(it->first, specInfo);
ret->lineInfo.resize(instructionOffsets.size());
for(size_t i = 0; i < instructionOffsets.size(); i++)
{
auto it = instructionLines.find(instructionOffsets[i]);
if(it != instructionLines.end())
ret->lineInfo[i].disassemblyLine = it->second;
else
ret->lineInfo[i].disassemblyLine = 0;
}
ret->constantBlocks = global.constantBlocks;
ret->inputs = active.inputs;
return ret;
}
rdcarray<ShaderDebugState> Debugger::ContinueDebug()
{
ThreadState &active = GetActiveLane();
rdcarray<ShaderDebugState> ret;
// if we've finished, return an empty set to signify that
if(active.Finished())
return ret;
// initialise a blank set of shader variable changes in the first ShaderDebugState
if(steps == 0)
{
ShaderDebugState initial;
for(const Id &v : active.live)
initial.changes.push_back({ShaderVariable(), active.ids[v]});
initial.sourceVars = sourceVars;
ret.push_back(initial);
steps++;
}
return ret;
}
rdcstr Debugger::GetRawName(Id id)
{
return StringFormat::Fmt("_%u", id.value());
}
rdcstr Debugger::GetHumanName(Id id)
{
// see if we have a dynamic name assigned (to disambiguate), if so use that
auto it = dynamicNames.find(id);
if(it != dynamicNames.end())
return it->second;
// otherwise try the string first
rdcstr name = strings[id];
// if we don't have a string name, we can be sure the id is unambiguous
if(name.empty())
return GetRawName(id);
rdcstr basename = name;
// otherwise check to see if it's been used before. If so give it a new name
int alias = 2;
while(usedNames.find(name) != usedNames.end())
{
name = basename + "@" + ToStr(alias);
alias++;
}
usedNames.insert(name);
dynamicNames[id] = name;
return name;
}
void Debugger::PreParse(uint32_t maxId)
{
Processor::PreParse(maxId);
strings.resize(idTypes.size());
}
void Debugger::PostParse()
{
Processor::PostParse();
for(const MemberName &mem : memberNames)
dataTypes[mem.id].children[mem.member].name = mem.name;
memberNames.clear();
}
void Debugger::RegisterOp(Iter it)
{
Processor::RegisterOp(it);
OpDecoder opdata(it);
if(opdata.op == Op::String)
{
OpString string(it);
strings[string.result] = string.string;
}
else if(opdata.op == Op::Name)
{
OpName name(it);
// technically you could name a string - in that case we ignore the name
if(strings[name.target].empty())
strings[name.target] = name.name;
}
else if(opdata.op == Op::MemberName)
{
OpMemberName memberName(it);
memberNames.push_back({memberName.type, memberName.member, memberName.name});
}
}
}; // namespace rdcspv
@@ -166,7 +166,7 @@ rdcstr Reflector::Disassemble(const rdcstr &entryPoint,
int alias = 2;
while(usedNames.find(name) != usedNames.end())
{
name = basename + "_" + ToStr(alias);
name = basename + "@" + ToStr(alias);
alias++;
}
+1
View File
@@ -35,6 +35,7 @@ set(sources
vk_replay.h
vk_resources.cpp
vk_resources.h
vk_shaderdebug.cpp
vk_state.cpp
vk_state.h
vk_serialise.cpp
@@ -119,6 +119,7 @@
<ClCompile Include="vk_rendertext.cpp" />
<ClCompile Include="vk_rendertexture.cpp" />
<ClCompile Include="vk_serialise.cpp" />
<ClCompile Include="vk_shaderdebug.cpp" />
<ClCompile Include="vk_shader_cache.cpp" />
<ClCompile Include="vk_sparse_initstate.cpp" />
<ClCompile Include="vk_stringise.cpp" />
@@ -151,6 +151,7 @@
<ClCompile Include="vk_image_states.cpp">
<Filter>Util</Filter>
</ClCompile>
<ClCompile Include="vk_shaderdebug.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="vk_replay.h">
-27
View File
@@ -3870,33 +3870,6 @@ void VulkanReplay::RefreshDerivedReplacements()
m_pDriver->vkDestroyPipeline(dev, pipe, NULL);
}
ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid,
uint32_t idx)
{
VULKANNOTIMP("DebugVertex");
return new ShaderDebugTrace();
}
ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y,
uint32_t sample, uint32_t primitive)
{
VULKANNOTIMP("DebugPixel");
return new ShaderDebugTrace();
}
ShaderDebugTrace *VulkanReplay::DebugThread(uint32_t eventId, const uint32_t groupid[3],
const uint32_t threadid[3])
{
VULKANNOTIMP("DebugThread");
return new ShaderDebugTrace();
}
rdcarray<ShaderDebugState> VulkanReplay::ContinueDebug(ShaderDebugger *debugger)
{
VULKANNOTIMP("ContinueDebug");
return {};
}
ResourceId VulkanReplay::CreateProxyTexture(const TextureDescription &templateTex)
{
VULKANNOTIMP("CreateProxyTexture");
@@ -0,0 +1,85 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "driver/shaders/spirv/spirv_debug.h"
#include "vk_core.h"
#include "vk_replay.h"
ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid,
uint32_t idx)
{
const VulkanRenderState &state = m_pDriver->m_RenderState;
VulkanCreationInfo &c = m_pDriver->m_CreationInfo;
VkMarkerRegion region(
StringFormat::Fmt("DebugVertex @ %u of (%u,%u,%u)", eventId, vertid, instid, idx));
const DrawcallDescription *draw = m_pDriver->GetDrawcall(eventId);
if(!(draw->flags & DrawFlags::Drawcall))
return new ShaderDebugTrace();
const VulkanCreationInfo::Pipeline &pipe = c.m_Pipeline[state.graphics.pipeline];
VulkanCreationInfo::ShaderModule &shader = c.m_ShaderModule[pipe.shaders[0].module];
rdcstr entryPoint = pipe.shaders[0].entryPoint;
const rdcarray<SpecConstant> &spec = pipe.shaders[0].specialization;
VulkanCreationInfo::ShaderModuleReflection &shadRefl =
shader.GetReflection(entryPoint, state.graphics.pipeline);
shadRefl.PopulateDisassembly(shader.spirv);
rdcspv::Debugger *debugger = new rdcspv::Debugger;
debugger->Parse(shader.spirv.GetSPIRV());
ShaderDebugTrace *ret =
debugger->BeginDebug(ShaderStage::Vertex, entryPoint, spec, shadRefl.instructionLines, 0);
return ret;
}
ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y,
uint32_t sample, uint32_t primitive)
{
VULKANNOTIMP("DebugPixel");
return new ShaderDebugTrace();
}
ShaderDebugTrace *VulkanReplay::DebugThread(uint32_t eventId, const uint32_t groupid[3],
const uint32_t threadid[3])
{
VULKANNOTIMP("DebugThread");
return new ShaderDebugTrace();
}
rdcarray<ShaderDebugState> VulkanReplay::ContinueDebug(ShaderDebugger *debugger)
{
rdcspv::Debugger *spvDebugger = (rdcspv::Debugger *)debugger;
if(!spvDebugger)
return {};
VkMarkerRegion region("ContinueDebug Simulation Loop");
return spvDebugger->ContinueDebug();
}