Vulkan: Add debugging of the command buffer.
Add a debugger context and server to vk::Device, along with a getter for acquiring the debugger context.
Generate a synthetic file containing all the Vulkan commands in the command buffer, and allow the debugger to single line step over these.
All of this is no-op unless ENABLE_VK_DEBUGGER is defined at compile time, and the VK_DEBUGGER_PORT env var is set at run time.
Bug: b/145351270
Change-Id: I8bea398a6c08d4cb23d76172dbca2a08ae109a6d
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/38913
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Vulkan/Debug/Value.hpp b/src/Vulkan/Debug/Value.hpp
index 9e85ff2..132f818 100644
--- a/src/Vulkan/Debug/Value.hpp
+++ b/src/Vulkan/Debug/Value.hpp
@@ -15,14 +15,14 @@
#ifndef VK_DEBUG_VALUE_HPP_
#define VK_DEBUG_VALUE_HPP_
+#include "Type.hpp"
+
#include <memory>
#include <string>
namespace vk {
namespace dbg {
-class Type;
-
// FormatFlags holds settings used to serialize a Value to a string.
struct FormatFlags
{
diff --git a/src/Vulkan/VkCommandBuffer.cpp b/src/Vulkan/VkCommandBuffer.cpp
index 60e968e..186b1ec 100644
--- a/src/Vulkan/VkCommandBuffer.cpp
+++ b/src/Vulkan/VkCommandBuffer.cpp
@@ -25,6 +25,12 @@
#include "VkRenderPass.hpp"
#include "Device/Renderer.hpp"
+#include "./Debug/Context.hpp"
+#include "./Debug/File.hpp"
+#include "./Debug/Thread.hpp"
+
+#include "marl/defer.h"
+
#include <cstring>
class vk::CommandBuffer::Command
@@ -1281,8 +1287,9 @@
namespace vk {
-CommandBuffer::CommandBuffer(VkCommandBufferLevel pLevel)
- : level(pLevel)
+CommandBuffer::CommandBuffer(Device *device, VkCommandBufferLevel pLevel)
+ : device(device)
+ , level(pLevel)
{
// FIXME (b/119409619): replace this vector by an allocator so we can control all memory allocations
commands = new std::vector<std::unique_ptr<Command>>();
@@ -1329,6 +1336,19 @@
state = EXECUTABLE;
+#ifdef ENABLE_VK_DEBUGGER
+ auto debuggerContext = device->getDebuggerContext();
+ if(debuggerContext)
+ {
+ std::string source;
+ for(auto &command : *commands)
+ {
+ source += command->description() + "\n";
+ }
+ debuggerFile = debuggerContext->lock().createVirtualFile("VkCommandBuffer", source.c_str());
+ }
+#endif // ENABLE_VK_DEBUGGER
+
return VK_SUCCESS;
}
@@ -1745,8 +1765,30 @@
// Perform recorded work
state = PENDING;
+#ifdef ENABLE_VK_DEBUGGER
+ std::shared_ptr<vk::dbg::Thread> debuggerThread;
+ auto debuggerContext = device->getDebuggerContext();
+ if(debuggerContext)
+ {
+ auto lock = debuggerContext->lock();
+ debuggerThread = lock.currentThread();
+ debuggerThread->setName("vkQueue processor");
+ debuggerThread->enter(lock, debuggerFile, "vkCommandBuffer::submit");
+ lock.unlock();
+ }
+ defer(if(debuggerThread) { debuggerThread->exit(); });
+ int line = 1;
+#endif // ENABLE_VK_DEBUGGER
+
for(auto &command : *commands)
{
+#ifdef ENABLE_VK_DEBUGGER
+ if(debuggerThread)
+ {
+ debuggerThread->update({ line++, debuggerFile });
+ }
+#endif // ENABLE_VK_DEBUGGER
+
command->play(executionState);
}
diff --git a/src/Vulkan/VkCommandBuffer.hpp b/src/Vulkan/VkCommandBuffer.hpp
index 497e8d1..0e03fc7 100644
--- a/src/Vulkan/VkCommandBuffer.hpp
+++ b/src/Vulkan/VkCommandBuffer.hpp
@@ -20,6 +20,7 @@
#include "VkObject.hpp"
#include "Device/Color.hpp"
#include "Device/Context.hpp"
+
#include <memory>
#include <vector>
@@ -33,6 +34,11 @@
namespace vk {
+namespace dbg {
+class File;
+} // namespace dbg
+
+class Device;
class Buffer;
class Event;
class Framebuffer;
@@ -47,7 +53,7 @@
public:
static constexpr VkSystemAllocationScope GetAllocationScope() { return VK_SYSTEM_ALLOCATION_SCOPE_OBJECT; }
- CommandBuffer(VkCommandBufferLevel pLevel);
+ CommandBuffer(Device *device, VkCommandBufferLevel pLevel);
static inline CommandBuffer *Cast(VkCommandBuffer object)
{
@@ -201,11 +207,17 @@
PENDING,
INVALID
};
+
+ Device *const device;
State state = INITIAL;
VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
// FIXME (b/119409619): replace this vector by an allocator so we can control all memory allocations
std::vector<std::unique_ptr<Command>> *commands;
+
+#ifdef ENABLE_VK_DEBUGGER
+ std::shared_ptr<vk::dbg::File> debuggerFile;
+#endif // ENABLE_VK_DEBUGGER
};
using DispatchableCommandBuffer = DispatchableObject<CommandBuffer, VkCommandBuffer>;
diff --git a/src/Vulkan/VkCommandPool.cpp b/src/Vulkan/VkCommandPool.cpp
index 35ec438..fe467e0 100644
--- a/src/Vulkan/VkCommandPool.cpp
+++ b/src/Vulkan/VkCommandPool.cpp
@@ -46,7 +46,7 @@
return 0;
}
-VkResult CommandPool::allocateCommandBuffers(VkCommandBufferLevel level, uint32_t commandBufferCount, VkCommandBuffer *pCommandBuffers)
+VkResult CommandPool::allocateCommandBuffers(Device *device, VkCommandBufferLevel level, uint32_t commandBufferCount, VkCommandBuffer *pCommandBuffers)
{
for(uint32_t i = 0; i < commandBufferCount; i++)
{
@@ -54,7 +54,7 @@
void *deviceMemory = vk::allocate(sizeof(DispatchableCommandBuffer), REQUIRED_MEMORY_ALIGNMENT,
DEVICE_MEMORY, DispatchableCommandBuffer::GetAllocationScope());
ASSERT(deviceMemory);
- DispatchableCommandBuffer *commandBuffer = new(deviceMemory) DispatchableCommandBuffer(level);
+ DispatchableCommandBuffer *commandBuffer = new(deviceMemory) DispatchableCommandBuffer(device, level);
if(commandBuffer)
{
pCommandBuffers[i] = *commandBuffer;
diff --git a/src/Vulkan/VkCommandPool.hpp b/src/Vulkan/VkCommandPool.hpp
index f4f99c2..0aec423 100644
--- a/src/Vulkan/VkCommandPool.hpp
+++ b/src/Vulkan/VkCommandPool.hpp
@@ -16,10 +16,13 @@
#define VK_COMMAND_POOL_HPP_
#include "VkObject.hpp"
+
#include <set>
namespace vk {
+class Device;
+
class CommandPool : public Object<CommandPool, VkCommandPool>
{
public:
@@ -28,7 +31,7 @@
static size_t ComputeRequiredAllocationSize(const VkCommandPoolCreateInfo *pCreateInfo);
- VkResult allocateCommandBuffers(VkCommandBufferLevel level, uint32_t commandBufferCount, VkCommandBuffer *pCommandBuffers);
+ VkResult allocateCommandBuffers(Device *device, VkCommandBufferLevel level, uint32_t commandBufferCount, VkCommandBuffer *pCommandBuffers);
void freeCommandBuffers(uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers);
VkResult reset(VkCommandPoolResetFlags flags);
void trim(VkCommandPoolTrimFlags flags);
diff --git a/src/Vulkan/VkDevice.cpp b/src/Vulkan/VkDevice.cpp
index cbc399c..8a92ac3 100644
--- a/src/Vulkan/VkDevice.cpp
+++ b/src/Vulkan/VkDevice.cpp
@@ -19,6 +19,8 @@
#include "VkDescriptorSetLayout.hpp"
#include "VkFence.hpp"
#include "VkQueue.hpp"
+#include "Debug/Context.hpp"
+#include "Debug/Server.hpp"
#include "Device/Blitter.hpp"
#include <chrono>
@@ -97,6 +99,18 @@
// FIXME (b/119409619): use an allocator here so we can control all memory allocations
blitter.reset(new sw::Blitter());
samplingRoutineCache.reset(new SamplingRoutineCache());
+
+#ifdef ENABLE_VK_DEBUGGER
+ static auto port = getenv("VK_DEBUGGER_PORT");
+ if(port)
+ {
+ // Construct the debugger context and server - this may block for a
+ // debugger connection, allowing breakpoints to be set before they're
+ // executed.
+ debugger.context = vk::dbg::Context::create();
+ debugger.server = vk::dbg::Server::create(debugger.context, atoi(port));
+ }
+#endif // ENABLE_VK_DEBUGGER
}
void Device::destroy(const VkAllocationCallbacks *pAllocator)
diff --git a/src/Vulkan/VkDevice.hpp b/src/Vulkan/VkDevice.hpp
index 94fbba6..d83e8e8 100644
--- a/src/Vulkan/VkDevice.hpp
+++ b/src/Vulkan/VkDevice.hpp
@@ -33,6 +33,11 @@
class PhysicalDevice;
class Queue;
+namespace dbg {
+class Context;
+class Server;
+} // namespace dbg
+
class Device
{
public:
@@ -93,6 +98,13 @@
rr::Routine *findInConstCache(const SamplingRoutineCache::Key &key) const;
void updateSamplingRoutineConstCache();
+#ifdef ENABLE_VK_DEBUGGER
+ std::shared_ptr<vk::dbg::Context> getDebuggerContext() const
+ {
+ return debugger.context;
+ }
+#endif // ENABLE_VK_DEBUGGER
+
private:
PhysicalDevice *const physicalDevice = nullptr;
Queue *const queues = nullptr;
@@ -105,6 +117,14 @@
ExtensionName *extensions = nullptr;
const VkPhysicalDeviceFeatures enabledFeatures = {};
std::shared_ptr<marl::Scheduler> scheduler;
+
+#ifdef ENABLE_VK_DEBUGGER
+ struct
+ {
+ std::shared_ptr<vk::dbg::Context> context;
+ std::shared_ptr<vk::dbg::Server> server;
+ } debugger;
+#endif // ENABLE_VK_DEBUGGER
};
using DispatchableDevice = DispatchableObject<Device, VkDevice>;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 8c5484d..6af8bd9 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -1964,7 +1964,7 @@
nextInfo = nextInfo->pNext;
}
- return vk::Cast(pAllocateInfo->commandPool)->allocateCommandBuffers(pAllocateInfo->level, pAllocateInfo->commandBufferCount, pCommandBuffers);
+ return vk::Cast(pAllocateInfo->commandPool)->allocateCommandBuffers(vk::Cast(device), pAllocateInfo->level, pAllocateInfo->commandBufferCount, pCommandBuffers);
}
VKAPI_ATTR void VKAPI_CALL vkFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers)