diff --git a/layersvt/CMakeLists.txt b/layersvt/CMakeLists.txt index 30e43d02b9..99241a66bd 100644 --- a/layersvt/CMakeLists.txt +++ b/layersvt/CMakeLists.txt @@ -169,18 +169,15 @@ if(BUILD_DEBUGMARKER) add_library(VkLayer_DebugMarker MODULE) set_target_properties(VkLayer_DebugMarker PROPERTIES FOLDER "layers/debugmarker") target_sources(VkLayer_DebugMarker PRIVATE - debug_marker/debug_marker_handwritten_dispatch.cpp - debug_marker/debug_marker_handwritten_functions.h + $ debug_marker/debug_marker_handwritten_functions_vk_ext_debug_marker.h debug_marker/debug_marker_handwritten_functions_vk_ext_debug_utils.h debug_marker/debug_marker.h + debug_marker/debug_marker_instance.cpp debug_marker/debug_marker.cpp debug_marker/debug_marker_perfetto.h debug_marker/debug_marker_perfetto.cpp perfetto/perfetto.cc - vk_layer_table.cpp - vk_layer_table.h - common/layer_keep_alive.cpp debug_marker/VkLayer_DebugMarker.json.in ) @@ -190,6 +187,8 @@ if(BUILD_DEBUGMARKER) ${CMAKE_CURRENT_BINARY_DIR} ) + target_link_libraries(VkLayer_DebugMarker PRIVATE layersvt_common) + if(CMAKE_SYSTEM_NAME MATCHES "Linux|BSD|DragonFly|GNU") if (BUILD_WSI_XCB_SUPPORT) target_compile_definitions(VkLayer_DebugMarker PRIVATE VK_USE_PLATFORM_XLIB_KHR) diff --git a/layersvt/debug_marker/VkLayer_DebugMarker.def b/layersvt/debug_marker/VkLayer_DebugMarker.def index a00a872d02..8f69252213 100644 --- a/layersvt/debug_marker/VkLayer_DebugMarker.def +++ b/layersvt/debug_marker/VkLayer_DebugMarker.def @@ -18,3 +18,5 @@ vkGetInstanceProcAddr vkGetDeviceProcAddr vkEnumerateInstanceLayerProperties vkEnumerateInstanceExtensionProperties +vkEnumerateDeviceLayerProperties +vkEnumerateDeviceExtensionProperties diff --git a/layersvt/debug_marker/debug_marker.cpp b/layersvt/debug_marker/debug_marker.cpp index 6f21ff4795..11bf435ad2 100644 --- a/layersvt/debug_marker/debug_marker.cpp +++ b/layersvt/debug_marker/debug_marker.cpp @@ -15,23 +15,62 @@ #include "debug_marker.h" #include "debug_marker_perfetto.h" +#include "debug_marker_handwritten_functions_vk_ext_debug_marker.h" +#include "debug_marker_handwritten_functions_vk_ext_debug_utils.h" #include "perfetto/perfetto.h" +#include -DebugMarker& DebugMarker::Get() { - static DebugMarker instance; - return instance; +DebugMarker::DebugMarker() = default; + +const layersvt::LayerManifest* DebugMarker::GetLayerManifest() const { + static const layersvt::LayerManifest manifest{ + .layer_name = "VK_LAYER_GOOGLE_DebugMarker", + .description = "layer: DebugMarker", + .spec_version = VK_MAKE_VERSION(1, 4, VK_HEADER_VERSION), + .implementation_version = VK_MAKE_VERSION(0, 1, 0), + .instance_extensions = + { + {VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_UTILS_SPEC_VERSION}, + }, + .device_extensions = + { + {VK_EXT_DEBUG_MARKER_EXTENSION_NAME, VK_EXT_DEBUG_MARKER_SPEC_VERSION}, + }, + .tool_properties = std::nullopt, + }; + return &manifest; +} + +void DebugMarker::PreCreateInstance(VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator) { + (void)pCreateInfo; + (void)pAllocator; + static std::once_flag perfetto_initialization_flag; + std::call_once(perfetto_initialization_flag, []() { InitializeDebugMarkerPerfetto(); }); } -void DebugMarker::SetVkInstance(VkPhysicalDevice phys_dev, VkInstance instance) { +void DebugMarker::PreDestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) { + (void)pAllocator; std::lock_guard lock(mutex_); - vk_instance_map_[phys_dev] = instance; + for (auto it = emulated_messengers_.begin(); it != emulated_messengers_.end();) { + if (it->second.instance == instance) { + it = emulated_messengers_.erase(it); + } else { + ++it; + } + } } -VkInstance DebugMarker::GetVkInstance(VkPhysicalDevice phys_dev) { +void DebugMarker::PreDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) { + (void)pAllocator; std::lock_guard lock(mutex_); - auto it = vk_instance_map_.find(phys_dev); - if (it != vk_instance_map_.end()) return it->second; - return VK_NULL_HANDLE; + uint64_t dev_handle = (uint64_t)device; + for (auto it = debug_object_names_.begin(); it != debug_object_names_.end();) { + if (it->second.vk_device == dev_handle) { + it = debug_object_names_.erase(it); + } else { + ++it; + } + } } void DebugMarker::SetDebugObjectName(uint64_t device, int32_t type, uint64_t handle, const char* name) { @@ -72,15 +111,89 @@ void DebugMarker::EmitAllDebugMarkers() { } } -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; } + +VkResult DebugMarker::CreateEmulatedMessenger(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + VkDebugUtilsMessengerEXT* pMessenger) { + std::lock_guard lock(mutex_); + uint64_t handle = next_emulated_messenger_++; + EmulatedMessenger& messenger = emulated_messengers_[handle]; + messenger.instance = instance; + messenger.create_info = *pCreateInfo; + // The pNext chain is owned by the application and is not used by the emulation. + messenger.create_info.pNext = nullptr; + *pMessenger = (VkDebugUtilsMessengerEXT)handle; + return VK_SUCCESS; +} + +void DebugMarker::DestroyEmulatedMessenger(VkDebugUtilsMessengerEXT messenger) { + std::lock_guard lock(mutex_); + emulated_messengers_.erase((uint64_t)messenger); +} + +void DebugMarker::SubmitEmulatedMessage(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) { + std::vector matching; + { + std::lock_guard lock(mutex_); + for (const auto& entry : emulated_messengers_) { + const VkDebugUtilsMessengerCreateInfoEXT& info = entry.second.create_info; + if (entry.second.instance == instance && (info.messageSeverity & messageSeverity) != 0 && + (info.messageType & messageTypes) != 0) { + matching.push_back(info); + } + } + } + // Invoke callbacks without holding the lock so that they may call back into the layer. + for (const auto& info : matching) { + info.pfnUserCallback(messageSeverity, messageTypes, pCallbackData, info.pUserData); + } +} + +PFN_vkVoidFunction DebugMarker::GetLayerInstanceCommand(VkInstance instance, const char* name) { + (void)instance; + if (strcmp(name, "vkCreateDebugUtilsMessengerEXT") == 0) + return reinterpret_cast(vkCreateDebugUtilsMessengerEXT); + if (strcmp(name, "vkDestroyDebugUtilsMessengerEXT") == 0) + return reinterpret_cast(vkDestroyDebugUtilsMessengerEXT); + if (strcmp(name, "vkSubmitDebugUtilsMessageEXT") == 0) + return reinterpret_cast(vkSubmitDebugUtilsMessageEXT); + return nullptr; +} + +PFN_vkVoidFunction DebugMarker::GetLayerDeviceCommand(VkDevice device, const char* name) { + (void)device; + + // VK_EXT_debug_marker + if (strcmp(name, "vkCmdDebugMarkerBeginEXT") == 0) return reinterpret_cast(vkCmdDebugMarkerBeginEXT); + if (strcmp(name, "vkCmdDebugMarkerEndEXT") == 0) return reinterpret_cast(vkCmdDebugMarkerEndEXT); + if (strcmp(name, "vkCmdDebugMarkerInsertEXT") == 0) return reinterpret_cast(vkCmdDebugMarkerInsertEXT); + if (strcmp(name, "vkDebugMarkerSetObjectNameEXT") == 0) + return reinterpret_cast(vkDebugMarkerSetObjectNameEXT); + if (strcmp(name, "vkDebugMarkerSetObjectTagEXT") == 0) + return reinterpret_cast(vkDebugMarkerSetObjectTagEXT); + + // VK_EXT_debug_utils + if (strcmp(name, "vkCmdBeginDebugUtilsLabelEXT") == 0) + return reinterpret_cast(vkCmdBeginDebugUtilsLabelEXT); + if (strcmp(name, "vkCmdEndDebugUtilsLabelEXT") == 0) return reinterpret_cast(vkCmdEndDebugUtilsLabelEXT); + if (strcmp(name, "vkCmdInsertDebugUtilsLabelEXT") == 0) + return reinterpret_cast(vkCmdInsertDebugUtilsLabelEXT); + if (strcmp(name, "vkSetDebugUtilsObjectNameEXT") == 0) + return reinterpret_cast(vkSetDebugUtilsObjectNameEXT); + if (strcmp(name, "vkSetDebugUtilsObjectTagEXT") == 0) return reinterpret_cast(vkSetDebugUtilsObjectTagEXT); + if (strcmp(name, "vkQueueBeginDebugUtilsLabelEXT") == 0) + return reinterpret_cast(vkQueueBeginDebugUtilsLabelEXT); + if (strcmp(name, "vkQueueEndDebugUtilsLabelEXT") == 0) + return reinterpret_cast(vkQueueEndDebugUtilsLabelEXT); + if (strcmp(name, "vkQueueInsertDebugUtilsLabelEXT") == 0) + return reinterpret_cast(vkQueueInsertDebugUtilsLabelEXT); + + return nullptr; +} diff --git a/layersvt/debug_marker/debug_marker.h b/layersvt/debug_marker/debug_marker.h index b705cefbfb..05df9ce890 100644 --- a/layersvt/debug_marker/debug_marker.h +++ b/layersvt/debug_marker/debug_marker.h @@ -16,11 +16,15 @@ #pragma once #include +#include #include -#include #include #include +#include + +#include "common/layer_base.h" +#include "common/layer_manifest.h" /** * The DebugMarker class is responsible for storing and managing debug marker @@ -45,19 +49,17 @@ * because a user might start another Perfetto session later, requiring us to emit * all object names again. * - * A potential issue exists if an application constantly creates and destroys - * objects without bound, as we currently do not remove names for destroyed objects. - * Support for removing names on object destruction can be added later if needed. - * - * This class is a singleton and provides thread-safe access to its state. + * This class is a singleton, inherits from LayerBase, and provides thread-safe access to its state. */ -class DebugMarker { +class DebugMarker : public layersvt::LayerBase { public: - /** - * @brief Returns the singleton instance of the DebugMarker class. - * @return Reference to the DebugMarker singleton. - */ - static DebugMarker& Get(); + DebugMarker(); + ~DebugMarker() override = default; + + static DebugMarker& Get() { + assert(LayerBase::Get() != nullptr && "LayerBase instance must be initialized"); + return *static_cast(LayerBase::Get()); + } /** * @brief Sets or updates the name associated with a Vulkan object. @@ -67,18 +69,12 @@ class DebugMarker { * @param name The name to associate with the object. */ void SetDebugObjectName(uint64_t device, int32_t type, uint64_t handle, const char* name); - + /** * @brief Emits all stored debug markers to the tracing system. */ void EmitAllDebugMarkers(); - /** - * @brief Clears all stored debug markers and instance mappings. - * @note This function is for testing only. - */ - void Clear(); - /** * @brief Checks if a debug name is stored for a given object. * @note This function is for testing only. @@ -86,19 +82,37 @@ class DebugMarker { bool HasDebugObjectName(int32_t type, uint64_t handle, const std::string& name); /** - * @brief Associates a Vulkan physical device with its corresponding instance. - * @param phys_dev The Vulkan physical device. - * @param instance The Vulkan instance. + * @brief Emulated VK_EXT_debug_utils messengers, used for instances whose downstream lacks the + * extension so that the commands advertised by this layer remain functional. + */ + VkResult CreateEmulatedMessenger(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + VkDebugUtilsMessengerEXT* pMessenger); + void DestroyEmulatedMessenger(VkDebugUtilsMessengerEXT messenger); + void SubmitEmulatedMessage(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData); + + protected: + /** + * Lifecycle hook called before vkCreateInstance. + */ + void PreCreateInstance(VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator) override; + + /** + * Lifecycle hook called before vkDestroyInstance to remove the instance's emulated messengers. */ - void SetVkInstance(VkPhysicalDevice phys_dev, VkInstance instance); + void PreDestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) override; /** - * @brief Retrieves the Vulkan instance associated with a given physical device. - * @param phys_dev The Vulkan physical device. - * @return The associated Vulkan instance. + * Lifecycle hook called before vkDestroyDevice to remove tracked names for destroyed objects. */ - VkInstance GetVkInstance(VkPhysicalDevice phys_dev); + void PreDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) override; + const layersvt::LayerManifest* GetLayerManifest() const override; + // VK_EXT_debug_marker and VK_EXT_debug_utils are implemented by this layer, so their commands + // are returned unconditionally; the hooks tolerate a downstream that lacks the extension. + PFN_vkVoidFunction GetLayerInstanceCommand(VkInstance instance, const char* name) override; + PFN_vkVoidFunction GetLayerDeviceCommand(VkDevice device, const char* name) override; private: struct DebugObjectName { @@ -113,13 +127,21 @@ class DebugMarker { }; 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_; + + struct EmulatedMessenger { + VkInstance instance; + VkDebugUtilsMessengerCreateInfoEXT create_info; + }; + + /** + * @brief Maps an emulated messenger handle to its owning instance and creation parameters. + * Handles are unique non-zero counter values. + */ + std::map emulated_messengers_; + uint64_t next_emulated_messenger_ = 1; }; diff --git a/layersvt/debug_marker/debug_marker_handwritten_dispatch.cpp b/layersvt/debug_marker/debug_marker_handwritten_dispatch.cpp deleted file mode 100644 index a320032500..0000000000 --- a/layersvt/debug_marker/debug_marker_handwritten_dispatch.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* 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, "vkGetInstanceProcAddr") == 0) return reinterpret_cast(vkGetInstanceProcAddr); - 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, "vkGetDeviceProcAddr") == 0) return reinterpret_cast(vkGetDeviceProcAddr); - 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/debug_marker_handwritten_functions.h b/layersvt/debug_marker/debug_marker_handwritten_functions.h deleted file mode 100644 index 18fe280c05..0000000000 --- a/layersvt/debug_marker/debug_marker_handwritten_functions.h +++ /dev/null @@ -1,250 +0,0 @@ -/* 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 util_GetExtensionProperties(0, nullptr, pPropertyCount, pProperties); -} - -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) += ARRAY_SIZE(deviceExtensions); - } - 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; - uint32_t additionalCount = 0; - - for (uint32_t i = 0; i < ARRAY_SIZE(deviceExtensions); ++i) { - bool found = false; - for (uint32_t j = 0; j < originalCount; ++j) { - if (strcmp(pProperties[j].extensionName, deviceExtensions[i].extensionName) == 0) { - found = true; - break; - } - } - if (!found) { - if (originalCount + additionalCount < requestedCount) { - pProperties[originalCount + additionalCount] = deviceExtensions[i]; - } - additionalCount++; - } - } - *pPropertyCount = originalCount + additionalCount; - if (*pPropertyCount > requestedCount) { - *pPropertyCount = requestedCount; - } - } - return res; - } - return VK_SUCCESS; -} - - -} // extern "C" diff --git a/layersvt/debug_marker/debug_marker_handwritten_functions_vk_ext_debug_marker.h b/layersvt/debug_marker/debug_marker_handwritten_functions_vk_ext_debug_marker.h index 84db032b13..b5c7216963 100644 --- a/layersvt/debug_marker/debug_marker_handwritten_functions_vk_ext_debug_marker.h +++ b/layersvt/debug_marker/debug_marker_handwritten_functions_vk_ext_debug_marker.h @@ -16,13 +16,14 @@ #pragma once #include -#include "vk_layer_table.h" +#include "common/dispatch_downstream.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. +// can claim full support for the extension. Since the layer implements the extension itself, +// the downstream may lack it, so passthroughs skip downstream dispatch when it is unavailable. // 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 @@ -77,41 +78,30 @@ 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); - } + layersvt::DispatchDownstreamIfAvailable<&VkuDeviceDispatchTable::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); - } + layersvt::DispatchDownstreamIfAvailable<&VkuDeviceDispatchTable::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); - } + layersvt::DispatchDownstreamIfAvailable<&VkuDeviceDispatchTable::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; + assert(pNameInfo != nullptr); + DebugMarker::Get().SetDebugObjectName((uint64_t)device, (int32_t)getVkObjectType(pNameInfo->objectType), pNameInfo->object, + pNameInfo->pObjectName); + return layersvt::DispatchDownstreamOrSuccess<&VkuDeviceDispatchTable::DebugMarkerSetObjectNameEXT>(device, pNameInfo); } // 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; + return layersvt::DispatchDownstreamOrSuccess<&VkuDeviceDispatchTable::DebugMarkerSetObjectTagEXT>(device, pTagInfo); } -} // extern "C" +} // extern "C" diff --git a/layersvt/debug_marker/debug_marker_handwritten_functions_vk_ext_debug_utils.h b/layersvt/debug_marker/debug_marker_handwritten_functions_vk_ext_debug_utils.h index 591a6eba82..c4d3b57c83 100644 --- a/layersvt/debug_marker/debug_marker_handwritten_functions_vk_ext_debug_utils.h +++ b/layersvt/debug_marker/debug_marker_handwritten_functions_vk_ext_debug_utils.h @@ -16,7 +16,7 @@ #pragma once #include -#include "vk_layer_table.h" +#include "common/dispatch_downstream.h" #include "debug_marker.h" extern "C" { @@ -24,88 +24,81 @@ 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. +// can claim full support for the extension. Since the layer implements the extension itself, +// the downstream may lack it, so passthroughs skip downstream dispatch when it is unavailable. +// Messenger commands are forwarded when the downstream supports them and emulated by the layer otherwise. // 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); - } + layersvt::DispatchDownstreamIfAvailable<&VkuDeviceDispatchTable::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); - } + layersvt::DispatchDownstreamIfAvailable<&VkuDeviceDispatchTable::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); - } + layersvt::DispatchDownstreamIfAvailable<&VkuDeviceDispatchTable::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; + assert(pNameInfo != nullptr); + DebugMarker::Get().SetDebugObjectName((uint64_t)device, (int32_t)pNameInfo->objectType, pNameInfo->objectHandle, + pNameInfo->pObjectName); + return layersvt::DispatchDownstreamOrSuccess<&VkuDeviceDispatchTable::SetDebugUtilsObjectNameEXT>(device, pNameInfo); } // 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; + return layersvt::DispatchDownstreamOrSuccess<&VkuDeviceDispatchTable::SetDebugUtilsObjectTagEXT>(device, pTagInfo); } // 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); - } + layersvt::DispatchDownstreamIfAvailable<&VkuDeviceDispatchTable::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); - } + layersvt::DispatchDownstreamIfAvailable<&VkuDeviceDispatchTable::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); - } + layersvt::DispatchDownstreamIfAvailable<&VkuDeviceDispatchTable::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; +// Required for VK_EXT_debug_utils. If the downstream lacks the extension, the messenger is emulated. +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pMessenger) { + return layersvt::DispatchDownstreamOr<&VkuInstanceDispatchTable::CreateDebugUtilsMessengerEXT>( + [instance, pCreateInfo, pMessenger] { + return DebugMarker::Get().CreateEmulatedMessenger(instance, pCreateInfo, pMessenger); + }, + instance, pCreateInfo, pAllocator, pMessenger); } -// 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); - } +// Required for VK_EXT_debug_utils. If the downstream lacks the extension, the messenger is emulated. +VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT messenger, + const VkAllocationCallbacks* pAllocator) { + layersvt::DispatchDownstreamOr<&VkuInstanceDispatchTable::DestroyDebugUtilsMessengerEXT>( + [messenger] { DebugMarker::Get().DestroyEmulatedMessenger(messenger); }, 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); - } +// Required for VK_EXT_debug_utils. If the downstream lacks the extension, emulated messengers are invoked. +VKAPI_ATTR void VKAPI_CALL vkSubmitDebugUtilsMessageEXT(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) { + layersvt::DispatchDownstreamOr<&VkuInstanceDispatchTable::SubmitDebugUtilsMessageEXT>( + [instance, messageSeverity, messageTypes, pCallbackData] { + DebugMarker::Get().SubmitEmulatedMessage(instance, messageSeverity, messageTypes, pCallbackData); + }, + instance, messageSeverity, messageTypes, pCallbackData); } -} // extern "C" +} // extern "C" diff --git a/layersvt/debug_marker/debug_marker_instance.cpp b/layersvt/debug_marker/debug_marker_instance.cpp new file mode 100644 index 0000000000..fd70255668 --- /dev/null +++ b/layersvt/debug_marker/debug_marker_instance.cpp @@ -0,0 +1,22 @@ +/* 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" + +// The layer singleton lives in its own translation unit so that unit tests can compile +// debug_marker.cpp without it and manage the active instance via layer_test::ResetLayer. +namespace { +DebugMarker g_layer; +} // namespace diff --git a/layersvt/test/CMakeLists.txt b/layersvt/test/CMakeLists.txt index 99fa92c24e..f2324442c0 100644 --- a/layersvt/test/CMakeLists.txt +++ b/layersvt/test/CMakeLists.txt @@ -37,6 +37,10 @@ function(LayerTest NAME) if (${NAME} STREQUAL "DebugMarker") target_sources(${TEST_NAME} PRIVATE ../debug_marker/debug_marker.cpp ../debug_marker/debug_marker_perfetto.cpp ../perfetto/perfetto.cc) target_include_directories(${TEST_NAME} PRIVATE .. ../debug_marker) + target_link_libraries(${TEST_NAME} layersvt_common) + if (NOT MSVC) + set_source_files_properties(../perfetto/perfetto.cc PROPERTIES COMPILE_OPTIONS "-Wno-deprecated-declarations") + endif() elseif (${NAME} STREQUAL "DeviceMemoryReport") target_sources(${TEST_NAME} PRIVATE ../device_memory_report/device_memory_report.cpp ../device_memory_report/device_memory_report_perfetto.cpp ../perfetto/perfetto.cc) target_include_directories(${TEST_NAME} PRIVATE .. ../device_memory_report) diff --git a/layersvt/test/test_debugmarker.cpp b/layersvt/test/test_debugmarker.cpp index e1f2325d2f..544fa7fc53 100644 --- a/layersvt/test/test_debugmarker.cpp +++ b/layersvt/test/test_debugmarker.cpp @@ -18,21 +18,29 @@ #include #include #include +#include + +#include "test/common/layer_base_test_peer.h" static const char* kLayerName = "VK_LAYER_GOOGLE_DebugMarker"; class DebugMarkerTests : public VkTestFramework { public: - ~DebugMarkerTests(){}; + ~DebugMarkerTests() {}; static void SetUpTestSuite() {} - static void TearDownTestSuite(){}; + static void TearDownTestSuite() {}; + + protected: + void SetUp() override { + VkTestFramework::SetUp(); + layer_test::ResetLayer(); + } }; 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); @@ -58,7 +66,367 @@ TEST_F(DebugMarkerTests, CombinedTest) { 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(); + // 3. Reset + layer_test::ResetLayer(); EXPECT_FALSE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_INSTANCE, (uint64_t)instance, "MyInstanceRenamed")); } + +TEST_F(DebugMarkerTests, ManifestTest) { + TEST_DESCRIPTION("Verify DebugMarker LayerManifest properties and extension enumeration via LayerBase"); + + // Test EnumerateInstanceLayerProperties + uint32_t property_count = 0; + VkResult result = layersvt::LayerBaseTestPeer::EnumerateInstanceLayerProperties(&property_count, nullptr); + EXPECT_EQ(result, VK_SUCCESS); + EXPECT_EQ(property_count, 1u); + + VkLayerProperties layer_properties{}; + result = layersvt::LayerBaseTestPeer::EnumerateInstanceLayerProperties(&property_count, &layer_properties); + EXPECT_EQ(result, VK_SUCCESS); + EXPECT_STREQ(layer_properties.layerName, kLayerName); + EXPECT_STREQ(layer_properties.description, "layer: DebugMarker"); + + // Test EnumerateDeviceLayerProperties + property_count = 0; + result = layersvt::LayerBaseTestPeer::EnumerateDeviceLayerProperties(VK_NULL_HANDLE, &property_count, nullptr); + EXPECT_EQ(result, VK_SUCCESS); + EXPECT_EQ(property_count, 1u); + + // Test EnumerateInstanceExtensionProperties + property_count = 0; + result = layersvt::LayerBaseTestPeer::EnumerateInstanceExtensionProperties(kLayerName, &property_count, nullptr); + EXPECT_EQ(result, VK_SUCCESS); + EXPECT_EQ(property_count, 1u); + + std::vector extensions(property_count); + result = layersvt::LayerBaseTestPeer::EnumerateInstanceExtensionProperties(kLayerName, &property_count, extensions.data()); + EXPECT_EQ(result, VK_SUCCESS); + EXPECT_STREQ(extensions[0].extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + + // Query non-matching layer name returns VK_ERROR_LAYER_NOT_PRESENT + property_count = 0; + result = layersvt::LayerBaseTestPeer::EnumerateInstanceExtensionProperties("VK_LAYER_NONEXISTENT", &property_count, nullptr); + EXPECT_EQ(result, VK_ERROR_LAYER_NOT_PRESENT); + + // Test EnumerateDeviceExtensionProperties + property_count = 0; + result = layersvt::LayerBaseTestPeer::EnumerateDeviceExtensionProperties(VK_NULL_HANDLE, kLayerName, &property_count, nullptr); + EXPECT_EQ(result, VK_SUCCESS); + EXPECT_EQ(property_count, 1u); + + extensions.resize(property_count); + result = layersvt::LayerBaseTestPeer::EnumerateDeviceExtensionProperties(VK_NULL_HANDLE, kLayerName, &property_count, + extensions.data()); + EXPECT_EQ(result, VK_SUCCESS); + EXPECT_STREQ(extensions[0].extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME); +} + +TEST_F(DebugMarkerTests, LayerBaseLifecycleAndTrackerTest) { + TEST_DESCRIPTION("Verify DebugMarker LayerBase inheritance and DispatchTableManager integration"); + + // A physical device shares the dispatch key of its parent instance. + void* mock_dispatch_key = reinterpret_cast(0x1000); + void* mock_instance_object = mock_dispatch_key; + void* mock_physical_device_object = mock_dispatch_key; + VkInstance mock_instance = reinterpret_cast(&mock_instance_object); + VkPhysicalDevice mock_physical_device = reinterpret_cast(&mock_physical_device_object); + + layersvt::LayerBaseTestPeer::GetDispatchTableManager(DebugMarker::Get()) + .InitInstanceTable(mock_instance, [](VkInstance, const char*) -> PFN_vkVoidFunction { return nullptr; }); + EXPECT_EQ(layersvt::LayerBaseTestPeer::GetVkInstance(mock_physical_device), mock_instance); + + layer_test::ResetLayer(); + EXPECT_EQ(layersvt::LayerBaseTestPeer::GetVkInstance(mock_physical_device), VK_NULL_HANDLE); +} + +TEST_F(DebugMarkerTests, PreDestroyDeviceCleanupTest) { + TEST_DESCRIPTION("Verify that DestroyDevice cleans up tracked objects associated with that device"); + + void* mock_dev1_vtable = reinterpret_cast(0x1000); + VkDevice dev1 = reinterpret_cast(&mock_dev1_vtable); + void* mock_dev2_vtable = reinterpret_cast(0x2000); + VkDevice dev2 = reinterpret_cast(&mock_dev2_vtable); + + layersvt::LayerBaseTestPeer::GetDispatchTableManager(DebugMarker::Get()) + .InitDeviceTable(dev1, [](VkDevice, const char*) -> PFN_vkVoidFunction { + return reinterpret_cast(+[](VkDevice, const VkAllocationCallbacks*) {}); + }); + layersvt::LayerBaseTestPeer::GetDispatchTableManager(DebugMarker::Get()) + .InitDeviceTable(dev2, [](VkDevice, const char*) -> PFN_vkVoidFunction { + return reinterpret_cast(+[](VkDevice, const VkAllocationCallbacks*) {}); + }); + + DebugMarker::Get().SetDebugObjectName((uint64_t)dev1, VK_OBJECT_TYPE_BUFFER, 0x1111, "Buffer1"); + DebugMarker::Get().SetDebugObjectName((uint64_t)dev2, VK_OBJECT_TYPE_BUFFER, 0x2222, "Buffer2"); + + EXPECT_TRUE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_BUFFER, 0x1111, "Buffer1")); + EXPECT_TRUE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_BUFFER, 0x2222, "Buffer2")); + + // Destroy dev1 - should remove Buffer1 but keep Buffer2 + layersvt::LayerBaseTestPeer::DestroyDevice(dev1, nullptr); + + EXPECT_FALSE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_BUFFER, 0x1111, "Buffer1")); + EXPECT_TRUE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_BUFFER, 0x2222, "Buffer2")); + + // Destroy dev2 - should remove Buffer2 + layersvt::LayerBaseTestPeer::DestroyDevice(dev2, nullptr); + EXPECT_FALSE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_BUFFER, 0x2222, "Buffer2")); +} + +TEST_F(DebugMarkerTests, TemplateMethodDispatchTest) { + TEST_DESCRIPTION("Verify DebugMarker layer-specific hooks and LayerBase template method dispatching"); + + // Layer-specific instance commands intercepted + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkCreateDebugUtilsMessengerEXT"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkDestroyDebugUtilsMessengerEXT"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkSubmitDebugUtilsMessageEXT"), nullptr); + + // Common lifecycle instance commands resolved via LayerBase fallback + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkCreateInstance"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkDestroyInstance"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkEnumerateInstanceExtensionProperties"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkCreateDevice"), nullptr); + + // Layer-specific device commands intercepted + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownDeviceCommand("vkCmdDebugMarkerBeginEXT"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownDeviceCommand("vkCmdBeginDebugUtilsLabelEXT"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownDeviceCommand("vkSetDebugUtilsObjectNameEXT"), nullptr); + + // Common lifecycle device commands resolved via LayerBase fallback + EXPECT_EQ(layersvt::LayerBaseTestPeer::GetKnownDeviceCommand("vkCreateDevice"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownDeviceCommand("vkDestroyDevice"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetKnownDeviceCommand("vkGetDeviceProcAddr"), nullptr); + + // Global commands resolvable with VK_NULL_HANDLE via GetInstanceProcAddr + EXPECT_NE(layersvt::LayerBaseTestPeer::GetInstanceProcAddr(VK_NULL_HANDLE, "vkGetInstanceProcAddr"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties"), nullptr); + + // Non-global commands return nullptr when passed VK_NULL_HANDLE + EXPECT_EQ(layersvt::LayerBaseTestPeer::GetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateDebugUtilsMessengerEXT"), nullptr); + EXPECT_EQ(layersvt::LayerBaseTestPeer::GetInstanceProcAddr(VK_NULL_HANDLE, "vkCmdDebugMarkerBeginEXT"), nullptr); + EXPECT_EQ(layersvt::LayerBaseTestPeer::GetDeviceProcAddr(VK_NULL_HANDLE, "vkCmdBeginDebugUtilsLabelEXT"), nullptr); + EXPECT_EQ(layersvt::LayerBaseTestPeer::GetInstanceProcAddr(VK_NULL_HANDLE, "vkNonExistentCmd"), nullptr); + EXPECT_EQ(layersvt::LayerBaseTestPeer::GetDeviceProcAddr(VK_NULL_HANDLE, "vkNonExistentCmd"), nullptr); + + // Non-global commands resolvable with valid instance/device handles + VkInstance mock_instance = reinterpret_cast(0x1234); + VkDevice mock_device = reinterpret_cast(0x5678); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetInstanceProcAddr(mock_instance, "vkCreateDebugUtilsMessengerEXT"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetInstanceProcAddr(mock_instance, "vkCmdDebugMarkerBeginEXT"), nullptr); + EXPECT_NE(layersvt::LayerBaseTestPeer::GetDeviceProcAddr(mock_device, "vkCmdBeginDebugUtilsLabelEXT"), nullptr); +} + +TEST_F(DebugMarkerTests, MissingDownstreamExtensionTest) { + TEST_DESCRIPTION("Verify debug marker and debug utils hooks do not crash when the downstream lacks the extensions"); + + // Dispatchable handles of a device share the device's dispatch key. + void* mock_dispatch_key = reinterpret_cast(0x3000); + void* mock_device_object = mock_dispatch_key; + void* mock_command_buffer_object = mock_dispatch_key; + VkDevice mock_device = reinterpret_cast(&mock_device_object); + VkCommandBuffer mock_command_buffer = reinterpret_cast(&mock_command_buffer_object); + + // The downstream device exposes no commands at all. + layersvt::LayerBaseTestPeer::GetDispatchTableManager(DebugMarker::Get()) + .InitDeviceTable(mock_device, [](VkDevice, const char*) -> PFN_vkVoidFunction { return nullptr; }); + + auto cmd_begin = reinterpret_cast( + layersvt::LayerBaseTestPeer::GetKnownDeviceCommand("vkCmdDebugMarkerBeginEXT")); + auto cmd_end = + reinterpret_cast(layersvt::LayerBaseTestPeer::GetKnownDeviceCommand("vkCmdDebugMarkerEndEXT")); + auto set_object_name = reinterpret_cast( + layersvt::LayerBaseTestPeer::GetKnownDeviceCommand("vkSetDebugUtilsObjectNameEXT")); + ASSERT_NE(cmd_begin, nullptr); + ASSERT_NE(cmd_end, nullptr); + ASSERT_NE(set_object_name, nullptr); + + VkDebugMarkerMarkerInfoEXT marker_info{VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT}; + marker_info.pMarkerName = "Marker"; + cmd_begin(mock_command_buffer, &marker_info); + cmd_end(mock_command_buffer); + + VkDebugUtilsObjectNameInfoEXT name_info{VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT}; + name_info.objectType = VK_OBJECT_TYPE_BUFFER; + name_info.objectHandle = 0x4444; + name_info.pObjectName = "Buffer"; + EXPECT_EQ(set_object_name(mock_device, &name_info), VK_SUCCESS); + EXPECT_TRUE(DebugMarker::Get().HasDebugObjectName(VK_OBJECT_TYPE_BUFFER, 0x4444, "Buffer")); +} + +namespace { + +struct MessengerCallbackLog { + uint32_t call_count = 0; + VkDebugUtilsMessageSeverityFlagBitsEXT last_severity = static_cast(0); +}; + +VKAPI_ATTR VkBool32 VKAPI_CALL LogMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT*, + void* pUserData) { + auto* log = static_cast(pUserData); + ++log->call_count; + log->last_severity = messageSeverity; + return VK_FALSE; +} + +VkDebugUtilsMessengerCreateInfoEXT MakeMessengerCreateInfo(VkDebugUtilsMessageSeverityFlagsEXT severity, + VkDebugUtilsMessageTypeFlagsEXT type, MessengerCallbackLog* log) { + VkDebugUtilsMessengerCreateInfoEXT create_info{VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; + create_info.messageSeverity = severity; + create_info.messageType = type; + create_info.pfnUserCallback = LogMessengerCallback; + create_info.pUserData = log; + return create_info; +} + +struct DebugUtilsMessengerCommands { + PFN_vkCreateDebugUtilsMessengerEXT create; + PFN_vkDestroyDebugUtilsMessengerEXT destroy; + PFN_vkSubmitDebugUtilsMessageEXT submit; +}; + +DebugUtilsMessengerCommands GetDebugUtilsMessengerCommands() { + return {reinterpret_cast( + layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkCreateDebugUtilsMessengerEXT")), + reinterpret_cast( + layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkDestroyDebugUtilsMessengerEXT")), + reinterpret_cast( + layersvt::LayerBaseTestPeer::GetKnownInstanceCommand("vkSubmitDebugUtilsMessageEXT"))}; +} + +} // namespace + +TEST_F(DebugMarkerTests, EmulatedDebugUtilsMessengerTest) { + TEST_DESCRIPTION("Verify debug utils messengers are emulated when the downstream lacks VK_EXT_debug_utils"); + + void* mock_instance_object = reinterpret_cast(0x5000); + VkInstance mock_instance = reinterpret_cast(&mock_instance_object); + + // The downstream instance only exposes vkDestroyInstance. + layersvt::LayerBaseTestPeer::GetDispatchTableManager(DebugMarker::Get()) + .InitInstanceTable(mock_instance, [](VkInstance, const char* name) -> PFN_vkVoidFunction { + if (strcmp(name, "vkDestroyInstance") == 0) { + return reinterpret_cast(+[](VkInstance, const VkAllocationCallbacks*) {}); + } + return nullptr; + }); + + DebugUtilsMessengerCommands commands = GetDebugUtilsMessengerCommands(); + ASSERT_NE(commands.create, nullptr); + ASSERT_NE(commands.destroy, nullptr); + ASSERT_NE(commands.submit, nullptr); + + MessengerCallbackLog error_log; + MessengerCallbackLog verbose_log; + VkDebugUtilsMessengerCreateInfoEXT error_info = MakeMessengerCreateInfo( + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, &error_log); + VkDebugUtilsMessengerCreateInfoEXT verbose_info = MakeMessengerCreateInfo( + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, &verbose_log); + + VkDebugUtilsMessengerEXT error_messenger = VK_NULL_HANDLE; + VkDebugUtilsMessengerEXT verbose_messenger = VK_NULL_HANDLE; + EXPECT_EQ(commands.create(mock_instance, &error_info, nullptr, &error_messenger), VK_SUCCESS); + EXPECT_EQ(commands.create(mock_instance, &verbose_info, nullptr, &verbose_messenger), VK_SUCCESS); + EXPECT_NE(error_messenger, VK_NULL_HANDLE); + EXPECT_NE(verbose_messenger, VK_NULL_HANDLE); + EXPECT_NE(error_messenger, verbose_messenger); + + // Only the messenger whose severity and type masks match is invoked. + VkDebugUtilsMessengerCallbackDataEXT callback_data{VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT}; + callback_data.pMessage = "message"; + commands.submit(mock_instance, VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, + &callback_data); + EXPECT_EQ(error_log.call_count, 1u); + EXPECT_EQ(error_log.last_severity, VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT); + EXPECT_EQ(verbose_log.call_count, 0u); + + // Matching severity with a non-matching type does not invoke the messenger. + commands.submit(mock_instance, VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, + &callback_data); + EXPECT_EQ(error_log.call_count, 1u); + EXPECT_EQ(verbose_log.call_count, 0u); + + // A destroyed messenger is no longer invoked; destroying VK_NULL_HANDLE is a no-op. + commands.destroy(mock_instance, error_messenger, nullptr); + commands.destroy(mock_instance, VK_NULL_HANDLE, nullptr); + commands.submit(mock_instance, VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, + &callback_data); + EXPECT_EQ(error_log.call_count, 1u); + + commands.submit(mock_instance, VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, + &callback_data); + EXPECT_EQ(verbose_log.call_count, 1u); + + // Destroying the instance removes its remaining emulated messengers. + layersvt::LayerBaseTestPeer::DestroyInstance(mock_instance, nullptr); + commands.submit(mock_instance, VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, + &callback_data); + EXPECT_EQ(verbose_log.call_count, 1u); +} + +namespace { + +uint32_t downstream_create_messenger_calls = 0; +uint32_t downstream_destroy_messenger_calls = 0; +uint32_t downstream_submit_message_calls = 0; +const VkDebugUtilsMessengerEXT kDownstreamMessenger = (VkDebugUtilsMessengerEXT)0xABCDu; + +} // namespace + +TEST_F(DebugMarkerTests, ForwardedDebugUtilsMessengerTest) { + TEST_DESCRIPTION("Verify debug utils messenger commands are forwarded when the downstream supports VK_EXT_debug_utils"); + + downstream_create_messenger_calls = 0; + downstream_destroy_messenger_calls = 0; + downstream_submit_message_calls = 0; + + void* mock_instance_object = reinterpret_cast(0x6000); + VkInstance mock_instance = reinterpret_cast(&mock_instance_object); + + layersvt::LayerBaseTestPeer::GetDispatchTableManager(DebugMarker::Get()) + .InitInstanceTable(mock_instance, [](VkInstance, const char* name) -> PFN_vkVoidFunction { + if (strcmp(name, "vkCreateDebugUtilsMessengerEXT") == 0) { + return reinterpret_cast(+[](VkInstance, const VkDebugUtilsMessengerCreateInfoEXT*, + const VkAllocationCallbacks*, + VkDebugUtilsMessengerEXT* pMessenger) { + ++downstream_create_messenger_calls; + *pMessenger = kDownstreamMessenger; + return VK_SUCCESS; + }); + } + if (strcmp(name, "vkDestroyDebugUtilsMessengerEXT") == 0) { + return reinterpret_cast( + +[](VkInstance, VkDebugUtilsMessengerEXT, const VkAllocationCallbacks*) { + ++downstream_destroy_messenger_calls; + }); + } + if (strcmp(name, "vkSubmitDebugUtilsMessageEXT") == 0) { + return reinterpret_cast( + +[](VkInstance, VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, + const VkDebugUtilsMessengerCallbackDataEXT*) { ++downstream_submit_message_calls; }); + } + return nullptr; + }); + + DebugUtilsMessengerCommands commands = GetDebugUtilsMessengerCommands(); + MessengerCallbackLog log; + VkDebugUtilsMessengerCreateInfoEXT create_info = MakeMessengerCreateInfo(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, &log); + + VkDebugUtilsMessengerEXT messenger = VK_NULL_HANDLE; + EXPECT_EQ(commands.create(mock_instance, &create_info, nullptr, &messenger), VK_SUCCESS); + EXPECT_EQ(messenger, kDownstreamMessenger); + EXPECT_EQ(downstream_create_messenger_calls, 1u); + + // Messages go to the downstream; the layer does not invoke the callback itself. + VkDebugUtilsMessengerCallbackDataEXT callback_data{VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT}; + commands.submit(mock_instance, VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, + &callback_data); + EXPECT_EQ(downstream_submit_message_calls, 1u); + EXPECT_EQ(log.call_count, 0u); + + commands.destroy(mock_instance, messenger, nullptr); + EXPECT_EQ(downstream_destroy_messenger_calls, 1u); +}