diff --git a/CMakeLists.txt b/CMakeLists.txt index b203849458..04cc890982 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..913426f70d 100644 --- a/layersvt/CMakeLists.txt +++ b/layersvt/CMakeLists.txt @@ -154,11 +154,43 @@ 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_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 + 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..6f21ff4795 --- /dev/null +++ b/layersvt/debug_marker.cpp @@ -0,0 +1,86 @@ +/* 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(mutex_); + vk_instance_map_[phys_dev] = instance; +} + +VkInstance DebugMarker::GetVkInstance(VkPhysicalDevice phys_dev) { + 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(mutex_); + + std::string name_str = name ? name : "NULL"; + debug_object_names_[std::make_pair(type, handle)] = DebugObjectName(device, type, handle, name_str); + + 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(mutex_); + for (const auto& entry : debug_object_names_) { + 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()); + }); + } +} + +void DebugMarker::Clear() { + std::lock_guard lock(mutex_); + vk_instance_map_.clear(); + debug_object_names_.clear(); +} + +bool DebugMarker::HasDebugObjectName(int32_t type, uint64_t handle, const std::string& name) { + std::lock_guard 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 new file mode 100644 index 0000000000..b705cefbfb --- /dev/null +++ b/layersvt/debug_marker.h @@ -0,0 +1,125 @@ +/* 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 + +/** + * The DebugMarker class is responsible for storing and managing debug marker + * 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. + */ +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. + * @note This function is for testing only. + */ + void Clear(); + + /** + * @brief Checks if a debug name is stored for a given object. + * @note This function is for testing only. + */ + bool HasDebugObjectName(int32_t type, uint64_t handle, const std::string& name); + + /** + * @brief Associates a Vulkan physical device with its corresponding instance. + * @param phys_dev The Vulkan physical device. + * @param instance The Vulkan instance. + */ + 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); + + + 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 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_; +}; diff --git a/layersvt/debug_marker_handwritten_dispatch.cpp b/layersvt/debug_marker_handwritten_dispatch.cpp new file mode 100644 index 0000000000..560321eeb6 --- /dev/null +++ b/layersvt/debug_marker_handwritten_dispatch.cpp @@ -0,0 +1,107 @@ +/* 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 "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 + +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); + 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); + + // 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); + + // 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); + 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; + } + + auto table = instance_dispatch_table(instance); + if (table == NULL) { + return nullptr; + } + + 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) { + 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..7f2dd82a11 --- /dev/null +++ b/layersvt/debug_marker_handwritten_functions.h @@ -0,0 +1,233 @@ +/* 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" + +// 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])) + +#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; + + +extern "C" { + +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; +} + +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; +} + +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; +} + +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 + 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; +} + + +} // 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/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" + } + ] + } +} diff --git a/layersvt/test/CMakeLists.txt b/layersvt/test/CMakeLists.txt index 999d475010..d1c2e3761d 100644 --- a/layersvt/test/CMakeLists.txt +++ b/layersvt/test/CMakeLists.txt @@ -34,13 +34,12 @@ 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=$" - ) - set_target_properties(${TEST_NAME} PROPERTIES FOLDER "layers/${NAME}/Test") if(WIN32 AND (QT_TARGET_TYPE STREQUAL STATIC_LIBRARY)) @@ -48,7 +47,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..4c21293bee --- /dev/null +++ b/layersvt/test/test_debugmarker.cpp @@ -0,0 +1,64 @@ +/* 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); + + // 1. Set instance name + DebugMarker::Get().SetDebugObjectName(0, VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstance"); + + EXPECT_TRUE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstance")); + + // 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")); + + // 3. Clear + DebugMarker::Get().Clear(); + EXPECT_FALSE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstanceRenamed")); +}