mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 10:00:40 +00:00
Add skeleton of SPIR-V shader debugging class
This commit is contained in:
@@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user