Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions layersvt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,18 +169,15 @@ if(BUILD_DEBUGMARKER)
add_library(VkLayer_DebugMarker MODULE)
set_target_properties(VkLayer_DebugMarker PROPERTIES FOLDER "layers/debugmarker")
target_sources(VkLayer_DebugMarker PRIVATE
debug_marker/debug_marker_handwritten_dispatch.cpp
debug_marker/debug_marker_handwritten_functions.h
$<TARGET_OBJECTS:layersvt_entrypoints>
debug_marker/debug_marker_handwritten_functions_vk_ext_debug_marker.h
debug_marker/debug_marker_handwritten_functions_vk_ext_debug_utils.h
debug_marker/debug_marker.h
debug_marker/debug_marker_instance.cpp
debug_marker/debug_marker.cpp
debug_marker/debug_marker_perfetto.h
debug_marker/debug_marker_perfetto.cpp
perfetto/perfetto.cc
vk_layer_table.cpp
vk_layer_table.h
common/layer_keep_alive.cpp
debug_marker/VkLayer_DebugMarker.json.in
)

Expand All @@ -190,6 +187,8 @@ if(BUILD_DEBUGMARKER)
${CMAKE_CURRENT_BINARY_DIR}
)

target_link_libraries(VkLayer_DebugMarker PRIVATE layersvt_common)

if(CMAKE_SYSTEM_NAME MATCHES "Linux|BSD|DragonFly|GNU")
if (BUILD_WSI_XCB_SUPPORT)
target_compile_definitions(VkLayer_DebugMarker PRIVATE VK_USE_PLATFORM_XLIB_KHR)
Expand Down
2 changes: 2 additions & 0 deletions layersvt/debug_marker/VkLayer_DebugMarker.def
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ vkGetInstanceProcAddr
vkGetDeviceProcAddr
vkEnumerateInstanceLayerProperties
vkEnumerateInstanceExtensionProperties
vkEnumerateDeviceLayerProperties
vkEnumerateDeviceExtensionProperties
143 changes: 128 additions & 15 deletions layersvt/debug_marker/debug_marker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,62 @@

#include "debug_marker.h"
#include "debug_marker_perfetto.h"
#include "debug_marker_handwritten_functions_vk_ext_debug_marker.h"
#include "debug_marker_handwritten_functions_vk_ext_debug_utils.h"
#include "perfetto/perfetto.h"
#include <cstring>

DebugMarker& DebugMarker::Get() {
static DebugMarker instance;
return instance;
DebugMarker::DebugMarker() = default;

const layersvt::LayerManifest* DebugMarker::GetLayerManifest() const {
static const layersvt::LayerManifest manifest{
.layer_name = "VK_LAYER_GOOGLE_DebugMarker",
.description = "layer: DebugMarker",
.spec_version = VK_MAKE_VERSION(1, 4, VK_HEADER_VERSION),
.implementation_version = VK_MAKE_VERSION(0, 1, 0),
.instance_extensions =
{
{VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_UTILS_SPEC_VERSION},
},
.device_extensions =
{
{VK_EXT_DEBUG_MARKER_EXTENSION_NAME, VK_EXT_DEBUG_MARKER_SPEC_VERSION},
},
.tool_properties = std::nullopt,
};
return &manifest;
}

void DebugMarker::PreCreateInstance(VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator) {
(void)pCreateInfo;
(void)pAllocator;
static std::once_flag perfetto_initialization_flag;
std::call_once(perfetto_initialization_flag, []() { InitializeDebugMarkerPerfetto(); });
}

void DebugMarker::SetVkInstance(VkPhysicalDevice phys_dev, VkInstance instance) {
void DebugMarker::PreDestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) {
(void)pAllocator;
std::lock_guard<std::mutex> lock(mutex_);
vk_instance_map_[phys_dev] = instance;
for (auto it = emulated_messengers_.begin(); it != emulated_messengers_.end();) {
if (it->second.instance == instance) {
it = emulated_messengers_.erase(it);
} else {
++it;
}
}
}

VkInstance DebugMarker::GetVkInstance(VkPhysicalDevice phys_dev) {
void DebugMarker::PreDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
(void)pAllocator;
std::lock_guard<std::mutex> lock(mutex_);
auto it = vk_instance_map_.find(phys_dev);
if (it != vk_instance_map_.end()) return it->second;
return VK_NULL_HANDLE;
uint64_t dev_handle = (uint64_t)device;
for (auto it = debug_object_names_.begin(); it != debug_object_names_.end();) {
if (it->second.vk_device == dev_handle) {
it = debug_object_names_.erase(it);
} else {
++it;
}
}
}

void DebugMarker::SetDebugObjectName(uint64_t device, int32_t type, uint64_t handle, const char* name) {
Expand Down Expand Up @@ -72,15 +111,89 @@ void DebugMarker::EmitAllDebugMarkers() {
}
}

void DebugMarker::Clear() {
std::lock_guard<std::mutex> lock(mutex_);
vk_instance_map_.clear();
debug_object_names_.clear();
}

bool DebugMarker::HasDebugObjectName(int32_t type, uint64_t handle, const std::string& name) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = debug_object_names_.find(std::make_pair(type, handle));
if (it == debug_object_names_.end()) return false;
return it->second.name == name;
}

