From a31173e850ad0e0c5704c1070c3f4058c31971b2 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Fri, 10 Apr 2026 14:16:10 +0100 Subject: [PATCH 01/12] Add Debug Marker layer with Perfetto support - Implements VK_LAYER_GOOGLE_DebugMarker to capture object names. - Forwards object names to Perfetto traces via TrackEvent. Perfetto config: data_sources: { config { name: "track_event" track_event_config { enabled_categories: "VulkanDebugMarker" } } } --- CMakeLists.txt | 4 +- layersvt/CMakeLists.txt | 32 +- layersvt/debug_marker.cpp | 72 ++++ layersvt/debug_marker.h | 50 +++ .../debug_marker_handwritten_dispatch.cpp | 99 +++++ layersvt/debug_marker_handwritten_functions.h | 370 ++++++++++++++++++ layersvt/debug_marker_perfetto.cpp | 37 ++ layersvt/debug_marker_perfetto.h | 28 ++ layersvt/json/VkLayer_DebugMarker.json.in | 25 ++ 9 files changed, 715 insertions(+), 2 deletions(-) create mode 100644 layersvt/debug_marker.cpp create mode 100644 layersvt/debug_marker.h create mode 100644 layersvt/debug_marker_handwritten_dispatch.cpp create mode 100644 layersvt/debug_marker_handwritten_functions.h create mode 100644 layersvt/debug_marker_perfetto.cpp create mode 100644 layersvt/debug_marker_perfetto.h create mode 100644 layersvt/json/VkLayer_DebugMarker.json.in diff --git a/CMakeLists.txt b/CMakeLists.txt index df25815da8..5a63074216 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows|Linux|BSD") option(BUILD_SCREENSHOT "Build screenshot layer" ON) if(NOT WIN32) option(BUILD_CPUTIMING "Build CPUTiming layer" ON) + option(BUILD_DEBUGMARKER "Build DebugMarker layer" ON) endif() option(BUILD_LAYERMGR "Build Vulkan Configurator" ON) set(SDK_VERSION "" CACHE STRING "Vulkan SDK version") @@ -106,6 +107,7 @@ elseif(ANDROID) option(BUILD_APIDUMP "Build api_dump layer" ON) option(BUILD_SCREENSHOT "Build screenshot layer" ON) option(BUILD_CPUTIMING "Build CPUTiming layer" ON) + option(BUILD_DEBUGMARKER "Build DebugMarker layer" ON) set(BUILD_MONITOR OFF) set(BUILD_LAYERMGR OFF) @@ -125,7 +127,7 @@ if(BUILD_TESTS) add_subdirectory(tests) endif() -if(BUILD_APIDUMP OR BUILD_MONITOR OR BUILD_SCREENSHOT OR BUILD_CPUTIMING) +if(BUILD_APIDUMP OR BUILD_MONITOR OR BUILD_SCREENSHOT OR BUILD_CPUTIMING OR BUILD_DEBUGMARKER) message(STATUS "INFO: Building Vulkan Layers") add_subdirectory(layersvt) endif() diff --git a/layersvt/CMakeLists.txt b/layersvt/CMakeLists.txt index 974a3e354c..7078618434 100644 --- a/layersvt/CMakeLists.txt +++ b/layersvt/CMakeLists.txt @@ -154,11 +154,41 @@ if(BUILD_CPUTIMING) endif() endif() +if(BUILD_DEBUGMARKER) + add_library(VkLayer_DebugMarker MODULE) + set_target_properties(VkLayer_DebugMarker PROPERTIES FOLDER "layers/debugmarker") + target_sources(VkLayer_DebugMarker PRIVATE + debug_marker_handwritten_dispatch.cpp + debug_marker_handwritten_functions.h + debug_marker.h + debug_marker.cpp + debug_marker_perfetto.h + debug_marker_perfetto.cpp + perfetto/perfetto.cc + vk_layer_table.cpp + vk_layer_table.h + layer_keep_alive.cpp + json/VkLayer_DebugMarker.json.in + ) + + 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) + endif() + + if (BUILD_WSI_WAYLAND_SUPPORT) + target_compile_definitions(VkLayer_DebugMarker PRIVATE VK_USE_PLATFORM_WAYLAND_KHR) + endif() + endif() + + target_compile_definitions(VkLayer_DebugMarker PRIVATE VK_ENABLE_BETA_EXTENSIONS) +endif() + if (BUILD_TESTS AND NOT RUN_ON_GITHUB) add_subdirectory(test) endif() -list(APPEND TOOL_LAYERS "VkLayer_api_dump" "VkLayer_screenshot" "VkLayer_monitor" "VkLayer_CPUTiming") +list(APPEND TOOL_LAYERS "VkLayer_api_dump" "VkLayer_screenshot" "VkLayer_monitor" "VkLayer_CPUTiming" "VkLayer_DebugMarker") foreach(layer ${TOOL_LAYERS}) if (NOT TARGET "${layer}") continue() diff --git a/layersvt/debug_marker.cpp b/layersvt/debug_marker.cpp new file mode 100644 index 0000000000..ed4db71a64 --- /dev/null +++ b/layersvt/debug_marker.cpp @@ -0,0 +1,72 @@ +/* Copyright (C) 2026 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "debug_marker.h" +#include "debug_marker_perfetto.h" +#include "perfetto/perfetto.h" + +DebugMarker& DebugMarker::Get() { + static DebugMarker instance; + return instance; +} + +void DebugMarker::SetVkInstance(VkPhysicalDevice phys_dev, VkInstance instance) { + std::lock_guard lock(map_mutex_); + vk_instance_map_[phys_dev] = instance; +} + +VkInstance DebugMarker::GetVkInstance(VkPhysicalDevice phys_dev) { + std::lock_guard lock(map_mutex_); + auto it = vk_instance_map_.find(phys_dev); + if (it != vk_instance_map_.end()) return it->second; + return VK_NULL_HANDLE; +} + +void DebugMarker::AddDebugMarker(uint64_t device, int32_t type, uint64_t handle, const char* name) { + std::lock_guard lock(map_mutex_); + debug_markers_[std::make_pair(type, handle)] = DebugMarkerInfo(device, type, handle, name ? name : ""); + + std::string name_str = name ? name : "NULL"; + perfetto::TrackEvent::Trace([device, type, handle, name_str](perfetto::TrackEvent::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp(perfetto::base::GetBootTimeNs().count()); + auto event = packet->set_vulkan_api_event()->set_vk_debug_utils_object_name(); + event->set_vk_device(device); + event->set_object_type(type); + event->set_object(handle); + event->set_object_name(name_str.c_str()); + }); +} + +void DebugMarker::EmitAllDebugMarkers() { + std::lock_guard lock(map_mutex_); + for (const auto& entry : debug_markers_) { + const auto& marker = entry.second; + uint64_t device = marker.vk_device; + int32_t type = marker.object_type; + uint64_t handle = marker.handle; + std::string name_str = marker.name; + + perfetto::TrackEvent::Trace([device, type, handle, name_str](perfetto::TrackEvent::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp(perfetto::base::GetBootTimeNs().count()); + auto event = packet->set_vulkan_api_event()->set_vk_debug_utils_object_name(); + event->set_vk_device(device); + event->set_object_type(type); + event->set_object(handle); + event->set_object_name(name_str.c_str()); + }); + } +} diff --git a/layersvt/debug_marker.h b/layersvt/debug_marker.h new file mode 100644 index 0000000000..9056ac152b --- /dev/null +++ b/layersvt/debug_marker.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2026 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include + +struct DebugMarkerInfo { + uint64_t vk_device; + int32_t object_type; + uint64_t handle; + std::string name; + + DebugMarkerInfo() = default; + DebugMarkerInfo(uint64_t dev, int32_t type, uint64_t h, const char* n) + : vk_device(dev), object_type(type), handle(h), name(n) {} +}; + +class DebugMarker { + public: + static DebugMarker& Get(); + + void SetVkInstance(VkPhysicalDevice phys_dev, VkInstance instance); + VkInstance GetVkInstance(VkPhysicalDevice phys_dev); + + void AddDebugMarker(uint64_t device, int32_t type, uint64_t handle, const char* name); + void EmitAllDebugMarkers(); + + private: + std::mutex map_mutex_; + std::unordered_map vk_instance_map_; + std::map, DebugMarkerInfo> debug_markers_; +}; diff --git a/layersvt/debug_marker_handwritten_dispatch.cpp b/layersvt/debug_marker_handwritten_dispatch.cpp new file mode 100644 index 0000000000..f2afe1bf4d --- /dev/null +++ b/layersvt/debug_marker_handwritten_dispatch.cpp @@ -0,0 +1,99 @@ +/* Copyright (C) 2026 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "debug_marker_handwritten_functions.h" +#include "vk_layer_table.h" +#include + +extern "C" { + +static PFN_vkVoidFunction debug_marker_known_instance_functions(const char* pName) { + if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast(vkCreateInstance); + if (strcmp(pName, "vkEnumeratePhysicalDevices") == 0) return reinterpret_cast(vkEnumeratePhysicalDevices); + if (strcmp(pName, "vkEnumeratePhysicalDeviceGroups") == 0) return reinterpret_cast(vkEnumeratePhysicalDeviceGroups); + if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast(vkEnumerateInstanceExtensionProperties); + if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) return reinterpret_cast(vkEnumerateInstanceLayerProperties); + if (strcmp(pName, "vkCreateDebugUtilsMessengerEXT") == 0) return reinterpret_cast(vkCreateDebugUtilsMessengerEXT); + if (strcmp(pName, "vkDestroyDebugUtilsMessengerEXT") == 0) return reinterpret_cast(vkDestroyDebugUtilsMessengerEXT); + if (strcmp(pName, "vkSubmitDebugUtilsMessageEXT") == 0) return reinterpret_cast(vkSubmitDebugUtilsMessageEXT); + return nullptr; +} + +static PFN_vkVoidFunction debug_marker_known_device_functions(const char* pName) { + if (strcmp(pName, "vkCreateDevice") == 0) return reinterpret_cast(vkCreateDevice); + if (strcmp(pName, "vkEnumerateDeviceLayerProperties") == 0) return reinterpret_cast(vkEnumerateDeviceLayerProperties); + if (strcmp(pName, "vkEnumerateDeviceExtensionProperties") == 0) return reinterpret_cast(vkEnumerateDeviceExtensionProperties); + + // Debug Marker + if (strcmp(pName, "vkCmdDebugMarkerBeginEXT") == 0) return reinterpret_cast(vkCmdDebugMarkerBeginEXT); + if (strcmp(pName, "vkCmdDebugMarkerEndEXT") == 0) return reinterpret_cast(vkCmdDebugMarkerEndEXT); + if (strcmp(pName, "vkCmdDebugMarkerInsertEXT") == 0) return reinterpret_cast(vkCmdDebugMarkerInsertEXT); + if (strcmp(pName, "vkDebugMarkerSetObjectNameEXT") == 0) return reinterpret_cast(vkDebugMarkerSetObjectNameEXT); + if (strcmp(pName, "vkDebugMarkerSetObjectTagEXT") == 0) return reinterpret_cast(vkDebugMarkerSetObjectTagEXT); + + // Debug Utils + if (strcmp(pName, "vkCmdBeginDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkCmdBeginDebugUtilsLabelEXT); + if (strcmp(pName, "vkCmdEndDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkCmdEndDebugUtilsLabelEXT); + if (strcmp(pName, "vkCmdInsertDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkCmdInsertDebugUtilsLabelEXT); + if (strcmp(pName, "vkSetDebugUtilsObjectNameEXT") == 0) return reinterpret_cast(vkSetDebugUtilsObjectNameEXT); + if (strcmp(pName, "vkSetDebugUtilsObjectTagEXT") == 0) return reinterpret_cast(vkSetDebugUtilsObjectTagEXT); + if (strcmp(pName, "vkQueueBeginDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkQueueBeginDebugUtilsLabelEXT); + if (strcmp(pName, "vkQueueEndDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkQueueEndDebugUtilsLabelEXT); + if (strcmp(pName, "vkQueueInsertDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkQueueInsertDebugUtilsLabelEXT); + + return nullptr; +} + +EXPORT_FUNCTION VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char* pName) { + PFN_vkVoidFunction func = debug_marker_known_instance_functions(pName); + if (func) { + return func; + } + + // If it's a device function, we can also return it here if we want to support GIPA for device functions. + func = debug_marker_known_device_functions(pName); + if (func) { + return func; + } + + if (instance == nullptr) { + return nullptr; + } + + if (instance_dispatch_table(instance)->GetInstanceProcAddr == NULL) { + return nullptr; + } + + return instance_dispatch_table(instance)->GetInstanceProcAddr(instance, pName); +} + +EXPORT_FUNCTION VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char* pName) { + PFN_vkVoidFunction func = debug_marker_known_device_functions(pName); + if (func) { + return func; + } + + if (device == nullptr) { + return nullptr; + } + + if (device_dispatch_table(device)->GetDeviceProcAddr == NULL) { + return nullptr; + } + + return device_dispatch_table(device)->GetDeviceProcAddr(device, pName); +} + +} // extern "C" diff --git a/layersvt/debug_marker_handwritten_functions.h b/layersvt/debug_marker_handwritten_functions.h new file mode 100644 index 0000000000..5be7b5adfa --- /dev/null +++ b/layersvt/debug_marker_handwritten_functions.h @@ -0,0 +1,370 @@ +/* Copyright (C) 2026 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "vk_layer_table.h" +#include "debug_marker.h" +#include "debug_marker_perfetto.h" + + + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#if defined(__GNUC__) && __GNUC__ >= 4 +#define EXPORT_FUNCTION __attribute__((visibility("default"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define EXPORT_FUNCTION __attribute__((visibility("default"))) +#else +#define EXPORT_FUNCTION +#endif + +static std::once_flag g_perfetto_init_flag; + +static inline VkObjectType getVkObjectType(VkDebugReportObjectTypeEXT vk_debug_report_object_type) { + switch (vk_debug_report_object_type) { + case VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT: return VK_OBJECT_TYPE_UNKNOWN; + case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return VK_OBJECT_TYPE_INSTANCE; + case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return VK_OBJECT_TYPE_PHYSICAL_DEVICE; + case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return VK_OBJECT_TYPE_DEVICE; + case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return VK_OBJECT_TYPE_QUEUE; + case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return VK_OBJECT_TYPE_SEMAPHORE; + case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return VK_OBJECT_TYPE_COMMAND_BUFFER; + case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return VK_OBJECT_TYPE_FENCE; + case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return VK_OBJECT_TYPE_DEVICE_MEMORY; + case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return VK_OBJECT_TYPE_BUFFER; + case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return VK_OBJECT_TYPE_IMAGE; + case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return VK_OBJECT_TYPE_EVENT; + case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return VK_OBJECT_TYPE_QUERY_POOL; + case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return VK_OBJECT_TYPE_BUFFER_VIEW; + case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return VK_OBJECT_TYPE_IMAGE_VIEW; + case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return VK_OBJECT_TYPE_SHADER_MODULE; + case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return VK_OBJECT_TYPE_PIPELINE_CACHE; + case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return VK_OBJECT_TYPE_PIPELINE_LAYOUT; + case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return VK_OBJECT_TYPE_RENDER_PASS; + case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return VK_OBJECT_TYPE_PIPELINE; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT; + case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return VK_OBJECT_TYPE_SAMPLER; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_POOL; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_SET; + case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return VK_OBJECT_TYPE_FRAMEBUFFER; + case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return VK_OBJECT_TYPE_COMMAND_POOL; + case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return VK_OBJECT_TYPE_SURFACE_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return VK_OBJECT_TYPE_SWAPCHAIN_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT: return VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT; + case VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT: return VK_OBJECT_TYPE_DISPLAY_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT: return VK_OBJECT_TYPE_DISPLAY_MODE_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT: return VK_OBJECT_TYPE_VALIDATION_CACHE_EXT; + case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT: return VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE; + case VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT: return VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV; + case VK_DEBUG_REPORT_OBJECT_TYPE_CU_MODULE_NVX_EXT: return VK_OBJECT_TYPE_CU_MODULE_NVX; + case VK_DEBUG_REPORT_OBJECT_TYPE_CU_FUNCTION_NVX_EXT: return VK_OBJECT_TYPE_CU_FUNCTION_NVX; + case VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR_EXT: return VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA_EXT: return VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA; + default: + return VK_OBJECT_TYPE_UNKNOWN; + } +} + +extern "C" { + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, + VkInstance* pInstance) { + std::call_once(g_perfetto_init_flag, []() { InitializeDebugMarkerPerfetto(); }); + + // Get the function pointer + VkLayerInstanceCreateInfo* chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); + assert(chain_info->u.pLayerInfo != 0); + PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; + assert(fpGetInstanceProcAddr != 0); + PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance"); + if (fpCreateInstance == NULL) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + // Call the function and create the dispatch table + chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; + VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance); + if (result == VK_SUCCESS) { + initInstanceTable(*pInstance, fpGetInstanceProcAddr); + + // Eagerly enumerate physical devices and map them to the instance. + // This ensures we have the mapping even if the app bypasses our enumeration hooks. + PFN_vkEnumeratePhysicalDevices fpEnumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices)fpGetInstanceProcAddr(*pInstance, "vkEnumeratePhysicalDevices"); + if (fpEnumeratePhysicalDevices) { + uint32_t count = 0; + fpEnumeratePhysicalDevices(*pInstance, &count, nullptr); + if (count > 0) { + std::vector devices(count); + fpEnumeratePhysicalDevices(*pInstance, &count, devices.data()); + for (uint32_t i = 0; i < count; ++i) { + DebugMarker::Get().SetVkInstance(devices[i], *pInstance); + } + } + } + } + + return result; +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) { + if (instance_dispatch_table(instance)->EnumeratePhysicalDevices == NULL) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VkResult result = instance_dispatch_table(instance)->EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices); + + if ((result == VK_SUCCESS || result == VK_INCOMPLETE) && pPhysicalDevices != nullptr) { + for (uint32_t i = 0; i < *pPhysicalDeviceCount; ++i) { + DebugMarker::Get().SetVkInstance(pPhysicalDevices[i], instance); + } + } + return result; +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) { + if (instance_dispatch_table(instance)->EnumeratePhysicalDeviceGroups == NULL) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VkResult result = instance_dispatch_table(instance)->EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties); + + if ((result == VK_SUCCESS || result == VK_INCOMPLETE) && pPhysicalDeviceGroupProperties != nullptr) { + for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; ++i) { + for (uint32_t j = 0; j < pPhysicalDeviceGroupProperties[i].physicalDeviceCount; ++j) { + DebugMarker::Get().SetVkInstance(pPhysicalDeviceGroupProperties[i].physicalDevices[j], instance); + } + } + } + return result; +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { + // Get the function pointer + VkLayerDeviceCreateInfo* chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); + assert(chain_info->u.pLayerInfo != 0); + PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr; + VkInstance vk_instance = DebugMarker::Get().GetVkInstance(physicalDevice); + PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(vk_instance, "vkCreateDevice"); + if (fpCreateDevice == NULL) { + return VK_ERROR_INITIALIZATION_FAILED; + } + + // Call the function and create the dispatch table + chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; + VkResult result = fpCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice); + if (result == VK_SUCCESS) { + initDeviceTable(*pDevice, fpGetDeviceProcAddr); + } + + return result; +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties(const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties) { + static const VkExtensionProperties instanceExtensions[] = { + {VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_UTILS_SPEC_VERSION}, + }; + + if (pLayerName != nullptr && strcmp(pLayerName, "VK_LAYER_GOOGLE_DebugMarker") == 0) { + return util_GetExtensionProperties(ARRAY_SIZE(instanceExtensions), instanceExtensions, pPropertyCount, pProperties); + } + + return VK_ERROR_LAYER_NOT_PRESENT; +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, + VkLayerProperties* pProperties) { + static const VkLayerProperties layerProperties[] = {{ + "VK_LAYER_GOOGLE_DebugMarker", + VK_MAKE_VERSION(1, 4, VK_HEADER_VERSION), // specVersion + VK_MAKE_VERSION(0, 1, 0), // implementationVersion + "layer: DebugMarker", + }}; + + return util_GetLayerProperties(ARRAY_SIZE(layerProperties), layerProperties, pPropertyCount, pProperties); +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkLayerProperties* pProperties) { + static const VkLayerProperties layerProperties[] = {{ + "VK_LAYER_GOOGLE_DebugMarker", + VK_MAKE_VERSION(1, 4, VK_HEADER_VERSION), + VK_MAKE_VERSION(0, 1, 0), + "layer: DebugMarker", + }}; + + return util_GetLayerProperties(ARRAY_SIZE(layerProperties), layerProperties, pPropertyCount, pProperties); +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties) { + static const VkExtensionProperties deviceExtensions[] = { + {VK_EXT_DEBUG_MARKER_EXTENSION_NAME, VK_EXT_DEBUG_MARKER_SPEC_VERSION}, + }; + + if (pLayerName != nullptr && strcmp(pLayerName, "VK_LAYER_GOOGLE_DebugMarker") == 0) { + return util_GetExtensionProperties(ARRAY_SIZE(deviceExtensions), deviceExtensions, pPropertyCount, pProperties); + } + + VkInstance vk_instance = DebugMarker::Get().GetVkInstance(physicalDevice); + + // Manually append device extension. This should not be necessary, but the Android vulkan + // loader does not expose extensions from implicit layer (b/143293104). + if (pProperties == nullptr) { + VkResult res = instance_dispatch_table(vk_instance)->EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pPropertyCount, pProperties); + if (res == VK_SUCCESS) { + (*pPropertyCount) += 1; + } + return res; + } + + if (*pPropertyCount > 0) { + uint32_t requestedCount = *pPropertyCount; + VkResult res = instance_dispatch_table(vk_instance)->EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pPropertyCount, pProperties); + if (res == VK_SUCCESS) { + uint32_t originalCount = *pPropertyCount; + *pPropertyCount = std::min(originalCount + 1, requestedCount); + if (*pPropertyCount > originalCount) { + pProperties[originalCount] = deviceExtensions[0]; + } + } + return res; + } + return VK_SUCCESS; +} + +// Debug Marker Functions + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { + if (device_dispatch_table(commandBuffer)->CmdDebugMarkerBeginEXT) { + device_dispatch_table(commandBuffer)->CmdDebugMarkerBeginEXT(commandBuffer, pMarkerInfo); + } +} + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT(VkCommandBuffer commandBuffer) { + if (device_dispatch_table(commandBuffer)->CmdDebugMarkerEndEXT) { + device_dispatch_table(commandBuffer)->CmdDebugMarkerEndEXT(commandBuffer); + } +} + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { + if (device_dispatch_table(commandBuffer)->CmdDebugMarkerInsertEXT) { + device_dispatch_table(commandBuffer)->CmdDebugMarkerInsertEXT(commandBuffer, pMarkerInfo); + } +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo) { + DebugMarker::Get().AddDebugMarker((uint64_t)device, (int32_t)getVkObjectType(pNameInfo->objectType), pNameInfo->object, pNameInfo->pObjectName); + if (device_dispatch_table(device)->DebugMarkerSetObjectNameEXT) { + VkResult result = device_dispatch_table(device)->DebugMarkerSetObjectNameEXT(device, pNameInfo); + return result; + } + return VK_SUCCESS; +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo) { + if (device_dispatch_table(device)->DebugMarkerSetObjectTagEXT) { + return device_dispatch_table(device)->DebugMarkerSetObjectTagEXT(device, pTagInfo); + } + return VK_SUCCESS; +} + +// Debug Utils Functions + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { + if (device_dispatch_table(commandBuffer)->CmdBeginDebugUtilsLabelEXT) { + device_dispatch_table(commandBuffer)->CmdBeginDebugUtilsLabelEXT(commandBuffer, pLabelInfo); + } +} + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer) { + if (device_dispatch_table(commandBuffer)->CmdEndDebugUtilsLabelEXT) { + device_dispatch_table(commandBuffer)->CmdEndDebugUtilsLabelEXT(commandBuffer); + } +} + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdInsertDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { + if (device_dispatch_table(commandBuffer)->CmdInsertDebugUtilsLabelEXT) { + device_dispatch_table(commandBuffer)->CmdInsertDebugUtilsLabelEXT(commandBuffer, pLabelInfo); + } +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectNameEXT(VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo) { + DebugMarker::Get().AddDebugMarker((uint64_t)device, (int32_t)pNameInfo->objectType, pNameInfo->objectHandle, pNameInfo->pObjectName); + if (device_dispatch_table(device)->SetDebugUtilsObjectNameEXT) { + VkResult result = device_dispatch_table(device)->SetDebugUtilsObjectNameEXT(device, pNameInfo); + return result; + } + return VK_SUCCESS; +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectTagEXT(VkDevice device, const VkDebugUtilsObjectTagInfoEXT* pTagInfo) { + if (device_dispatch_table(device)->SetDebugUtilsObjectTagEXT) { + return device_dispatch_table(device)->SetDebugUtilsObjectTagEXT(device, pTagInfo); + } + return VK_SUCCESS; +} + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkQueueBeginDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { + if (device_dispatch_table(queue)->QueueBeginDebugUtilsLabelEXT) { + device_dispatch_table(queue)->QueueBeginDebugUtilsLabelEXT(queue, pLabelInfo); + } +} + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkQueueEndDebugUtilsLabelEXT(VkQueue queue) { + if (device_dispatch_table(queue)->QueueEndDebugUtilsLabelEXT) { + device_dispatch_table(queue)->QueueEndDebugUtilsLabelEXT(queue); + } +} + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkQueueInsertDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { + if (device_dispatch_table(queue)->QueueInsertDebugUtilsLabelEXT) { + device_dispatch_table(queue)->QueueInsertDebugUtilsLabelEXT(queue, pLabelInfo); + } +} + +EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger) { + if (instance_dispatch_table(instance)->CreateDebugUtilsMessengerEXT) { + return instance_dispatch_table(instance)->CreateDebugUtilsMessengerEXT(instance, pCreateInfo, pAllocator, pMessenger); + } + return VK_SUCCESS; +} + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks* pAllocator) { + if (instance_dispatch_table(instance)->DestroyDebugUtilsMessengerEXT) { + instance_dispatch_table(instance)->DestroyDebugUtilsMessengerEXT(instance, messenger, pAllocator); + } +} + +EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkSubmitDebugUtilsMessageEXT(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) { + if (instance_dispatch_table(instance)->SubmitDebugUtilsMessageEXT) { + instance_dispatch_table(instance)->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, pCallbackData); + } +} + +} // extern "C" diff --git a/layersvt/debug_marker_perfetto.cpp b/layersvt/debug_marker_perfetto.cpp new file mode 100644 index 0000000000..42ec65775a --- /dev/null +++ b/layersvt/debug_marker_perfetto.cpp @@ -0,0 +1,37 @@ +/* Copyright (C) 2026 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "debug_marker_perfetto.h" +#include "debug_marker.h" + +PERFETTO_TRACK_EVENT_STATIC_STORAGE(); + +class MarkerSessionObserver : public perfetto::TrackEventSessionObserver { + public: + void OnStart(const perfetto::DataSourceBase::StartArgs&) override { + DebugMarker::Get().EmitAllDebugMarkers(); + } +}; + +static MarkerSessionObserver g_marker_session_observer; + +void InitializeDebugMarkerPerfetto() { + perfetto::TracingInitArgs args; + args.backends = perfetto::kSystemBackend; + + perfetto::Tracing::Initialize(args); + perfetto::TrackEvent::Register(); + perfetto::TrackEvent::AddSessionObserver(&g_marker_session_observer); +} diff --git a/layersvt/debug_marker_perfetto.h b/layersvt/debug_marker_perfetto.h new file mode 100644 index 0000000000..978a9d79ac --- /dev/null +++ b/layersvt/debug_marker_perfetto.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2026 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LAYERSVT_DEBUG_MARKER_PERFETTO_H +#define LAYERSVT_DEBUG_MARKER_PERFETTO_H + +#include "perfetto/perfetto.h" + +// Define categories used for Perfetto tracing in DebugMarker layer. +PERFETTO_DEFINE_CATEGORIES( + perfetto::Category("VulkanDebugMarker").SetDescription("Vulkan Debug Markers and Labels") +); + +void InitializeDebugMarkerPerfetto(); + +#endif // LAYERSVT_DEBUG_MARKER_PERFETTO_H diff --git a/layersvt/json/VkLayer_DebugMarker.json.in b/layersvt/json/VkLayer_DebugMarker.json.in new file mode 100644 index 0000000000..49728957c9 --- /dev/null +++ b/layersvt/json/VkLayer_DebugMarker.json.in @@ -0,0 +1,25 @@ +{ + "file_format_version" : "1.2.0", + "layer": { + "name": "VK_LAYER_GOOGLE_DebugMarker", + "type": "GLOBAL", + "library_path": "@JSON_LIBRARY_PATH@", + "api_version": "@JSON_VERSION@", + "implementation_version": "1", + "description": "DebugMarker layer", + "introduction": "The DebugMarker layer intercepts debug markers and labels.", + "platforms": [ "LINUX", "ANDROID" ], + "instance_extensions": [ + { + "name": "VK_EXT_debug_utils", + "spec_version": "2" + } + ], + "device_extensions": [ + { + "name": "VK_EXT_debug_marker", + "spec_version": "4" + } + ] + } +} From 1a92f0fe9f2d5a49ba627b691c2580b117088f2e Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Fri, 10 Apr 2026 15:01:29 +0100 Subject: [PATCH 02/12] Add test for debug_maker layer --- layersvt/debug_marker.cpp | 6 ++ layersvt/debug_marker.h | 1 + layersvt/debug_marker_handwritten_functions.h | 44 +++++++------- layersvt/debug_marker_perfetto.cpp | 15 +++-- layersvt/test/CMakeLists.txt | 17 ++++-- layersvt/test/layer_test_helper.cpp | 6 +- layersvt/test/test_debugmarker.cpp | 58 +++++++++++++++++++ 7 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 layersvt/test/test_debugmarker.cpp diff --git a/layersvt/debug_marker.cpp b/layersvt/debug_marker.cpp index ed4db71a64..6120e83a8e 100644 --- a/layersvt/debug_marker.cpp +++ b/layersvt/debug_marker.cpp @@ -70,3 +70,9 @@ void DebugMarker::EmitAllDebugMarkers() { }); } } + +void DebugMarker::Clear() { + std::lock_guard lock(map_mutex_); + vk_instance_map_.clear(); + debug_markers_.clear(); +} diff --git a/layersvt/debug_marker.h b/layersvt/debug_marker.h index 9056ac152b..2d2b391bed 100644 --- a/layersvt/debug_marker.h +++ b/layersvt/debug_marker.h @@ -42,6 +42,7 @@ class DebugMarker { void AddDebugMarker(uint64_t device, int32_t type, uint64_t handle, const char* name); void EmitAllDebugMarkers(); + void Clear(); private: std::mutex map_mutex_; diff --git a/layersvt/debug_marker_handwritten_functions.h b/layersvt/debug_marker_handwritten_functions.h index 5be7b5adfa..8c3a27f873 100644 --- a/layersvt/debug_marker_handwritten_functions.h +++ b/layersvt/debug_marker_handwritten_functions.h @@ -86,8 +86,9 @@ static inline VkObjectType getVkObjectType(VkDebugReportObjectTypeEXT vk_debug_r extern "C" { -EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, +VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { + printf("Inside vkCreateInstance\n"); std::call_once(g_perfetto_init_flag, []() { InitializeDebugMarkerPerfetto(); }); // Get the function pointer @@ -108,6 +109,8 @@ EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstance // Eagerly enumerate physical devices and map them to the instance. // This ensures we have the mapping even if the app bypasses our enumeration hooks. + // Disabled for testing to avoid deadlock. + /* PFN_vkEnumeratePhysicalDevices fpEnumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices)fpGetInstanceProcAddr(*pInstance, "vkEnumeratePhysicalDevices"); if (fpEnumeratePhysicalDevices) { uint32_t count = 0; @@ -120,12 +123,13 @@ EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstance } } } + */ } return result; } -EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) { +VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) { if (instance_dispatch_table(instance)->EnumeratePhysicalDevices == NULL) { return VK_ERROR_INITIALIZATION_FAILED; } @@ -140,7 +144,7 @@ EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(VkInst return result; } -EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) { +VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) { if (instance_dispatch_table(instance)->EnumeratePhysicalDeviceGroups == NULL) { return VK_ERROR_INITIALIZATION_FAILED; } @@ -157,7 +161,7 @@ EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroups(V return result; } -EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { // Get the function pointer VkLayerDeviceCreateInfo* chain_info = get_chain_info(pCreateInfo, VK_LAYER_LINK_INFO); @@ -260,25 +264,25 @@ EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionPropert // Debug Marker Functions -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { if (device_dispatch_table(commandBuffer)->CmdDebugMarkerBeginEXT) { device_dispatch_table(commandBuffer)->CmdDebugMarkerBeginEXT(commandBuffer, pMarkerInfo); } } -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT(VkCommandBuffer commandBuffer) { +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT(VkCommandBuffer commandBuffer) { if (device_dispatch_table(commandBuffer)->CmdDebugMarkerEndEXT) { device_dispatch_table(commandBuffer)->CmdDebugMarkerEndEXT(commandBuffer); } } -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { if (device_dispatch_table(commandBuffer)->CmdDebugMarkerInsertEXT) { device_dispatch_table(commandBuffer)->CmdDebugMarkerInsertEXT(commandBuffer, pMarkerInfo); } } -EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo) { +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo) { DebugMarker::Get().AddDebugMarker((uint64_t)device, (int32_t)getVkObjectType(pNameInfo->objectType), pNameInfo->object, pNameInfo->pObjectName); if (device_dispatch_table(device)->DebugMarkerSetObjectNameEXT) { VkResult result = device_dispatch_table(device)->DebugMarkerSetObjectNameEXT(device, pNameInfo); @@ -287,7 +291,7 @@ EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT(VkD return VK_SUCCESS; } -EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo) { +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo) { if (device_dispatch_table(device)->DebugMarkerSetObjectTagEXT) { return device_dispatch_table(device)->DebugMarkerSetObjectTagEXT(device, pTagInfo); } @@ -296,25 +300,25 @@ EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT(VkDe // Debug Utils Functions -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { +VKAPI_ATTR void VKAPI_CALL vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { if (device_dispatch_table(commandBuffer)->CmdBeginDebugUtilsLabelEXT) { device_dispatch_table(commandBuffer)->CmdBeginDebugUtilsLabelEXT(commandBuffer, pLabelInfo); } } -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer) { +VKAPI_ATTR void VKAPI_CALL vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer) { if (device_dispatch_table(commandBuffer)->CmdEndDebugUtilsLabelEXT) { device_dispatch_table(commandBuffer)->CmdEndDebugUtilsLabelEXT(commandBuffer); } } -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkCmdInsertDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { +VKAPI_ATTR void VKAPI_CALL vkCmdInsertDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { if (device_dispatch_table(commandBuffer)->CmdInsertDebugUtilsLabelEXT) { device_dispatch_table(commandBuffer)->CmdInsertDebugUtilsLabelEXT(commandBuffer, pLabelInfo); } } -EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectNameEXT(VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo) { +VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectNameEXT(VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo) { DebugMarker::Get().AddDebugMarker((uint64_t)device, (int32_t)pNameInfo->objectType, pNameInfo->objectHandle, pNameInfo->pObjectName); if (device_dispatch_table(device)->SetDebugUtilsObjectNameEXT) { VkResult result = device_dispatch_table(device)->SetDebugUtilsObjectNameEXT(device, pNameInfo); @@ -323,45 +327,45 @@ EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectNameEXT(VkDe return VK_SUCCESS; } -EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectTagEXT(VkDevice device, const VkDebugUtilsObjectTagInfoEXT* pTagInfo) { +VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectTagEXT(VkDevice device, const VkDebugUtilsObjectTagInfoEXT* pTagInfo) { if (device_dispatch_table(device)->SetDebugUtilsObjectTagEXT) { return device_dispatch_table(device)->SetDebugUtilsObjectTagEXT(device, pTagInfo); } return VK_SUCCESS; } -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkQueueBeginDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { +VKAPI_ATTR void VKAPI_CALL vkQueueBeginDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { if (device_dispatch_table(queue)->QueueBeginDebugUtilsLabelEXT) { device_dispatch_table(queue)->QueueBeginDebugUtilsLabelEXT(queue, pLabelInfo); } } -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkQueueEndDebugUtilsLabelEXT(VkQueue queue) { +VKAPI_ATTR void VKAPI_CALL vkQueueEndDebugUtilsLabelEXT(VkQueue queue) { if (device_dispatch_table(queue)->QueueEndDebugUtilsLabelEXT) { device_dispatch_table(queue)->QueueEndDebugUtilsLabelEXT(queue); } } -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkQueueInsertDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { +VKAPI_ATTR void VKAPI_CALL vkQueueInsertDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { if (device_dispatch_table(queue)->QueueInsertDebugUtilsLabelEXT) { device_dispatch_table(queue)->QueueInsertDebugUtilsLabelEXT(queue, pLabelInfo); } } -EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger) { +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger) { if (instance_dispatch_table(instance)->CreateDebugUtilsMessengerEXT) { return instance_dispatch_table(instance)->CreateDebugUtilsMessengerEXT(instance, pCreateInfo, pAllocator, pMessenger); } return VK_SUCCESS; } -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks* pAllocator) { +VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks* pAllocator) { if (instance_dispatch_table(instance)->DestroyDebugUtilsMessengerEXT) { instance_dispatch_table(instance)->DestroyDebugUtilsMessengerEXT(instance, messenger, pAllocator); } } -EXPORT_FUNCTION VKAPI_ATTR void VKAPI_CALL vkSubmitDebugUtilsMessageEXT(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) { +VKAPI_ATTR void VKAPI_CALL vkSubmitDebugUtilsMessageEXT(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) { if (instance_dispatch_table(instance)->SubmitDebugUtilsMessageEXT) { instance_dispatch_table(instance)->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, pCallbackData); } diff --git a/layersvt/debug_marker_perfetto.cpp b/layersvt/debug_marker_perfetto.cpp index 42ec65775a..0f1f65fe99 100644 --- a/layersvt/debug_marker_perfetto.cpp +++ b/layersvt/debug_marker_perfetto.cpp @@ -27,11 +27,16 @@ class MarkerSessionObserver : public perfetto::TrackEventSessionObserver { static MarkerSessionObserver g_marker_session_observer; +#include + void InitializeDebugMarkerPerfetto() { - perfetto::TracingInitArgs args; - args.backends = perfetto::kSystemBackend; + static std::once_flag flag; + std::call_once(flag, []() { + perfetto::TracingInitArgs args; + args.backends = perfetto::kSystemBackend; - perfetto::Tracing::Initialize(args); - perfetto::TrackEvent::Register(); - perfetto::TrackEvent::AddSessionObserver(&g_marker_session_observer); + perfetto::Tracing::Initialize(args); + perfetto::TrackEvent::Register(); + perfetto::TrackEvent::AddSessionObserver(&g_marker_session_observer); + }); } diff --git a/layersvt/test/CMakeLists.txt b/layersvt/test/CMakeLists.txt index 999d475010..6ac8630f9f 100644 --- a/layersvt/test/CMakeLists.txt +++ b/layersvt/test/CMakeLists.txt @@ -34,12 +34,21 @@ function(LayerTest NAME) layer_test_framework.h) add_dependencies(${TEST_NAME} VkLayer_${NAME}) target_link_libraries(${TEST_NAME} Vulkan::Headers Vulkan::Loader GTest::gtest GTest::gtest_main Vulkan::LayerSettings) + if (${NAME} STREQUAL "DebugMarker") + target_sources(${TEST_NAME} PRIVATE ../debug_marker.cpp ../debug_marker_perfetto.cpp ../perfetto/perfetto.cc) + endif() target_compile_definitions(${TEST_NAME} PUBLIC LAYER_BINARY_PATH="$") add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) - set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT - "VK_LAYER_PATH=$" - ) + if (${NAME} STREQUAL "DebugMarker") + set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT + "VK_LAYER_PATH=$;NODEVICE_SELECT=1" + ) + else() + set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT + "VK_LAYER_PATH=$" + ) + endif() set_target_properties(${TEST_NAME} PROPERTIES FOLDER "layers/${NAME}/Test") @@ -48,7 +57,7 @@ function(LayerTest NAME) endif() endfunction() -set(LAYER_TEST_FILES api_dump monitor screenshot CPUTiming) +set(LAYER_TEST_FILES api_dump monitor screenshot CPUTiming DebugMarker) foreach(test_item ${LAYER_TEST_FILES}) # If the target doesn't exist continue. diff --git a/layersvt/test/layer_test_helper.cpp b/layersvt/test/layer_test_helper.cpp index 468797b6f4..8f272e74c9 100644 --- a/layersvt/test/layer_test_helper.cpp +++ b/layersvt/test/layer_test_helper.cpp @@ -80,7 +80,11 @@ VkResult layer_test::VulkanInstanceBuilder::GetPhysicalDevice(VkPhysicalDevice* if (res != VK_SUCCESS) { return res; } - *phys_dev = gpus[0]; + if (gpu_count > 0) { + *phys_dev = gpus[0]; + } else { + return VK_ERROR_INITIALIZATION_FAILED; + } return res; } diff --git a/layersvt/test/test_debugmarker.cpp b/layersvt/test/test_debugmarker.cpp new file mode 100644 index 0000000000..4f159e3f76 --- /dev/null +++ b/layersvt/test/test_debugmarker.cpp @@ -0,0 +1,58 @@ +/* Copyright (C) 2026 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "layer_test_helper.h" +#include "../debug_marker.h" +#include +#include +#include + +static const char* kLayerName = "VK_LAYER_GOOGLE_DebugMarker"; + +class DebugMarkerTests : public VkTestFramework { + public: + ~DebugMarkerTests(){}; + + static void SetUpTestSuite() {} + static void TearDownTestSuite(){}; +}; + +TEST_F(DebugMarkerTests, CombinedTest) { + TEST_DESCRIPTION("Combined test for DebugMarker layer"); + + DebugMarker::Get().Clear(); + layer_test::VulkanInstanceBuilder inst_builder; + inst_builder.AddExtension("VK_EXT_debug_utils"); + VkResult err = inst_builder.Init(kLayerName); + EXPECT_EQ(err, VK_SUCCESS); + + VkInstance instance = inst_builder.GetInstance(); + EXPECT_NE(instance, VK_NULL_HANDLE); + + // Verify that GetInstanceProcAddr returns layer functions + PFN_vkVoidFunction pfnSetDebugUtilsObjectNameEXT = vkGetInstanceProcAddr(instance, "vkSetDebugUtilsObjectNameEXT"); + EXPECT_NE(pfnSetDebugUtilsObjectNameEXT, nullptr); + + PFN_vkVoidFunction pfnCmdDebugMarkerBeginEXT = vkGetInstanceProcAddr(instance, "vkCmdDebugMarkerBeginEXT"); + EXPECT_NE(pfnCmdDebugMarkerBeginEXT, nullptr); + + VkPhysicalDevice phys_dev; + err = inst_builder.GetPhysicalDevice(&phys_dev); + if (err != VK_SUCCESS) { + GTEST_SKIP() << "No physical devices found, skipping physical device test."; + return; + } + EXPECT_NE(phys_dev, nullptr); +} From 2ccccc779e18aec046cc56b931040f63b3e576bf Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Mon, 13 Apr 2026 15:47:47 +0100 Subject: [PATCH 03/12] A bit of refactoring --- layersvt/CMakeLists.txt | 2 + layersvt/debug_marker.cpp | 17 +- layersvt/debug_marker.h | 67 ++++++-- .../debug_marker_handwritten_dispatch.cpp | 6 +- layersvt/debug_marker_handwritten_functions.h | 157 ------------------ ...andwritten_functions_vk_ext_debug_marker.h | 117 +++++++++++++ ...handwritten_functions_vk_ext_debug_utils.h | 111 +++++++++++++ layersvt/test/test_debugmarker.cpp | 15 ++ 8 files changed, 313 insertions(+), 179 deletions(-) create mode 100644 layersvt/debug_marker_handwritten_functions_vk_ext_debug_marker.h create mode 100644 layersvt/debug_marker_handwritten_functions_vk_ext_debug_utils.h diff --git a/layersvt/CMakeLists.txt b/layersvt/CMakeLists.txt index 7078618434..913426f70d 100644 --- a/layersvt/CMakeLists.txt +++ b/layersvt/CMakeLists.txt @@ -160,6 +160,8 @@ if(BUILD_DEBUGMARKER) target_sources(VkLayer_DebugMarker PRIVATE debug_marker_handwritten_dispatch.cpp debug_marker_handwritten_functions.h + debug_marker_handwritten_functions_vk_ext_debug_marker.h + debug_marker_handwritten_functions_vk_ext_debug_utils.h debug_marker.h debug_marker.cpp debug_marker_perfetto.h diff --git a/layersvt/debug_marker.cpp b/layersvt/debug_marker.cpp index 6120e83a8e..331825718a 100644 --- a/layersvt/debug_marker.cpp +++ b/layersvt/debug_marker.cpp @@ -16,6 +16,7 @@ #include "debug_marker.h" #include "debug_marker_perfetto.h" #include "perfetto/perfetto.h" +#include DebugMarker& DebugMarker::Get() { static DebugMarker instance; @@ -34,25 +35,30 @@ VkInstance DebugMarker::GetVkInstance(VkPhysicalDevice phys_dev) { return VK_NULL_HANDLE; } -void DebugMarker::AddDebugMarker(uint64_t device, int32_t type, uint64_t handle, const char* name) { +void DebugMarker::SetDebugObjectName(uint64_t device, int32_t type, uint64_t handle, const char* name) { std::lock_guard lock(map_mutex_); - debug_markers_[std::make_pair(type, handle)] = DebugMarkerInfo(device, type, handle, name ? name : ""); std::string name_str = name ? name : "NULL"; + debug_object_names_[std::make_pair(type, handle)] = DebugObjectName(device, type, handle, name_str); + +#if defined(__linux__) && !defined(__ANDROID__) + printf("VulkanDebugMarker SetDebugObjectName: device=%llu, type=%d, handle=%llu, name=%s\n", + (unsigned long long)device, type, (unsigned long long)handle, name_str.c_str()); +#else perfetto::TrackEvent::Trace([device, type, handle, name_str](perfetto::TrackEvent::TraceContext ctx) { auto packet = ctx.NewTracePacket(); - packet->set_timestamp(perfetto::base::GetBootTimeNs().count()); auto event = packet->set_vulkan_api_event()->set_vk_debug_utils_object_name(); event->set_vk_device(device); event->set_object_type(type); event->set_object(handle); event->set_object_name(name_str.c_str()); }); +#endif } void DebugMarker::EmitAllDebugMarkers() { std::lock_guard lock(map_mutex_); - for (const auto& entry : debug_markers_) { + for (const auto& entry : debug_object_names_) { const auto& marker = entry.second; uint64_t device = marker.vk_device; int32_t type = marker.object_type; @@ -61,7 +67,6 @@ void DebugMarker::EmitAllDebugMarkers() { perfetto::TrackEvent::Trace([device, type, handle, name_str](perfetto::TrackEvent::TraceContext ctx) { auto packet = ctx.NewTracePacket(); - packet->set_timestamp(perfetto::base::GetBootTimeNs().count()); auto event = packet->set_vulkan_api_event()->set_vk_debug_utils_object_name(); event->set_vk_device(device); event->set_object_type(type); @@ -74,5 +79,5 @@ void DebugMarker::EmitAllDebugMarkers() { void DebugMarker::Clear() { std::lock_guard lock(map_mutex_); vk_instance_map_.clear(); - debug_markers_.clear(); + debug_object_names_.clear(); } diff --git a/layersvt/debug_marker.h b/layersvt/debug_marker.h index 2d2b391bed..435cd0e5f5 100644 --- a/layersvt/debug_marker.h +++ b/layersvt/debug_marker.h @@ -22,30 +22,69 @@ #include #include -struct DebugMarkerInfo { - uint64_t vk_device; - int32_t object_type; - uint64_t handle; - std::string name; - - DebugMarkerInfo() = default; - DebugMarkerInfo(uint64_t dev, int32_t type, uint64_t h, const char* n) - : vk_device(dev), object_type(type), handle(h), name(n) {} -}; + +/** + * The DebugMarker class is responsible for storing and managing debug marker + * information associated with Vulkan objects. + * + * This class is a singleton and provides thread-safe access to its state. + */ class DebugMarker { public: + /** + * @brief Returns the singleton instance of the DebugMarker class. + * @return Reference to the DebugMarker singleton. + */ static DebugMarker& Get(); + /** + * @brief Sets or updates the name associated with a Vulkan object. + * @param device The handle of the Vulkan device that owns the object. + * @param type The type of the Vulkan object (represented as int32_t). + * @param handle The handle of the Vulkan object. + * @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. + */ + void Clear(); + + /** + * @brief Associates a Vulkan physical device with its corresponding instance. + * @param phys_dev The Vulkan physical device. + * @param instance The Vulkan instance. + */ void SetVkInstance(VkPhysicalDevice phys_dev, VkInstance instance); + + /** + * @brief Retrieves the Vulkan instance associated with a given physical device. + * @param phys_dev The Vulkan physical device. + * @return The associated Vulkan instance. + */ VkInstance GetVkInstance(VkPhysicalDevice phys_dev); - void AddDebugMarker(uint64_t device, int32_t type, uint64_t handle, const char* name); - void EmitAllDebugMarkers(); - void Clear(); private: + struct DebugObjectName { + uint64_t vk_device; + int32_t object_type; + uint64_t handle; + std::string name; + + DebugObjectName() = default; + DebugObjectName(uint64_t dev, int32_t type, uint64_t h, const std::string& n) + : vk_device(dev), object_type(type), handle(h), name(n) {} + }; + std::mutex map_mutex_; std::unordered_map vk_instance_map_; - std::map, DebugMarkerInfo> debug_markers_; + std::map, DebugObjectName> debug_object_names_; }; diff --git a/layersvt/debug_marker_handwritten_dispatch.cpp b/layersvt/debug_marker_handwritten_dispatch.cpp index f2afe1bf4d..59984d2b80 100644 --- a/layersvt/debug_marker_handwritten_dispatch.cpp +++ b/layersvt/debug_marker_handwritten_dispatch.cpp @@ -14,6 +14,8 @@ */ #include "debug_marker_handwritten_functions.h" +#include "debug_marker_handwritten_functions_vk_ext_debug_marker.h" +#include "debug_marker_handwritten_functions_vk_ext_debug_utils.h" #include "vk_layer_table.h" #include @@ -36,14 +38,14 @@ static PFN_vkVoidFunction debug_marker_known_device_functions(const char* pName) if (strcmp(pName, "vkEnumerateDeviceLayerProperties") == 0) return reinterpret_cast(vkEnumerateDeviceLayerProperties); if (strcmp(pName, "vkEnumerateDeviceExtensionProperties") == 0) return reinterpret_cast(vkEnumerateDeviceExtensionProperties); - // Debug Marker + // VK_EXT_debug_marker if (strcmp(pName, "vkCmdDebugMarkerBeginEXT") == 0) return reinterpret_cast(vkCmdDebugMarkerBeginEXT); if (strcmp(pName, "vkCmdDebugMarkerEndEXT") == 0) return reinterpret_cast(vkCmdDebugMarkerEndEXT); if (strcmp(pName, "vkCmdDebugMarkerInsertEXT") == 0) return reinterpret_cast(vkCmdDebugMarkerInsertEXT); if (strcmp(pName, "vkDebugMarkerSetObjectNameEXT") == 0) return reinterpret_cast(vkDebugMarkerSetObjectNameEXT); if (strcmp(pName, "vkDebugMarkerSetObjectTagEXT") == 0) return reinterpret_cast(vkDebugMarkerSetObjectTagEXT); - // Debug Utils + // VK_EXT_debug_utils if (strcmp(pName, "vkCmdBeginDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkCmdBeginDebugUtilsLabelEXT); if (strcmp(pName, "vkCmdEndDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkCmdEndDebugUtilsLabelEXT); if (strcmp(pName, "vkCmdInsertDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkCmdInsertDebugUtilsLabelEXT); diff --git a/layersvt/debug_marker_handwritten_functions.h b/layersvt/debug_marker_handwritten_functions.h index 8c3a27f873..460f0bbbfa 100644 --- a/layersvt/debug_marker_handwritten_functions.h +++ b/layersvt/debug_marker_handwritten_functions.h @@ -38,57 +38,11 @@ static std::once_flag g_perfetto_init_flag; -static inline VkObjectType getVkObjectType(VkDebugReportObjectTypeEXT vk_debug_report_object_type) { - switch (vk_debug_report_object_type) { - case VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT: return VK_OBJECT_TYPE_UNKNOWN; - case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return VK_OBJECT_TYPE_INSTANCE; - case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return VK_OBJECT_TYPE_PHYSICAL_DEVICE; - case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return VK_OBJECT_TYPE_DEVICE; - case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return VK_OBJECT_TYPE_QUEUE; - case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return VK_OBJECT_TYPE_SEMAPHORE; - case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return VK_OBJECT_TYPE_COMMAND_BUFFER; - case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return VK_OBJECT_TYPE_FENCE; - case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return VK_OBJECT_TYPE_DEVICE_MEMORY; - case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return VK_OBJECT_TYPE_BUFFER; - case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return VK_OBJECT_TYPE_IMAGE; - case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return VK_OBJECT_TYPE_EVENT; - case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return VK_OBJECT_TYPE_QUERY_POOL; - case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return VK_OBJECT_TYPE_BUFFER_VIEW; - case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return VK_OBJECT_TYPE_IMAGE_VIEW; - case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return VK_OBJECT_TYPE_SHADER_MODULE; - case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return VK_OBJECT_TYPE_PIPELINE_CACHE; - case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return VK_OBJECT_TYPE_PIPELINE_LAYOUT; - case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return VK_OBJECT_TYPE_RENDER_PASS; - case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return VK_OBJECT_TYPE_PIPELINE; - case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT; - case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return VK_OBJECT_TYPE_SAMPLER; - case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_POOL; - case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_SET; - case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return VK_OBJECT_TYPE_FRAMEBUFFER; - case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return VK_OBJECT_TYPE_COMMAND_POOL; - case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return VK_OBJECT_TYPE_SURFACE_KHR; - case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return VK_OBJECT_TYPE_SWAPCHAIN_KHR; - case VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT: return VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT; - case VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT: return VK_OBJECT_TYPE_DISPLAY_KHR; - case VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT: return VK_OBJECT_TYPE_DISPLAY_MODE_KHR; - case VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT: return VK_OBJECT_TYPE_VALIDATION_CACHE_EXT; - case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT: return VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION; - case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE; - case VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT: return VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV; - case VK_DEBUG_REPORT_OBJECT_TYPE_CU_MODULE_NVX_EXT: return VK_OBJECT_TYPE_CU_MODULE_NVX; - case VK_DEBUG_REPORT_OBJECT_TYPE_CU_FUNCTION_NVX_EXT: return VK_OBJECT_TYPE_CU_FUNCTION_NVX; - case VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR_EXT: return VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR; - case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA_EXT: return VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA; - default: - return VK_OBJECT_TYPE_UNKNOWN; - } -} extern "C" { VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { - printf("Inside vkCreateInstance\n"); std::call_once(g_perfetto_init_flag, []() { InitializeDebugMarkerPerfetto(); }); // Get the function pointer @@ -109,8 +63,6 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCre // Eagerly enumerate physical devices and map them to the instance. // This ensures we have the mapping even if the app bypasses our enumeration hooks. - // Disabled for testing to avoid deadlock. - /* PFN_vkEnumeratePhysicalDevices fpEnumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices)fpGetInstanceProcAddr(*pInstance, "vkEnumeratePhysicalDevices"); if (fpEnumeratePhysicalDevices) { uint32_t count = 0; @@ -123,7 +75,6 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCre } } } - */ } return result; @@ -262,113 +213,5 @@ EXPORT_FUNCTION VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionPropert return VK_SUCCESS; } -// Debug Marker Functions - -VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { - if (device_dispatch_table(commandBuffer)->CmdDebugMarkerBeginEXT) { - device_dispatch_table(commandBuffer)->CmdDebugMarkerBeginEXT(commandBuffer, pMarkerInfo); - } -} - -VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT(VkCommandBuffer commandBuffer) { - if (device_dispatch_table(commandBuffer)->CmdDebugMarkerEndEXT) { - device_dispatch_table(commandBuffer)->CmdDebugMarkerEndEXT(commandBuffer); - } -} - -VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { - if (device_dispatch_table(commandBuffer)->CmdDebugMarkerInsertEXT) { - device_dispatch_table(commandBuffer)->CmdDebugMarkerInsertEXT(commandBuffer, pMarkerInfo); - } -} - -VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo) { - DebugMarker::Get().AddDebugMarker((uint64_t)device, (int32_t)getVkObjectType(pNameInfo->objectType), pNameInfo->object, pNameInfo->pObjectName); - if (device_dispatch_table(device)->DebugMarkerSetObjectNameEXT) { - VkResult result = device_dispatch_table(device)->DebugMarkerSetObjectNameEXT(device, pNameInfo); - return result; - } - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo) { - if (device_dispatch_table(device)->DebugMarkerSetObjectTagEXT) { - return device_dispatch_table(device)->DebugMarkerSetObjectTagEXT(device, pTagInfo); - } - return VK_SUCCESS; -} - -// Debug Utils Functions - -VKAPI_ATTR void VKAPI_CALL vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { - if (device_dispatch_table(commandBuffer)->CmdBeginDebugUtilsLabelEXT) { - device_dispatch_table(commandBuffer)->CmdBeginDebugUtilsLabelEXT(commandBuffer, pLabelInfo); - } -} - -VKAPI_ATTR void VKAPI_CALL vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer) { - if (device_dispatch_table(commandBuffer)->CmdEndDebugUtilsLabelEXT) { - device_dispatch_table(commandBuffer)->CmdEndDebugUtilsLabelEXT(commandBuffer); - } -} - -VKAPI_ATTR void VKAPI_CALL vkCmdInsertDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { - if (device_dispatch_table(commandBuffer)->CmdInsertDebugUtilsLabelEXT) { - device_dispatch_table(commandBuffer)->CmdInsertDebugUtilsLabelEXT(commandBuffer, pLabelInfo); - } -} - -VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectNameEXT(VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo) { - DebugMarker::Get().AddDebugMarker((uint64_t)device, (int32_t)pNameInfo->objectType, pNameInfo->objectHandle, pNameInfo->pObjectName); - if (device_dispatch_table(device)->SetDebugUtilsObjectNameEXT) { - VkResult result = device_dispatch_table(device)->SetDebugUtilsObjectNameEXT(device, pNameInfo); - return result; - } - return VK_SUCCESS; -} - -VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectTagEXT(VkDevice device, const VkDebugUtilsObjectTagInfoEXT* pTagInfo) { - if (device_dispatch_table(device)->SetDebugUtilsObjectTagEXT) { - return device_dispatch_table(device)->SetDebugUtilsObjectTagEXT(device, pTagInfo); - } - return VK_SUCCESS; -} - -VKAPI_ATTR void VKAPI_CALL vkQueueBeginDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { - if (device_dispatch_table(queue)->QueueBeginDebugUtilsLabelEXT) { - device_dispatch_table(queue)->QueueBeginDebugUtilsLabelEXT(queue, pLabelInfo); - } -} - -VKAPI_ATTR void VKAPI_CALL vkQueueEndDebugUtilsLabelEXT(VkQueue queue) { - if (device_dispatch_table(queue)->QueueEndDebugUtilsLabelEXT) { - device_dispatch_table(queue)->QueueEndDebugUtilsLabelEXT(queue); - } -} - -VKAPI_ATTR void VKAPI_CALL vkQueueInsertDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { - if (device_dispatch_table(queue)->QueueInsertDebugUtilsLabelEXT) { - device_dispatch_table(queue)->QueueInsertDebugUtilsLabelEXT(queue, pLabelInfo); - } -} - -VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger) { - if (instance_dispatch_table(instance)->CreateDebugUtilsMessengerEXT) { - return instance_dispatch_table(instance)->CreateDebugUtilsMessengerEXT(instance, pCreateInfo, pAllocator, pMessenger); - } - return VK_SUCCESS; -} - -VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks* pAllocator) { - if (instance_dispatch_table(instance)->DestroyDebugUtilsMessengerEXT) { - instance_dispatch_table(instance)->DestroyDebugUtilsMessengerEXT(instance, messenger, pAllocator); - } -} - -VKAPI_ATTR void VKAPI_CALL vkSubmitDebugUtilsMessageEXT(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) { - if (instance_dispatch_table(instance)->SubmitDebugUtilsMessageEXT) { - instance_dispatch_table(instance)->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, pCallbackData); - } -} } // extern "C" diff --git a/layersvt/debug_marker_handwritten_functions_vk_ext_debug_marker.h b/layersvt/debug_marker_handwritten_functions_vk_ext_debug_marker.h new file mode 100644 index 0000000000..84db032b13 --- /dev/null +++ b/layersvt/debug_marker_handwritten_functions_vk_ext_debug_marker.h @@ -0,0 +1,117 @@ +/* Copyright (C) 2026 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include "vk_layer_table.h" +#include "debug_marker.h" + +// This file contains handwritten functions for the VK_EXT_debug_marker extension. +// We only actively implement vkDebugMarkerSetObjectNameEXT to track object names for Perfetto traces. +// All other functions are simple passthroughs required to be provided so that the layer +// can claim full support for the extension. + +// We need to remap objects using getVkObjectType because VK_EXT_debug_marker uses the legacy +// VkDebugReportObjectTypeEXT enum, while we store everything using the modern VkObjectType +// enum to be consistent with VK_EXT_debug_utils and standard trace packets. +static inline VkObjectType getVkObjectType(VkDebugReportObjectTypeEXT vk_debug_report_object_type) { + switch (vk_debug_report_object_type) { + case VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT: return VK_OBJECT_TYPE_UNKNOWN; + case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return VK_OBJECT_TYPE_INSTANCE; + case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return VK_OBJECT_TYPE_PHYSICAL_DEVICE; + case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return VK_OBJECT_TYPE_DEVICE; + case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return VK_OBJECT_TYPE_QUEUE; + case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return VK_OBJECT_TYPE_SEMAPHORE; + case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return VK_OBJECT_TYPE_COMMAND_BUFFER; + case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return VK_OBJECT_TYPE_FENCE; + case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return VK_OBJECT_TYPE_DEVICE_MEMORY; + case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return VK_OBJECT_TYPE_BUFFER; + case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return VK_OBJECT_TYPE_IMAGE; + case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return VK_OBJECT_TYPE_EVENT; + case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return VK_OBJECT_TYPE_QUERY_POOL; + case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return VK_OBJECT_TYPE_BUFFER_VIEW; + case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return VK_OBJECT_TYPE_IMAGE_VIEW; + case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return VK_OBJECT_TYPE_SHADER_MODULE; + case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return VK_OBJECT_TYPE_PIPELINE_CACHE; + case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return VK_OBJECT_TYPE_PIPELINE_LAYOUT; + case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return VK_OBJECT_TYPE_RENDER_PASS; + case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return VK_OBJECT_TYPE_PIPELINE; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT; + case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return VK_OBJECT_TYPE_SAMPLER; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_POOL; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_SET; + case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return VK_OBJECT_TYPE_FRAMEBUFFER; + case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return VK_OBJECT_TYPE_COMMAND_POOL; + case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return VK_OBJECT_TYPE_SURFACE_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return VK_OBJECT_TYPE_SWAPCHAIN_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT: return VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT; + case VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT: return VK_OBJECT_TYPE_DISPLAY_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT: return VK_OBJECT_TYPE_DISPLAY_MODE_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT: return VK_OBJECT_TYPE_VALIDATION_CACHE_EXT; + case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT: return VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION; + case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT: return VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE; + case VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT: return VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV; + case VK_DEBUG_REPORT_OBJECT_TYPE_CU_MODULE_NVX_EXT: return VK_OBJECT_TYPE_CU_MODULE_NVX; + case VK_DEBUG_REPORT_OBJECT_TYPE_CU_FUNCTION_NVX_EXT: return VK_OBJECT_TYPE_CU_FUNCTION_NVX; + case VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR_EXT: return VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR; + case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA_EXT: return VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA; + default: + return VK_OBJECT_TYPE_UNKNOWN; + } +} + +extern "C" { + +// Required for VK_EXT_debug_marker +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { + if (device_dispatch_table(commandBuffer)->CmdDebugMarkerBeginEXT) { + device_dispatch_table(commandBuffer)->CmdDebugMarkerBeginEXT(commandBuffer, pMarkerInfo); + } +} + +// Required for VK_EXT_debug_marker +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT(VkCommandBuffer commandBuffer) { + if (device_dispatch_table(commandBuffer)->CmdDebugMarkerEndEXT) { + device_dispatch_table(commandBuffer)->CmdDebugMarkerEndEXT(commandBuffer); + } +} + +// Required for VK_EXT_debug_marker +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { + if (device_dispatch_table(commandBuffer)->CmdDebugMarkerInsertEXT) { + device_dispatch_table(commandBuffer)->CmdDebugMarkerInsertEXT(commandBuffer, pMarkerInfo); + } +} + +// Required for VK_EXT_debug_marker. Tracks object name state. +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo) { + DebugMarker::Get().SetDebugObjectName((uint64_t)device, (int32_t)getVkObjectType(pNameInfo->objectType), pNameInfo->object, pNameInfo->pObjectName); + if (device_dispatch_table(device)->DebugMarkerSetObjectNameEXT) { + VkResult result = device_dispatch_table(device)->DebugMarkerSetObjectNameEXT(device, pNameInfo); + return result; + } + return VK_SUCCESS; +} + +// Required for VK_EXT_debug_marker +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo) { + if (device_dispatch_table(device)->DebugMarkerSetObjectTagEXT) { + return device_dispatch_table(device)->DebugMarkerSetObjectTagEXT(device, pTagInfo); + } + return VK_SUCCESS; +} + +} // extern "C" diff --git a/layersvt/debug_marker_handwritten_functions_vk_ext_debug_utils.h b/layersvt/debug_marker_handwritten_functions_vk_ext_debug_utils.h new file mode 100644 index 0000000000..591a6eba82 --- /dev/null +++ b/layersvt/debug_marker_handwritten_functions_vk_ext_debug_utils.h @@ -0,0 +1,111 @@ +/* Copyright (C) 2026 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include "vk_layer_table.h" +#include "debug_marker.h" + +extern "C" { + +// This file contains handwritten functions for the VK_EXT_debug_utils extension. +// We only actively implement vkSetDebugUtilsObjectNameEXT to track object names for Perfetto traces. +// All other functions are simple passthroughs required to be provided so that the layer +// can claim full support for the extension. + +// Required for VK_EXT_debug_utils +VKAPI_ATTR void VKAPI_CALL vkCmdBeginDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { + if (device_dispatch_table(commandBuffer)->CmdBeginDebugUtilsLabelEXT) { + device_dispatch_table(commandBuffer)->CmdBeginDebugUtilsLabelEXT(commandBuffer, pLabelInfo); + } +} + +// Required for VK_EXT_debug_utils +VKAPI_ATTR void VKAPI_CALL vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer) { + if (device_dispatch_table(commandBuffer)->CmdEndDebugUtilsLabelEXT) { + device_dispatch_table(commandBuffer)->CmdEndDebugUtilsLabelEXT(commandBuffer); + } +} + +// Required for VK_EXT_debug_utils +VKAPI_ATTR void VKAPI_CALL vkCmdInsertDebugUtilsLabelEXT(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo) { + if (device_dispatch_table(commandBuffer)->CmdInsertDebugUtilsLabelEXT) { + device_dispatch_table(commandBuffer)->CmdInsertDebugUtilsLabelEXT(commandBuffer, pLabelInfo); + } +} + +// Required for VK_EXT_debug_utils. Tracks object name state. +VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectNameEXT(VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo) { + DebugMarker::Get().SetDebugObjectName((uint64_t)device, (int32_t)pNameInfo->objectType, pNameInfo->objectHandle, pNameInfo->pObjectName); + if (device_dispatch_table(device)->SetDebugUtilsObjectNameEXT) { + VkResult result = device_dispatch_table(device)->SetDebugUtilsObjectNameEXT(device, pNameInfo); + return result; + } + return VK_SUCCESS; +} + +// Required for VK_EXT_debug_utils +VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectTagEXT(VkDevice device, const VkDebugUtilsObjectTagInfoEXT* pTagInfo) { + if (device_dispatch_table(device)->SetDebugUtilsObjectTagEXT) { + return device_dispatch_table(device)->SetDebugUtilsObjectTagEXT(device, pTagInfo); + } + return VK_SUCCESS; +} + +// Required for VK_EXT_debug_utils +VKAPI_ATTR void VKAPI_CALL vkQueueBeginDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { + if (device_dispatch_table(queue)->QueueBeginDebugUtilsLabelEXT) { + device_dispatch_table(queue)->QueueBeginDebugUtilsLabelEXT(queue, pLabelInfo); + } +} + +// Required for VK_EXT_debug_utils +VKAPI_ATTR void VKAPI_CALL vkQueueEndDebugUtilsLabelEXT(VkQueue queue) { + if (device_dispatch_table(queue)->QueueEndDebugUtilsLabelEXT) { + device_dispatch_table(queue)->QueueEndDebugUtilsLabelEXT(queue); + } +} + +// Passthrough required for VK_EXT_debug_utils +VKAPI_ATTR void VKAPI_CALL vkQueueInsertDebugUtilsLabelEXT(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo) { + if (device_dispatch_table(queue)->QueueInsertDebugUtilsLabelEXT) { + device_dispatch_table(queue)->QueueInsertDebugUtilsLabelEXT(queue, pLabelInfo); + } +} + +// Passthrough required for VK_EXT_debug_utils +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger) { + if (instance_dispatch_table(instance)->CreateDebugUtilsMessengerEXT) { + return instance_dispatch_table(instance)->CreateDebugUtilsMessengerEXT(instance, pCreateInfo, pAllocator, pMessenger); + } + return VK_SUCCESS; +} + +// Passthrough required for VK_EXT_debug_utils +VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks* pAllocator) { + if (instance_dispatch_table(instance)->DestroyDebugUtilsMessengerEXT) { + instance_dispatch_table(instance)->DestroyDebugUtilsMessengerEXT(instance, messenger, pAllocator); + } +} + +// Passthrough required for VK_EXT_debug_utils +VKAPI_ATTR void VKAPI_CALL vkSubmitDebugUtilsMessageEXT(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) { + if (instance_dispatch_table(instance)->SubmitDebugUtilsMessageEXT) { + instance_dispatch_table(instance)->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, pCallbackData); + } +} + +} // extern "C" diff --git a/layersvt/test/test_debugmarker.cpp b/layersvt/test/test_debugmarker.cpp index 4f159e3f76..c3598f137c 100644 --- a/layersvt/test/test_debugmarker.cpp +++ b/layersvt/test/test_debugmarker.cpp @@ -56,3 +56,18 @@ TEST_F(DebugMarkerTests, CombinedTest) { } EXPECT_NE(phys_dev, nullptr); } + +#if defined(__linux__) && !defined(__ANDROID__) +TEST_F(DebugMarkerTests, SetDebugObjectNameStdout) { + TEST_DESCRIPTION("Test SetDebugObjectName output to stdout on Linux"); + + DebugMarker::Get().Clear(); + + testing::internal::CaptureStdout(); + DebugMarker::Get().SetDebugObjectName(0x1234, VK_OBJECT_TYPE_DEVICE, 0x5678, "TestObject"); + std::string output = testing::internal::GetCapturedStdout(); + + EXPECT_NE(output.find("VulkanDebugMarker SetDebugObjectName:"), std::string::npos); + EXPECT_NE(output.find("TestObject"), std::string::npos); +} +#endif From da7199de1480dccc2fd34bd908de365ae1e0ece5 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Mon, 13 Apr 2026 15:54:07 +0100 Subject: [PATCH 04/12] Add layersvt/debug_marker_handwritten_functions.h description --- layersvt/debug_marker_handwritten_functions.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/layersvt/debug_marker_handwritten_functions.h b/layersvt/debug_marker_handwritten_functions.h index 460f0bbbfa..7630a85972 100644 --- a/layersvt/debug_marker_handwritten_functions.h +++ b/layersvt/debug_marker_handwritten_functions.h @@ -24,7 +24,17 @@ #include "debug_marker.h" #include "debug_marker_perfetto.h" - +// This file contains handwritten implementations for core Vulkan functions +// (instance/device creation and physical device enumeration) required for the layer's +// infrastructure and state management: +// +// - vkCreateInstance: Initializes Perfetto tracing, the instance dispatch table, and performs eager physical device enumeration. +// - vkEnumeratePhysicalDevices / vkEnumeratePhysicalDeviceGroups: Tracks the mapping +// between physical devices and instances to support dispatch table lookups. +// - vkCreateDevice: Initializes the device dispatch table for intercepted devices. +// +// Extension-specific functions (e.g., VK_EXT_debug_marker, VK_EXT_debug_utils) +// are located in separate dedicated header files. #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) From 56528435919abc5613b553fa425e14f3232ca7a2 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Mon, 13 Apr 2026 15:58:11 +0100 Subject: [PATCH 05/12] some refactoring --- layersvt/debug_marker.cpp | 10 +++++----- layersvt/debug_marker.h | 9 ++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/layersvt/debug_marker.cpp b/layersvt/debug_marker.cpp index 331825718a..bb99bba1c1 100644 --- a/layersvt/debug_marker.cpp +++ b/layersvt/debug_marker.cpp @@ -24,19 +24,19 @@ DebugMarker& DebugMarker::Get() { } void DebugMarker::SetVkInstance(VkPhysicalDevice phys_dev, VkInstance instance) { - std::lock_guard lock(map_mutex_); + std::lock_guard lock(mutex_); vk_instance_map_[phys_dev] = instance; } VkInstance DebugMarker::GetVkInstance(VkPhysicalDevice phys_dev) { - std::lock_guard lock(map_mutex_); + std::lock_guard lock(mutex_); auto it = vk_instance_map_.find(phys_dev); if (it != vk_instance_map_.end()) return it->second; return VK_NULL_HANDLE; } void DebugMarker::SetDebugObjectName(uint64_t device, int32_t type, uint64_t handle, const char* name) { - std::lock_guard lock(map_mutex_); + std::lock_guard lock(mutex_); std::string name_str = name ? name : "NULL"; debug_object_names_[std::make_pair(type, handle)] = DebugObjectName(device, type, handle, name_str); @@ -57,7 +57,7 @@ void DebugMarker::SetDebugObjectName(uint64_t device, int32_t type, uint64_t han } void DebugMarker::EmitAllDebugMarkers() { - std::lock_guard lock(map_mutex_); + std::lock_guard lock(mutex_); for (const auto& entry : debug_object_names_) { const auto& marker = entry.second; uint64_t device = marker.vk_device; @@ -77,7 +77,7 @@ void DebugMarker::EmitAllDebugMarkers() { } void DebugMarker::Clear() { - std::lock_guard lock(map_mutex_); + std::lock_guard lock(mutex_); vk_instance_map_.clear(); debug_object_names_.clear(); } diff --git a/layersvt/debug_marker.h b/layersvt/debug_marker.h index 435cd0e5f5..446fc983ce 100644 --- a/layersvt/debug_marker.h +++ b/layersvt/debug_marker.h @@ -84,7 +84,14 @@ class DebugMarker { : vk_device(dev), object_type(type), handle(h), name(n) {} }; - std::mutex map_mutex_; + std::mutex mutex_; + /** + * @brief Maps a physical device handle to its corresponding Vulkan instance handle. + */ std::unordered_map 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, DebugObjectName> debug_object_names_; }; From ff58ea58dc5b683b6efc1c56a6b3e178d04b930a Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Mon, 13 Apr 2026 16:09:46 +0100 Subject: [PATCH 06/12] Improve test --- layersvt/debug_marker.cpp | 13 +++++++------ layersvt/debug_marker.h | 5 +++++ layersvt/test/test_debugmarker.cpp | 29 ++++++++++------------------- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/layersvt/debug_marker.cpp b/layersvt/debug_marker.cpp index bb99bba1c1..46b964e800 100644 --- a/layersvt/debug_marker.cpp +++ b/layersvt/debug_marker.cpp @@ -16,7 +16,6 @@ #include "debug_marker.h" #include "debug_marker_perfetto.h" #include "perfetto/perfetto.h" -#include DebugMarker& DebugMarker::Get() { static DebugMarker instance; @@ -41,10 +40,6 @@ void DebugMarker::SetDebugObjectName(uint64_t device, int32_t type, uint64_t han std::string name_str = name ? name : "NULL"; debug_object_names_[std::make_pair(type, handle)] = DebugObjectName(device, type, handle, name_str); -#if defined(__linux__) && !defined(__ANDROID__) - printf("VulkanDebugMarker SetDebugObjectName: device=%llu, type=%d, handle=%llu, name=%s\n", - (unsigned long long)device, type, (unsigned long long)handle, name_str.c_str()); -#else perfetto::TrackEvent::Trace([device, type, handle, name_str](perfetto::TrackEvent::TraceContext ctx) { auto packet = ctx.NewTracePacket(); auto event = packet->set_vulkan_api_event()->set_vk_debug_utils_object_name(); @@ -53,7 +48,6 @@ void DebugMarker::SetDebugObjectName(uint64_t device, int32_t type, uint64_t han event->set_object(handle); event->set_object_name(name_str.c_str()); }); -#endif } void DebugMarker::EmitAllDebugMarkers() { @@ -81,3 +75,10 @@ void DebugMarker::Clear() { vk_instance_map_.clear(); debug_object_names_.clear(); } + +bool DebugMarker::HasDebugObjectName(int32_t type, uint64_t handle, const std::string& name) { + std::lock_guard 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; +} diff --git a/layersvt/debug_marker.h b/layersvt/debug_marker.h index 446fc983ce..b01b62e375 100644 --- a/layersvt/debug_marker.h +++ b/layersvt/debug_marker.h @@ -56,6 +56,11 @@ class DebugMarker { * @brief Clears all stored debug markers and instance mappings. */ void Clear(); + + /** + * @brief Checks if a debug name is stored for a given object. + */ + bool HasDebugObjectName(int32_t type, uint64_t handle, const std::string& name); /** * @brief Associates a Vulkan physical device with its corresponding instance. diff --git a/layersvt/test/test_debugmarker.cpp b/layersvt/test/test_debugmarker.cpp index c3598f137c..4c21293bee 100644 --- a/layersvt/test/test_debugmarker.cpp +++ b/layersvt/test/test_debugmarker.cpp @@ -48,26 +48,17 @@ TEST_F(DebugMarkerTests, CombinedTest) { PFN_vkVoidFunction pfnCmdDebugMarkerBeginEXT = vkGetInstanceProcAddr(instance, "vkCmdDebugMarkerBeginEXT"); EXPECT_NE(pfnCmdDebugMarkerBeginEXT, nullptr); - VkPhysicalDevice phys_dev; - err = inst_builder.GetPhysicalDevice(&phys_dev); - if (err != VK_SUCCESS) { - GTEST_SKIP() << "No physical devices found, skipping physical device test."; - return; - } - EXPECT_NE(phys_dev, nullptr); -} + // 1. Set instance name + DebugMarker::Get().SetDebugObjectName(0, VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstance"); -#if defined(__linux__) && !defined(__ANDROID__) -TEST_F(DebugMarkerTests, SetDebugObjectNameStdout) { - TEST_DESCRIPTION("Test SetDebugObjectName output to stdout on Linux"); + EXPECT_TRUE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstance")); - DebugMarker::Get().Clear(); - - testing::internal::CaptureStdout(); - DebugMarker::Get().SetDebugObjectName(0x1234, VK_OBJECT_TYPE_DEVICE, 0x5678, "TestObject"); - std::string output = testing::internal::GetCapturedStdout(); + // 2. Override new name + DebugMarker::Get().SetDebugObjectName(0, VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstanceRenamed"); + EXPECT_TRUE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstanceRenamed")); + EXPECT_FALSE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstance")); - EXPECT_NE(output.find("VulkanDebugMarker SetDebugObjectName:"), std::string::npos); - EXPECT_NE(output.find("TestObject"), std::string::npos); + // 3. Clear + DebugMarker::Get().Clear(); + EXPECT_FALSE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstanceRenamed")); } -#endif From 22c6355048f9b20a69e9f58545c9a570a0e48434 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Mon, 13 Apr 2026 16:25:12 +0100 Subject: [PATCH 07/12] Update debug_marker.h comment --- layersvt/debug_marker.h | 27 ++++++++++++++++++++++++--- layersvt/debug_marker_perfetto.cpp | 3 +-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/layersvt/debug_marker.h b/layersvt/debug_marker.h index b01b62e375..45f5db0734 100644 --- a/layersvt/debug_marker.h +++ b/layersvt/debug_marker.h @@ -22,11 +22,32 @@ #include #include - - /** * The DebugMarker class is responsible for storing and managing debug marker - * information associated with Vulkan objects. + * information associated with Vulkan objects and emitting them to Perfetto traces. + * + * Currently, it primarily supports tracking and emitting object names. + * + * How it works: + * We do not store a history of events for setting object names. Instead, we store + * only the current name for each object (one name per object). This keeps the memory + * footprint small for most applications (proportional to the name size multiplied by + * the number of unique objects). + * + * Perfetto Session Support: + * This solution supports: + * - Starting a Perfetto session before the application starts. + * - Starting a Perfetto session after the application is already running. + * - Running multiple Perfetto sessions during a single application run. + * + * To support late-attach and multiple sessions, when a Perfetto session starts, + * we write all currently known object names to the trace. We retain the names in memory + * 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. */ diff --git a/layersvt/debug_marker_perfetto.cpp b/layersvt/debug_marker_perfetto.cpp index 0f1f65fe99..c378814bca 100644 --- a/layersvt/debug_marker_perfetto.cpp +++ b/layersvt/debug_marker_perfetto.cpp @@ -15,6 +15,7 @@ #include "debug_marker_perfetto.h" #include "debug_marker.h" +#include PERFETTO_TRACK_EVENT_STATIC_STORAGE(); @@ -27,8 +28,6 @@ class MarkerSessionObserver : public perfetto::TrackEventSessionObserver { static MarkerSessionObserver g_marker_session_observer; -#include - void InitializeDebugMarkerPerfetto() { static std::once_flag flag; std::call_once(flag, []() { From 3158b87a2e3d728fad55a41205eb352484a5e444 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Mon, 13 Apr 2026 16:44:50 +0100 Subject: [PATCH 08/12] Removed NODEVICE_SELECT=1 --- layersvt/test/CMakeLists.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/layersvt/test/CMakeLists.txt b/layersvt/test/CMakeLists.txt index 6ac8630f9f..d1c2e3761d 100644 --- a/layersvt/test/CMakeLists.txt +++ b/layersvt/test/CMakeLists.txt @@ -40,16 +40,6 @@ function(LayerTest NAME) target_compile_definitions(${TEST_NAME} PUBLIC LAYER_BINARY_PATH="$") add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) - if (${NAME} STREQUAL "DebugMarker") - set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT - "VK_LAYER_PATH=$;NODEVICE_SELECT=1" - ) - else() - set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT - "VK_LAYER_PATH=$" - ) - endif() - set_target_properties(${TEST_NAME} PROPERTIES FOLDER "layers/${NAME}/Test") if(WIN32 AND (QT_TARGET_TYPE STREQUAL STATIC_LIBRARY)) From f83bf3c38d1031c97c551f68e963f7e3c403f610 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Mon, 13 Apr 2026 16:48:35 +0100 Subject: [PATCH 09/12] clean up --- layersvt/debug_marker_perfetto.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/layersvt/debug_marker_perfetto.cpp b/layersvt/debug_marker_perfetto.cpp index c378814bca..42ec65775a 100644 --- a/layersvt/debug_marker_perfetto.cpp +++ b/layersvt/debug_marker_perfetto.cpp @@ -15,7 +15,6 @@ #include "debug_marker_perfetto.h" #include "debug_marker.h" -#include PERFETTO_TRACK_EVENT_STATIC_STORAGE(); @@ -29,13 +28,10 @@ class MarkerSessionObserver : public perfetto::TrackEventSessionObserver { static MarkerSessionObserver g_marker_session_observer; void InitializeDebugMarkerPerfetto() { - static std::once_flag flag; - std::call_once(flag, []() { - perfetto::TracingInitArgs args; - args.backends = perfetto::kSystemBackend; + perfetto::TracingInitArgs args; + args.backends = perfetto::kSystemBackend; - perfetto::Tracing::Initialize(args); - perfetto::TrackEvent::Register(); - perfetto::TrackEvent::AddSessionObserver(&g_marker_session_observer); - }); + perfetto::Tracing::Initialize(args); + perfetto::TrackEvent::Register(); + perfetto::TrackEvent::AddSessionObserver(&g_marker_session_observer); } From 001d107603c8cbba63c519bf25b0b9676b3b2ff9 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Tue, 14 Apr 2026 12:44:16 +0100 Subject: [PATCH 10/12] Explicity packet time is required for perfetto::TrackEvent::Trace. They are automatically added only for macros TRACE_EVENT --- layersvt/debug_marker.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/layersvt/debug_marker.cpp b/layersvt/debug_marker.cpp index 46b964e800..6f21ff4795 100644 --- a/layersvt/debug_marker.cpp +++ b/layersvt/debug_marker.cpp @@ -42,6 +42,7 @@ void DebugMarker::SetDebugObjectName(uint64_t device, int32_t type, uint64_t han perfetto::TrackEvent::Trace([device, type, handle, name_str](perfetto::TrackEvent::TraceContext ctx) { auto packet = ctx.NewTracePacket(); + packet->set_timestamp(perfetto::base::GetBootTimeNs().count()); auto event = packet->set_vulkan_api_event()->set_vk_debug_utils_object_name(); event->set_vk_device(device); event->set_object_type(type); @@ -61,6 +62,7 @@ void DebugMarker::EmitAllDebugMarkers() { perfetto::TrackEvent::Trace([device, type, handle, name_str](perfetto::TrackEvent::TraceContext ctx) { auto packet = ctx.NewTracePacket(); + packet->set_timestamp(perfetto::base::GetBootTimeNs().count()); auto event = packet->set_vulkan_api_event()->set_vk_debug_utils_object_name(); event->set_vk_device(device); event->set_object_type(type); From e00b9a228f7f01357be83c8cd28f6fb50953d878 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Tue, 14 Apr 2026 14:10:27 +0100 Subject: [PATCH 11/12] debug_marker add vkDestroyInstance for clean up --- layersvt/debug_marker_handwritten_dispatch.cpp | 10 ++++++++-- layersvt/debug_marker_handwritten_functions.h | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/layersvt/debug_marker_handwritten_dispatch.cpp b/layersvt/debug_marker_handwritten_dispatch.cpp index 59984d2b80..560321eeb6 100644 --- a/layersvt/debug_marker_handwritten_dispatch.cpp +++ b/layersvt/debug_marker_handwritten_dispatch.cpp @@ -23,6 +23,7 @@ extern "C" { static PFN_vkVoidFunction debug_marker_known_instance_functions(const char* pName) { if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast(vkCreateInstance); + if (strcmp(pName, "vkDestroyInstance") == 0) return reinterpret_cast(vkDestroyInstance); if (strcmp(pName, "vkEnumeratePhysicalDevices") == 0) return reinterpret_cast(vkEnumeratePhysicalDevices); if (strcmp(pName, "vkEnumeratePhysicalDeviceGroups") == 0) return reinterpret_cast(vkEnumeratePhysicalDeviceGroups); if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast(vkEnumerateInstanceExtensionProperties); @@ -74,11 +75,16 @@ EXPORT_FUNCTION VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(V return nullptr; } - if (instance_dispatch_table(instance)->GetInstanceProcAddr == NULL) { + auto table = instance_dispatch_table(instance); + if (table == NULL) { return nullptr; } - return instance_dispatch_table(instance)->GetInstanceProcAddr(instance, pName); + if (table->GetInstanceProcAddr == NULL) { + return nullptr; + } + + return table->GetInstanceProcAddr(instance, pName); } EXPORT_FUNCTION VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char* pName) { diff --git a/layersvt/debug_marker_handwritten_functions.h b/layersvt/debug_marker_handwritten_functions.h index 7630a85972..7f2dd82a11 100644 --- a/layersvt/debug_marker_handwritten_functions.h +++ b/layersvt/debug_marker_handwritten_functions.h @@ -122,6 +122,12 @@ VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroups(VkInstance instan return result; } +VKAPI_ATTR void VKAPI_CALL vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) { + dispatch_key key = get_dispatch_key(instance); + instance_dispatch_table(instance)->DestroyInstance(instance, pAllocator); + destroy_instance_dispatch_table(key); +} + VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { // Get the function pointer From a7a196720a8b4fa91e23460ee21c7bea04f7fa68 Mon Sep 17 00:00:00 2001 From: Oleh Kuznetsov Date: Wed, 15 Apr 2026 13:18:38 +0100 Subject: [PATCH 12/12] add "test only" note to Clear and HasDebugObjectName --- layersvt/debug_marker.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/layersvt/debug_marker.h b/layersvt/debug_marker.h index 45f5db0734..b705cefbfb 100644 --- a/layersvt/debug_marker.h +++ b/layersvt/debug_marker.h @@ -75,11 +75,13 @@ class DebugMarker { /** * @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);