diff --git a/renderdoc/driver/shaders/spirv/CMakeLists.txt b/renderdoc/driver/shaders/spirv/CMakeLists.txt
index 79ce54429..58a2e237f 100644
--- a/renderdoc/driver/shaders/spirv/CMakeLists.txt
+++ b/renderdoc/driver/shaders/spirv/CMakeLists.txt
@@ -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
diff --git a/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj b/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj
index bdcce8b6e..3c53f1c0a 100644
--- a/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj
+++ b/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj
@@ -156,6 +156,7 @@
precompiled.h
precompiled.h
+
Level4
Use
@@ -243,6 +244,7 @@
+
diff --git a/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj.filters b/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj.filters
index c88771a72..d4fa2546a 100644
--- a/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj.filters
+++ b/renderdoc/driver/shaders/spirv/renderdoc_spirv.vcxproj.filters
@@ -143,6 +143,7 @@
+
@@ -295,5 +296,6 @@
JSON-Generated helpers
+
\ No newline at end of file
diff --git a/renderdoc/driver/shaders/spirv/spirv_debug.h b/renderdoc/driver/shaders/spirv/spirv_debug.h
new file mode 100644
index 000000000..8214ce1af
--- /dev/null
+++ b/renderdoc/driver/shaders/spirv/spirv_debug.h
@@ -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 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 inputs, outputs;
+
+ // every ID's variable, if a pointer it may be pointing at a ShaderVariable stored elsewhere
+ DenseIdMap ids;
+
+ // the list of IDs that are currently valid and live
+ rdcarray 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 &spirvWords);
+ ShaderDebugTrace *BeginDebug(const ShaderStage stage, const rdcstr &entryPoint,
+ const rdcarray &specInfo,
+ const std::map &instructionLines,
+ uint32_t activeIndex);
+
+ rdcarray 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 workgroup;
+
+ rdcarray sourceVars;
+ rdcarray instructionOffsets;
+
+ uint32_t activeLaneIndex = 0;
+
+ int steps = 0;
+
+ DenseIdMap strings;
+
+ struct MemberName
+ {
+ Id id;
+ uint32_t member;
+ rdcstr name;
+ };
+
+ rdcarray memberNames;
+
+ rdcstr GetRawName(Id id);
+ rdcstr GetHumanName(Id id);
+
+ std::set usedNames;
+ std::map dynamicNames;
+};
+
+}; // namespace rdcspv
diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp
new file mode 100644
index 000000000..b4618760a
--- /dev/null
+++ b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp
@@ -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 &spirvWords)
+{
+ Processor::Parse(spirvWords);
+}
+
+ShaderDebugTrace *Debugger::BeginDebug(const ShaderStage stage, const rdcstr &entryPoint,
+ const rdcarray &specInfo,
+ const std::map &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 Debugger::ContinueDebug()
+{
+ ThreadState &active = GetActiveLane();
+
+ rdcarray 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
diff --git a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp
index f84b4e444..c45cf5eaf 100644
--- a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp
+++ b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp
@@ -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++;
}
diff --git a/renderdoc/driver/vulkan/CMakeLists.txt b/renderdoc/driver/vulkan/CMakeLists.txt
index ad11b4292..22a23dbf2 100644
--- a/renderdoc/driver/vulkan/CMakeLists.txt
+++ b/renderdoc/driver/vulkan/CMakeLists.txt
@@ -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
diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj
index f1c0e5ce6..951d22327 100644
--- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj
+++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj
@@ -119,6 +119,7 @@
+
diff --git a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters
index 176b58798..90c84e2fb 100644
--- a/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters
+++ b/renderdoc/driver/vulkan/renderdoc_vulkan.vcxproj.filters
@@ -151,6 +151,7 @@
Util
+
diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp
index e4dd818f4..ef1154764 100644
--- a/renderdoc/driver/vulkan/vk_replay.cpp
+++ b/renderdoc/driver/vulkan/vk_replay.cpp
@@ -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 VulkanReplay::ContinueDebug(ShaderDebugger *debugger)
-{
- VULKANNOTIMP("ContinueDebug");
- return {};
-}
-
ResourceId VulkanReplay::CreateProxyTexture(const TextureDescription &templateTex)
{
VULKANNOTIMP("CreateProxyTexture");
diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp
new file mode 100644
index 000000000..632b1318d
--- /dev/null
+++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp
@@ -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 &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 VulkanReplay::ContinueDebug(ShaderDebugger *debugger)
+{
+ rdcspv::Debugger *spvDebugger = (rdcspv::Debugger *)debugger;
+
+ if(!spvDebugger)
+ return {};
+
+ VkMarkerRegion region("ContinueDebug Simulation Loop");
+
+ return spvDebugger->ContinueDebug();
+}