VkResult DebugMarker::CreateEmulatedMessenger(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
VkDebugUtilsMessengerEXT* pMessenger) {
std::lock_guard<std::mutex> lock(mutex_);
uint64_t handle = next_emulated_messenger_++;
EmulatedMessenger& messenger = emulated_messengers_[handle];
messenger.instance = instance;
messenger.create_info = *pCreateInfo;
// The pNext chain is owned by the application and is not used by the emulation.
messenger.create_info.pNext = nullptr;
*pMessenger = (VkDebugUtilsMessengerEXT)handle;
return VK_SUCCESS;
}

void DebugMarker::DestroyEmulatedMessenger(VkDebugUtilsMessengerEXT messenger) {
std::lock_guard<std::mutex> lock(mutex_);
emulated_messengers_.erase((uint64_t)messenger);
}

void DebugMarker::SubmitEmulatedMessage(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) {
std::vector<VkDebugUtilsMessengerCreateInfoEXT> matching;
{
std::lock_guard<std::mutex> lock(mutex_);
for (const auto& entry : emulated_messengers_) {
const VkDebugUtilsMessengerCreateInfoEXT& info = entry.second.create_info;
if (entry.second.instance == instance && (info.messageSeverity & messageSeverity) != 0 &&
(info.messageType & messageTypes) != 0) {
matching.push_back(info);
}
}
}
// Invoke callbacks without holding the lock so that they may call back into the layer.
for (const auto& info : matching) {
info.pfnUserCallback(messageSeverity, messageTypes, pCallbackData, info.pUserData);
}
}

PFN_vkVoidFunction DebugMarker::GetLayerInstanceCommand(VkInstance instance, const char* name) {
Comment thread
olehkuznetsov marked this conversation as resolved.
(void)instance;
if (strcmp(name, "vkCreateDebugUtilsMessengerEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkCreateDebugUtilsMessengerEXT);
if (strcmp(name, "vkDestroyDebugUtilsMessengerEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkDestroyDebugUtilsMessengerEXT);
if (strcmp(name, "vkSubmitDebugUtilsMessageEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkSubmitDebugUtilsMessageEXT);
return nullptr;
}

PFN_vkVoidFunction DebugMarker::GetLayerDeviceCommand(VkDevice device, const char* name) {
(void)device;

// VK_EXT_debug_marker
if (strcmp(name, "vkCmdDebugMarkerBeginEXT") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vkCmdDebugMarkerBeginEXT);
if (strcmp(name, "vkCmdDebugMarkerEndEXT") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vkCmdDebugMarkerEndEXT);
if (strcmp(name, "vkCmdDebugMarkerInsertEXT") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vkCmdDebugMarkerInsertEXT);
if (strcmp(name, "vkDebugMarkerSetObjectNameEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkDebugMarkerSetObjectNameEXT);
if (strcmp(name, "vkDebugMarkerSetObjectTagEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkDebugMarkerSetObjectTagEXT);

// VK_EXT_debug_utils
if (strcmp(name, "vkCmdBeginDebugUtilsLabelEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkCmdBeginDebugUtilsLabelEXT);
if (strcmp(name, "vkCmdEndDebugUtilsLabelEXT") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vkCmdEndDebugUtilsLabelEXT);
if (strcmp(name, "vkCmdInsertDebugUtilsLabelEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkCmdInsertDebugUtilsLabelEXT);
if (strcmp(name, "vkSetDebugUtilsObjectNameEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkSetDebugUtilsObjectNameEXT);
if (strcmp(name, "vkSetDebugUtilsObjectTagEXT") == 0) return reinterpret_cast<PFN_vkVoidFunction>(vkSetDebugUtilsObjectTagEXT);
if (strcmp(name, "vkQueueBeginDebugUtilsLabelEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkQueueBeginDebugUtilsLabelEXT);
if (strcmp(name, "vkQueueEndDebugUtilsLabelEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkQueueEndDebugUtilsLabelEXT);
if (strcmp(name, "vkQueueInsertDebugUtilsLabelEXT") == 0)
return reinterpret_cast<PFN_vkVoidFunction>(vkQueueInsertDebugUtilsLabelEXT);

return nullptr;
}
84 changes: 53 additions & 31 deletions layersvt/debug_marker/debug_marker.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
#pragma once

#include <vulkan/vulkan.h>
#include <cassert>
#include <mutex>
#include <unordered_map>

#include <map>
#include <string>
#include <vector>

#include "common/layer_base.h"
#include "common/layer_manifest.h"

/**
* The DebugMarker class is responsible for storing and managing debug marker
Expand All @@ -45,19 +49,17 @@
* because a user might start another Perfetto session later, requiring us to emit
* all object names again.
*
* A potential issue exists if an application constantly creates and destroys
* objects without bound, as we currently do not remove names for destroyed objects.
* Support for removing names on object destruction can be added later if needed.
*
* This class is a singleton and provides thread-safe access to its state.
* This class is a singleton, inherits from LayerBase, and provides thread-safe access to its state.
*/
class DebugMarker {
class DebugMarker : public layersvt::LayerBase {
public:
/**
* @brief Returns the singleton instance of the DebugMarker class.
* @return Reference to the DebugMarker singleton.
*/
static DebugMarker& Get();
DebugMarker();
~DebugMarker() override = default;

static DebugMarker& Get() {
assert(LayerBase::Get() != nullptr && "LayerBase instance must be initialized");
return *static_cast<DebugMarker*>(LayerBase::Get());
}

/**
* @brief Sets or updates the name associated with a Vulkan object.
Expand All @@ -67,38 +69,50 @@ class DebugMarker {
* @param name The name to associate with the object.
*/
void SetDebugObjectName(uint64_t device, int32_t type, uint64_t handle, const char* name);

/**
* @brief Emits all stored debug markers to the tracing system.
*/
void EmitAllDebugMarkers();

/**
* @brief Clears all stored debug markers and instance mappings.
* @note This function is for testing only.
*/
void Clear();

/**
* @brief Checks if a debug name is stored for a given object.
* @note This function is for testing only.
*/
bool HasDebugObjectName(int32_t type, uint64_t handle, const std::string& name);

/**
* @brief Associates a Vulkan physical device with its corresponding instance.
* @param phys_dev The Vulkan physical device.
* @param instance The Vulkan instance.
* @brief Emulated VK_EXT_debug_utils messengers, used for instances whose downstream lacks the
* extension so that the commands advertised by this layer remain functional.
*/
VkResult CreateEmulatedMessenger(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
VkDebugUtilsMessengerEXT* pMessenger);
void DestroyEmulatedMessenger(VkDebugUtilsMessengerEXT messenger);
void SubmitEmulatedMessage(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData);

protected:
/**
* Lifecycle hook called before vkCreateInstance.
*/
void PreCreateInstance(VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator) override;

/**
* Lifecycle hook called before vkDestroyInstance to remove the instance's emulated messengers.
*/
void SetVkInstance(VkPhysicalDevice phys_dev, VkInstance instance);
void PreDestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) override;

/**
* @brief Retrieves the Vulkan instance associated with a given physical device.
* @param phys_dev The Vulkan physical device.
* @return The associated Vulkan instance.
* Lifecycle hook called before vkDestroyDevice to remove tracked names for destroyed objects.
*/
VkInstance GetVkInstance(VkPhysicalDevice phys_dev);
void PreDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) override;

const layersvt::LayerManifest* GetLayerManifest() const override;
// VK_EXT_debug_marker and VK_EXT_debug_utils are implemented by this layer, so their commands
// are returned unconditionally; the hooks tolerate a downstream that lacks the extension.
PFN_vkVoidFunction GetLayerInstanceCommand(VkInstance instance, const char* name) override;
PFN_vkVoidFunction GetLayerDeviceCommand(VkDevice device, const char* name) override;

private:
struct DebugObjectName {
Expand All @@ -113,13 +127,21 @@ class DebugMarker {
};

std::mutex mutex_;
/**
* @brief Maps a physical device handle to its corresponding Vulkan instance handle.
*/
std::unordered_map<VkPhysicalDevice, VkInstance> vk_instance_map_;
/**
* @brief Maps a pair of (object_type, object_handle) to its debug name information.
* We use a pair as the key because handles are not guaranteed to be unique across different object types.
*/
std::map<std::pair<int32_t, uint64_t>, DebugObjectName> debug_object_names_;

struct EmulatedMessenger {
VkInstance instance;
VkDebugUtilsMessengerCreateInfoEXT create_info;
};

/**
* @brief Maps an emulated messenger handle to its owning instance and creation parameters.
* Handles are unique non-zero counter values.
*/
std::map<uint64_t, EmulatedMessenger> emulated_messengers_;
uint64_t next_emulated_messenger_ = 1;
};
Loading
Loading