From 2db40c0ec532d5c9d427be6c10e80c126626bfc4 Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Wed, 13 Nov 2024 19:00:12 -0700 Subject: [PATCH] scripts: Start to refactor ValidationObject Previously, there were top level ValidationObjects stored in layer_data_map. These didn't do validation but contained more ValidationObjects in the object_dispatch vector (and a few others). This caused much confusion, since some members were only used at the top level and others only at the lower level. When uses got mixed up, this could only be detected at runtime since the members were always available even though they might not be set up correctly. Start to split this up by moving top level functionality to a new DispatchObject class. This does handle wrapping and coordination of validation by the child ValidationObjects. There are still many members related to settings, extension status, logging and dispatch tables that are duplicated at both levels. Fixing this is a big change that needs to be done separately. Also note that DispatchObject and ValidationObject are still used to represent both VkDevice and VkInstance. This will require much more work to undo. --- .../chassis/layer_chassis_dispatch_manual.cpp | 18 +- layers/core_checks/cc_wsi.cpp | 9 +- layers/gpu/core/gpuav_setup.cpp | 2 +- .../gpuav_shader_instrumentor.cpp | 2 +- layers/state_tracker/state_tracker.cpp | 22 +- .../thread_tracker/thread_safety_validation.h | 5 + layers/vulkan/generated/chassis.cpp | 145 +++--- layers/vulkan/generated/chassis.h | 242 ++++++---- .../generated/chassis_dispatch_helper.h | 5 +- .../generated/layer_chassis_dispatch.cpp | 2 +- .../vulkan/generated/layer_chassis_dispatch.h | 4 +- .../layer_chassis_dispatch_generator.py | 6 +- scripts/generators/layer_chassis_generator.py | 418 +++++++++++------- 13 files changed, 542 insertions(+), 338 deletions(-) diff --git a/layers/chassis/layer_chassis_dispatch_manual.cpp b/layers/chassis/layer_chassis_dispatch_manual.cpp index 203b317a0ff..3d311c71074 100644 --- a/layers/chassis/layer_chassis_dispatch_manual.cpp +++ b/layers/chassis/layer_chassis_dispatch_manual.cpp @@ -150,14 +150,9 @@ VkResult DispatchCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipeli dynamic_rendering->stencilAttachmentFormat != VK_FORMAT_UNDEFINED); } - auto &graphics_info = pCreateInfos[idx0]; - auto state_info = dynamic_cast(layer_data); - vku::PNextCopyState pnext_copy_state = { - [state_info, &graphics_info](VkBaseOutStructure *safe_struct, const VkBaseOutStructure *in_struct) -> bool { - return vvl::Pipeline::PnextRenderingInfoCustomCopy(state_info, graphics_info, safe_struct, in_struct); - }}; - local_pCreateInfos[idx0].initialize(&pCreateInfos[idx0], uses_color_attachment, uses_depthstencil_attachment, - &pnext_copy_state); + // TODO: this used to use vvl::Pipeline::PnextRenderingInfoCustomCopy() but it was effectively a no-op + // since the layer_data returned by GetLayerDataPtr() above was NEVER an instance of ValidationStateTracker + local_pCreateInfos[idx0].initialize(&pCreateInfos[idx0], uses_color_attachment, uses_depthstencil_attachment); if (pCreateInfos[idx0].basePipelineHandle) { local_pCreateInfos[idx0].basePipelineHandle = layer_data->Unwrap(pCreateInfos[idx0].basePipelineHandle); @@ -236,7 +231,7 @@ VkResult DispatchCreateGraphicsPipelines(VkDevice device, VkPipelineCache pipeli } template -static void UpdateCreateRenderPassState(ValidationObject *layer_data, const T *pCreateInfo, VkRenderPass renderPass) { +static void UpdateCreateRenderPassState(DispatchObject *layer_data, const T *pCreateInfo, VkRenderPass renderPass) { auto &renderpass_state = layer_data->renderpasses_states[renderPass]; for (uint32_t subpass = 0; subpass < pCreateInfo->subpassCount; ++subpass) { @@ -255,8 +250,7 @@ static void UpdateCreateRenderPassState(ValidationObject *layer_data, const T *p } template <> -void UpdateCreateRenderPassState(ValidationObject *layer_data, const VkRenderPassCreateInfo2 *pCreateInfo, - VkRenderPass renderPass) { +void UpdateCreateRenderPassState(DispatchObject *layer_data, const VkRenderPassCreateInfo2 *pCreateInfo, VkRenderPass renderPass) { auto &renderpass_state = layer_data->renderpasses_states[renderPass]; for (uint32_t subpassIndex = 0; subpassIndex < pCreateInfo->subpassCount; ++subpassIndex) { @@ -648,7 +642,7 @@ void DispatchDestroyDescriptorUpdateTemplateKHR(VkDevice device, VkDescriptorUpd layer_data->device_dispatch_table.DestroyDescriptorUpdateTemplateKHR(device, descriptorUpdateTemplate, pAllocator); } -void *BuildUnwrappedUpdateTemplateBuffer(ValidationObject *layer_data, uint64_t descriptorUpdateTemplate, const void *pData) { +void *BuildUnwrappedUpdateTemplateBuffer(DispatchObject *layer_data, uint64_t descriptorUpdateTemplate, const void *pData) { auto const template_map_entry = layer_data->desc_template_createinfo_map.find(descriptorUpdateTemplate); auto const &create_info = template_map_entry->second->create_info; size_t allocation_size = 0; diff --git a/layers/core_checks/cc_wsi.cpp b/layers/core_checks/cc_wsi.cpp index 56e6c916b3e..f7a55189dae 100644 --- a/layers/core_checks/cc_wsi.cpp +++ b/layers/core_checks/cc_wsi.cpp @@ -1449,8 +1449,7 @@ bool CoreChecks::PreCallValidateGetDeviceGroupSurfacePresentModes2EXT(VkDevice d bool skip = false; if (physical_device_count == 1) { - ValidationObject *device_object = GetLayerDataPtr(GetDispatchKey(device), layer_data_map); - skip |= ValidatePhysicalDeviceSurfaceSupport(device_object->physical_device, pSurfaceInfo->surface, + skip |= ValidatePhysicalDeviceSurfaceSupport(physical_device, pSurfaceInfo->surface, "VUID-vkGetDeviceGroupSurfacePresentModes2EXT-pSurfaceInfo-06213", error_obj.location); } else { @@ -1486,10 +1485,8 @@ bool CoreChecks::PreCallValidateGetDeviceGroupSurfacePresentModesKHR(VkDevice de bool skip = false; if (physical_device_count == 1) { - ValidationObject *device_object = GetLayerDataPtr(GetDispatchKey(device), layer_data_map); - skip |= - ValidatePhysicalDeviceSurfaceSupport(device_object->physical_device, surface, - "VUID-vkGetDeviceGroupSurfacePresentModesKHR-surface-06212", error_obj.location); + skip |= ValidatePhysicalDeviceSurfaceSupport( + physical_device, surface, "VUID-vkGetDeviceGroupSurfacePresentModesKHR-surface-06212", error_obj.location); } else { for (uint32_t i = 0; i < physical_device_count; ++i) { skip |= ValidatePhysicalDeviceSurfaceSupport(device_group_create_info.pPhysicalDevices[i], surface, diff --git a/layers/gpu/core/gpuav_setup.cpp b/layers/gpu/core/gpuav_setup.cpp index 85cc71ce68d..12aef065044 100644 --- a/layers/gpu/core/gpuav_setup.cpp +++ b/layers/gpu/core/gpuav_setup.cpp @@ -595,7 +595,7 @@ void Validator::InternalVmaError(LogObjectList objlist, const Location &loc, con // Once we encounter an internal issue disconnect everything. // This prevents need to check "if (aborted)" (which is awful when we easily forget to check somewhere and the user gets spammed // with errors making it hard to see the first error with the real source of the problem). - ReleaseDeviceDispatchObject(LayerObjectTypeGpuAssisted); + dispatch_->ReleaseDeviceValidationObject(LayerObjectTypeGpuAssisted); } VkDeviceAddress Validator::GetBufferDeviceAddressHelper(VkBuffer buffer) const { diff --git a/layers/gpu/instrumentation/gpuav_shader_instrumentor.cpp b/layers/gpu/instrumentation/gpuav_shader_instrumentor.cpp index cb9e92432a5..d287a5fe50c 100644 --- a/layers/gpu/instrumentation/gpuav_shader_instrumentor.cpp +++ b/layers/gpu/instrumentation/gpuav_shader_instrumentor.cpp @@ -1286,7 +1286,7 @@ void GpuShaderInstrumentor::InternalError(LogObjectList objlist, const Location // Once we encounter an internal issue disconnect everything. // This prevents need to check "if (aborted)" (which is awful when we easily forget to check somewhere and the user gets spammed // with errors making it hard to see the first error with the real source of the problem). - ReleaseDeviceDispatchObject(LayerObjectTypeGpuAssisted); + dispatch_->ReleaseDeviceValidationObject(LayerObjectTypeGpuAssisted); } void GpuShaderInstrumentor::InternalWarning(LogObjectList objlist, const Location &loc, const char *const specific_message) const { diff --git a/layers/state_tracker/state_tracker.cpp b/layers/state_tracker/state_tracker.cpp index 93cba183fe6..10c986008af 100644 --- a/layers/state_tracker/state_tracker.cpp +++ b/layers/state_tracker/state_tracker.cpp @@ -692,7 +692,7 @@ void ValidationStateTracker::PostCallRecordCreateDevice(VkPhysicalDevice gpu, co if (VK_SUCCESS != record_obj.result) return; // The current object represents the VkInstance, look up / create the object for the device. - ValidationObject *device_object = GetLayerDataPtr(GetDispatchKey(*pDevice), layer_data_map); + DispatchObject *device_object = GetLayerDataPtr(GetDispatchKey(*pDevice), layer_data_map); ValidationObject *validation_data = device_object->GetValidationObject(this->container_type); ValidationStateTracker *device_state = static_cast(validation_data); @@ -3952,24 +3952,6 @@ std::shared_ptr ValidationStateTracker::CreatePhysicalDevic return std::make_shared(handle); } -// This is here as some applications will call exit() which results in all our static allocations (like std::map) having their -// destructor called and destroyed from under us. It is not possible to detect as sometimes (when using things like robin hood) the -// size()/empty() will give false positive that memory is there there. We add this global hook that will go through and remove all -// the function calls such that things can safely run in the case the applicaiton still wants to make Vulkan calls in their atexit() -// handler -void ApplicationAtExit() { - // On a "normal" application, this function is called after vkDestroyInstance and layer_data_map is empty - // - // If there are multiple devices we still want to delete them all as exit() is a global scope call - for (auto object : layer_data_map) { - // Should only be a instance and device object, but being safe. - // Need to clean up both the instance and device function hooks - if (object.second->container_type == LayerObjectTypeInstance || object.second->container_type == LayerObjectTypeDevice) { - object.second->ReleaseAllDispatchObjects(); - } - } -} - void ValidationStateTracker::PostCallRecordCreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance, const RecordObject &record_obj) { @@ -3977,8 +3959,6 @@ void ValidationStateTracker::PostCallRecordCreateInstance(const VkInstanceCreate return; } - atexit(ApplicationAtExit); - instance_state = this; uint32_t count = 0; // this can fail if the allocator fails diff --git a/layers/thread_tracker/thread_safety_validation.h b/layers/thread_tracker/thread_safety_validation.h index 9a7845fb117..b510160cd7c 100644 --- a/layers/thread_tracker/thread_safety_validation.h +++ b/layers/thread_tracker/thread_safety_validation.h @@ -280,6 +280,11 @@ class ThreadSafety : public ValidationObject { vvl::concurrent_unordered_map dsl_read_only_map; vvl::concurrent_unordered_map ds_read_only_map; bool DsReadOnly(VkDescriptorSet) const; + // Map of wrapped swapchain handles to arrays of wrapped swapchain image IDs + // Each swapchain has an immutable list of wrapped swapchain image IDs -- always return these IDs if they exist + vvl::unordered_map> swapchain_wrapped_image_handle_map; + // Map of wrapped descriptor pools to set of wrapped descriptor sets allocated from each pool + vvl::unordered_map> pool_descriptor_sets_map; counter c_VkCommandBuffer; counter c_VkDevice; diff --git a/layers/vulkan/generated/chassis.cpp b/layers/vulkan/generated/chassis.cpp index 1ea9be767cb..5ea258bb1a9 100644 --- a/layers/vulkan/generated/chassis.cpp +++ b/layers/vulkan/generated/chassis.cpp @@ -38,7 +38,7 @@ thread_local WriteLockGuard* ValidationObject::record_guard{}; -small_unordered_map layer_data_map; +small_unordered_map layer_data_map; // Global unique object identifier. std::atomic global_unique_id(1ULL); @@ -109,7 +109,7 @@ static std::vector CreateObjectDispatch(const CHECK_ENABLED& return object_dispatch; } -static void InitDeviceObjectDispatch(ValidationObject* instance_interceptor, ValidationObject* device_interceptor) { +static void InitDeviceDispatchObject(DispatchObject* instance_interceptor, DispatchObject* device_interceptor) { auto disables = instance_interceptor->disabled; auto enables = instance_interceptor->enabled; @@ -243,8 +243,17 @@ std::vector>& GetCustomStypeInfo() { return custom_stype_info; } +ValidationObject* DispatchObject::GetValidationObject(LayerObjectTypeId object_type) const { + for (auto validation_object : object_dispatch) { + if (validation_object->container_type == object_type) { + return validation_object; + } + } + return nullptr; +} + template -ValidationObjectType* ValidationObject::GetValidationObject() const { +ValidationObjectType* DispatchObject::GetValidationObject() const { LayerObjectTypeId type_id; if constexpr (std::is_same_v) { type_id = LayerObjectTypeThreading; @@ -260,23 +269,22 @@ ValidationObjectType* ValidationObject::GetValidationObject() const { return static_cast(GetValidationObject(type_id)); } -template ThreadSafety* ValidationObject::GetValidationObject() const; -template StatelessValidation* ValidationObject::GetValidationObject() const; -template ObjectLifetimes* ValidationObject::GetValidationObject() const; -template CoreChecks* ValidationObject::GetValidationObject() const; +template ThreadSafety* DispatchObject::GetValidationObject() const; +template StatelessValidation* DispatchObject::GetValidationObject() const; +template ObjectLifetimes* DispatchObject::GetValidationObject() const; +template CoreChecks* DispatchObject::GetValidationObject() const; -// Takes the layer and removes it from the chassis so it will not be called anymore +// Takes the validation type and removes it from the chassis so it will not be called anymore // Designed for things like GPU-AV to remove itself while keeping everything else alive -void ValidationObject::ReleaseDeviceDispatchObject(LayerObjectTypeId type_id) const { - auto layer_data = GetLayerDataPtr(GetDispatchKey(device), layer_data_map); - for (auto object_it = layer_data->object_dispatch.begin(); object_it != layer_data->object_dispatch.end(); object_it++) { +void DispatchObject::ReleaseDeviceValidationObject(LayerObjectTypeId type_id) const { + for (auto object_it = object_dispatch.begin(); object_it != object_dispatch.end(); object_it++) { if ((*object_it)->container_type == type_id) { ValidationObject* object = *object_it; - layer_data->object_dispatch.erase(object_it); + object_dispatch.erase(object_it); - for (auto intercept_vector_it = layer_data->intercept_vectors.begin(); - intercept_vector_it != layer_data->intercept_vectors.end(); intercept_vector_it++) { + for (auto intercept_vector_it = intercept_vectors.begin(); intercept_vector_it != intercept_vectors.end(); + intercept_vector_it++) { for (auto intercept_object_it = intercept_vector_it->begin(); intercept_object_it != intercept_vector_it->end(); intercept_object_it++) { if (object == *intercept_object_it) { @@ -288,7 +296,7 @@ void ValidationObject::ReleaseDeviceDispatchObject(LayerObjectTypeId type_id) co // We can't destroy the object itself now as it might be unsafe (things are still being used) // If the rare case happens we need to release, we will cleanup later when we normally would have cleaned this up - layer_data->aborted_object_dispatch.push_back(object); + aborted_object_dispatch.push_back(object); break; } } @@ -296,21 +304,17 @@ void ValidationObject::ReleaseDeviceDispatchObject(LayerObjectTypeId type_id) co // Incase we need to teardown things early, we want to do it safely, so we will keep the entrypoints into layer, but just remove all // the internal chassis hooks so that any call becomes a no-op (but still dispatches into the driver) -void ValidationObject::ReleaseAllDispatchObjects() const { - assert(container_type == LayerObjectTypeInstance || container_type == LayerObjectTypeDevice); - auto dispatch_key = container_type == LayerObjectTypeInstance ? GetDispatchKey(instance) : GetDispatchKey(device); - auto layer_data = GetLayerDataPtr(dispatch_key, layer_data_map); - +void DispatchObject::ReleaseAllValidationObjects() const { // Some chassis loops use the intercept_vectors instead of looking up the object - for (auto& intercept_vector : layer_data->intercept_vectors) { + for (auto& intercept_vector : intercept_vectors) { intercept_vector.clear(); } - for (auto object_it = layer_data->object_dispatch.begin(); object_it != layer_data->object_dispatch.end(); object_it++) { + for (auto object_it = object_dispatch.begin(); object_it != object_dispatch.end(); object_it++) { ValidationObject* object = *object_it; - layer_data->aborted_object_dispatch.push_back(object); + aborted_object_dispatch.push_back(object); } - layer_data->object_dispatch.clear(); + object_dispatch.clear(); } namespace vulkan_layer_chassis { @@ -334,7 +338,7 @@ const vvl::unordered_map& GetNameToFuncPtrMap(); // Manually written functions // Check enabled instance extensions against supported instance extension whitelist -static void InstanceExtensionWhitelist(ValidationObject* layer_data, const VkInstanceCreateInfo* pCreateInfo, VkInstance instance) { +static void InstanceExtensionWhitelist(DispatchObject* layer_data, const VkInstanceCreateInfo* pCreateInfo, VkInstance instance) { for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { // Check for recognized instance extensions vvl::Extension extension = GetExtension(pCreateInfo->ppEnabledExtensionNames[i]); @@ -350,7 +354,7 @@ static void InstanceExtensionWhitelist(ValidationObject* layer_data, const VkIns } // Check enabled device extensions against supported device extension whitelist -static void DeviceExtensionWhitelist(ValidationObject* layer_data, const VkDeviceCreateInfo* pCreateInfo, VkDevice device) { +static void DeviceExtensionWhitelist(DispatchObject* layer_data, const VkDeviceCreateInfo* pCreateInfo, VkDevice device) { for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { // Check for recognized device extensions vvl::Extension extension = GetExtension(pCreateInfo->ppEnabledExtensionNames[i]); @@ -365,7 +369,7 @@ static void DeviceExtensionWhitelist(ValidationObject* layer_data, const VkDevic } } -void OutputLayerStatusInfo(ValidationObject* context) { +void OutputLayerStatusInfo(DispatchObject* context) { std::string list_of_enables; std::string list_of_disables; for (uint32_t i = 0; i < kMaxEnableFlags; i++) { @@ -488,8 +492,24 @@ VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceExtensionProperties(VkPhysicalDevi return layer_data->instance_dispatch_table.EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pCount, pProperties); } +// This is here as some applications will call exit() which results in all our static allocations (like std::map) having their +// destructor called and destroyed from under us. It is not possible to detect as sometimes (when using things like robin hood) the +// size()/empty() will give false positive that memory is there there. We add this global hook that will go through and remove all +// the function calls such that things can safely run in the case the applicaiton still wants to make Vulkan calls in their atexit() +// handler +void ApplicationAtExit() { + // On a "normal" application, this function is called after vkDestroyInstance and layer_data_map is empty + // + // If there are multiple devices we still want to delete them all as exit() is a global scope call + for (auto object : layer_data_map) { + object.second->ReleaseAllValidationObjects(); + } +} + VKAPI_ATTR VkResult VKAPI_CALL CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { + atexit(ApplicationAtExit); + VVL_ZoneScoped; VkLayerInstanceCreateInfo* chain_info = GetChainInfo(pCreateInfo, VK_LAYER_LINK_INFO); @@ -526,20 +546,34 @@ VKAPI_ATTR VkResult VKAPI_CALL CreateInstance(const VkInstanceCreateInfo* pCreat wrap_handles = false; } + DispatchObject* framework = new DispatchObject(); + + framework->api_version = api_version; + framework->object_dispatch = local_object_dispatch; + framework->disabled = local_disables; + framework->enabled = local_enables; + framework->global_settings = local_global_settings; + framework->gpuav_settings = local_gpuav_settings; + framework->syncval_settings = local_syncval_settings; + framework->debug_report = debug_report; + framework->instance_extensions.InitFromInstanceCreateInfo(specified_version, pCreateInfo); + // Initialize the validation objects for (auto* intercept : local_object_dispatch) { intercept->api_version = api_version; intercept->debug_report = debug_report; + intercept->dispatch_ = framework; } // Define logic to cleanup everything in case of an error - auto cleanup_allocations = [debug_report, &local_object_dispatch]() { + auto cleanup_allocations = [debug_report, framework, &local_object_dispatch]() { DeactivateInstanceDebugCallbacks(debug_report); vku::FreePnextChain(debug_report->instance_pnext_chain); LayerDebugUtilsDestroyInstance(debug_report); for (ValidationObject* object : local_object_dispatch) { delete object; } + delete framework; }; // Init dispatch array and call registration functions @@ -566,26 +600,15 @@ VKAPI_ATTR VkResult VKAPI_CALL CreateInstance(const VkInstanceCreateInfo* pCreat return result; } record_obj.result = result; - auto framework = GetLayerDataPtr(GetDispatchKey(*pInstance), layer_data_map); - - framework->object_dispatch = local_object_dispatch; - framework->container_type = LayerObjectTypeInstance; - framework->disabled = local_disables; - framework->enabled = local_enables; - framework->global_settings = local_global_settings; - framework->gpuav_settings = local_gpuav_settings; - framework->syncval_settings = local_syncval_settings; - framework->instance = *pInstance; + layer_init_instance_dispatch_table(*pInstance, &framework->instance_dispatch_table, fpGetInstanceProcAddr); - framework->debug_report = debug_report; - framework->api_version = api_version; - framework->instance_extensions.InitFromInstanceCreateInfo(specified_version, pCreateInfo); // We need to call this to properly check which device extensions have been promoted when validating query functions // that take as input a physical device, which can be called before a logical device has been created. framework->device_extensions.InitFromDeviceCreateInfo(&framework->instance_extensions, specified_version); + layer_data_map[GetDispatchKey(*pInstance)] = framework; OutputLayerStatusInfo(framework); for (auto* intercept : framework->object_dispatch) { @@ -596,6 +619,8 @@ VKAPI_ATTR VkResult VKAPI_CALL CreateInstance(const VkInstanceCreateInfo* pCreat intercept->gpuav_settings = framework->gpuav_settings; intercept->syncval_settings = framework->syncval_settings; intercept->instance = *pInstance; + intercept->debug_report = debug_report; + intercept->api_version = api_version; } for (ValidationObject* intercept : framework->object_dispatch) { @@ -684,10 +709,20 @@ VKAPI_ATTR VkResult VKAPI_CALL CreateDevice(VkPhysicalDevice gpu, const VkDevice // Setup the validation tables based on the application API version from the instance and the capabilities of the device driver auto effective_api_version = std::min(APIVersion(device_properties.apiVersion), instance_interceptor->api_version); - DeviceExtensions device_extensions = {}; - device_extensions.InitFromDeviceCreateInfo(&instance_interceptor->instance_extensions, effective_api_version, pCreateInfo); - for (auto item : instance_interceptor->object_dispatch) { - item->device_extensions = device_extensions; + DispatchObject* device_interceptor = new DispatchObject(); + + device_interceptor->device_extensions.InitFromDeviceCreateInfo(&instance_interceptor->instance_extensions, + effective_api_version, pCreateInfo); + device_interceptor->instance_dispatch_table = instance_interceptor->instance_dispatch_table; + device_interceptor->instance_extensions = instance_interceptor->instance_extensions; + device_interceptor->physical_device = gpu; + device_interceptor->instance = instance_interceptor->instance; + device_interceptor->debug_report = instance_interceptor->debug_report; + + // This is odd but we need to set the current device_extensions in all of the + // instance validation objects so that they are available for validating CreateDevice + for (auto* object : instance_interceptor->object_dispatch) { + object->device_extensions = device_interceptor->device_extensions; } // Make copy to modify as some ValidationObjects will want to add extensions/features on @@ -698,7 +733,10 @@ VKAPI_ATTR VkResult VKAPI_CALL CreateDevice(VkPhysicalDevice gpu, const VkDevice for (const ValidationObject* intercept : instance_interceptor->object_dispatch) { auto lock = intercept->ReadLock(); skip |= intercept->PreCallValidateCreateDevice(gpu, pCreateInfo, pAllocator, pDevice, error_obj); - if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; + if (skip) { + delete device_interceptor; + return VK_ERROR_VALIDATION_FAILED_EXT; + } } RecordObject record_obj(vvl::Func::vkCreateDevice); @@ -709,32 +747,27 @@ VKAPI_ATTR VkResult VKAPI_CALL CreateDevice(VkPhysicalDevice gpu, const VkDevice VkResult result = fpCreateDevice(gpu, reinterpret_cast(&modified_create_info), pAllocator, pDevice); if (result != VK_SUCCESS) { + delete device_interceptor; return result; } record_obj.result = result; - - auto device_interceptor = GetLayerDataPtr(GetDispatchKey(*pDevice), layer_data_map); - device_interceptor->container_type = LayerObjectTypeDevice; + device_interceptor->device = *pDevice; // Save local info in device object device_interceptor->api_version = device_interceptor->device_extensions.InitFromDeviceCreateInfo( &instance_interceptor->instance_extensions, effective_api_version, reinterpret_cast(&modified_create_info)); - device_interceptor->device_extensions = device_extensions; layer_init_device_dispatch_table(*pDevice, &device_interceptor->device_dispatch_table, fpGetDeviceProcAddr); - - device_interceptor->device = *pDevice; - device_interceptor->physical_device = gpu; - device_interceptor->instance = instance_interceptor->instance; - device_interceptor->debug_report = instance_interceptor->debug_report; + layer_data_map[GetDispatchKey(*pDevice)] = device_interceptor; instance_interceptor->debug_report->device_created++; - InitDeviceObjectDispatch(instance_interceptor, device_interceptor); + InitDeviceDispatchObject(instance_interceptor, device_interceptor); // Initialize all of the objects with the appropriate data for (auto* object : device_interceptor->object_dispatch) { + object->dispatch_ = device_interceptor; object->device = device_interceptor->device; object->physical_device = device_interceptor->physical_device; object->instance = instance_interceptor->instance; diff --git a/layers/vulkan/generated/chassis.h b/layers/vulkan/generated/chassis.h index 4fb1fb6bcba..71cd0ba11df 100644 --- a/layers/vulkan/generated/chassis.h +++ b/layers/vulkan/generated/chassis.h @@ -2232,8 +2232,6 @@ VKAPI_ATTR void VKAPI_CALL CmdDrawMeshTasksIndirectCountEXT(VkCommandBuffer comm // Layer object type identifiers enum LayerObjectTypeId { - LayerObjectTypeInstance, // Container for an instance dispatch object - LayerObjectTypeDevice, // Container for a device dispatch object LayerObjectTypeThreading, // Instance or device threading layer object LayerObjectTypeParameterValidation, // Instance or device parameter validation layer object LayerObjectTypeObjectTracker, // Instance or device object tracker layer object @@ -2267,6 +2265,162 @@ enum class ValidValue { #else #define DECORATE_PRINTF(_fmt_num, _first_param_num) #endif + +class ValidationObject; + +class DispatchObject { + public: + APIVersion api_version; + DebugReport* debug_report = nullptr; + VkInstance instance = VK_NULL_HANDLE; + VkPhysicalDevice physical_device = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + + VkLayerInstanceDispatchTable instance_dispatch_table; + VkLayerDispatchTable device_dispatch_table; + + InstanceExtensions instance_extensions; + DeviceExtensions device_extensions = {}; + GlobalSettings global_settings = {}; + GpuAVSettings gpuav_settings = {}; + SyncValSettings syncval_settings = {}; + + CHECK_DISABLED disabled = {}; + CHECK_ENABLED enabled = {}; + + mutable std::vector> intercept_vectors; + mutable std::vector object_dispatch; + mutable std::vector aborted_object_dispatch; + + // Handle Wrapping Data + // Reverse map display handles + vvl::concurrent_unordered_map display_id_reverse_mapping; + // Wrapping Descriptor Template Update structures requires access to the template createinfo structs + vvl::unordered_map> desc_template_createinfo_map; + struct SubpassesUsageStates { + vvl::unordered_set subpasses_using_color_attachment; + vvl::unordered_set subpasses_using_depthstencil_attachment; + }; + // Uses unwrapped handles + vvl::unordered_map renderpasses_states; + // Map of wrapped swapchain handles to arrays of wrapped swapchain image IDs + // Each swapchain has an immutable list of wrapped swapchain image IDs -- always return these IDs if they exist + vvl::unordered_map> swapchain_wrapped_image_handle_map; + // Map of wrapped descriptor pools to set of wrapped descriptor sets allocated from each pool + vvl::unordered_map> pool_descriptor_sets_map; + + vvl::concurrent_unordered_map>, 0> deferred_operation_post_completion; + vvl::concurrent_unordered_map&)>>, 0> + deferred_operation_post_check; + vvl::concurrent_unordered_map, 0> deferred_operation_pipelines; + + void InitObjectDispatchVectors(); + void ReleaseDeviceValidationObject(LayerObjectTypeId type_id) const; + void ReleaseAllValidationObjects() const; + + ValidationObject* GetValidationObject(LayerObjectTypeId object_type) const; + + template + ValidationObjectType* GetValidationObject() const; + // Unwrap a handle. + template + HandleType Unwrap(HandleType wrapped_handle) { + if (wrapped_handle == (HandleType)VK_NULL_HANDLE) return wrapped_handle; + auto iter = unique_id_mapping.find(CastToUint64(wrapped_handle)); + if (iter == unique_id_mapping.end()) return (HandleType)0; + return (HandleType)iter->second; + } + + // Wrap a newly created handle with a new unique ID, and return the new ID. + template + HandleType WrapNew(HandleType new_created_handle) { + if (new_created_handle == (HandleType)VK_NULL_HANDLE) return new_created_handle; + auto unique_id = global_unique_id++; + unique_id = HashedUint64::hash(unique_id); + assert(unique_id != 0); // can't be 0, otherwise unwrap will apply special rule for VK_NULL_HANDLE + unique_id_mapping.insert_or_assign(unique_id, CastToUint64(new_created_handle)); + return (HandleType)unique_id; + } + + // VkDisplayKHR objects are statically created in the driver at VkCreateInstance. + // They live with the PhyiscalDevice and apps never created/destroy them. + // Apps needs will query for them and the first time we see it we wrap it + VkDisplayKHR MaybeWrapDisplay(VkDisplayKHR handle) { + // See if this display is already known + auto it = display_id_reverse_mapping.find(handle); + if (it != display_id_reverse_mapping.end()) return (VkDisplayKHR)it->second; + + // First time see this VkDisplayKHR, so wrap + const uint64_t unique_id = (uint64_t)WrapNew(handle); + display_id_reverse_mapping.insert_or_assign(handle, unique_id); + return (VkDisplayKHR)unique_id; + } + // Debug Logging Helpers + bool DECORATE_PRINTF(5, 6) + LogError(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kErrorBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + // Currently works like LogWarning, but allows developer to better categorize the warning + bool DECORATE_PRINTF(5, 6) LogUndefinedValue(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, + const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kWarningBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + bool DECORATE_PRINTF(5, 6) + LogWarning(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kWarningBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + bool DECORATE_PRINTF(5, 6) LogPerformanceWarning(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, + const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kPerformanceWarningBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + bool DECORATE_PRINTF(5, 6) + LogInfo(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kInformationBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + bool DECORATE_PRINTF(5, 6) + LogVerbose(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kVerboseBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + void LogInternalError(std::string_view failure_location, const LogObjectList& obj_list, const Location& loc, + std::string_view entrypoint, VkResult err) const { + const std::string_view err_string = string_VkResult(err); + std::string vuid = "INTERNAL-ERROR-"; + vuid += entrypoint; + LogError(vuid, obj_list, loc, "at %s: %s() was called in the Validation Layer state tracking and failed with result = %s.", + failure_location.data(), entrypoint.data(), err_string.data()); + } +}; + // Layer chassis validation object base class definition class ValidationObject { public: @@ -2276,43 +2430,32 @@ class ValidationObject { std::string FormatHandle(T&& h) const { return debug_report->FormatHandle(std::forward(h)); } - - std::vector> intercept_vectors; + DispatchObject* dispatch_{}; VkLayerInstanceDispatchTable instance_dispatch_table; VkLayerDispatchTable device_dispatch_table; InstanceExtensions instance_extensions; DeviceExtensions device_extensions = {}; - CHECK_DISABLED disabled = {}; - CHECK_ENABLED enabled = {}; GlobalSettings global_settings = {}; GpuAVSettings gpuav_settings = {}; SyncValSettings syncval_settings = {}; + CHECK_DISABLED disabled = {}; + CHECK_ENABLED enabled = {}; + VkInstance instance = VK_NULL_HANDLE; VkPhysicalDevice physical_device = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE; bool is_device_lost = false; - std::vector object_dispatch; - std::vector aborted_object_dispatch; LayerObjectTypeId container_type; - void ReleaseDeviceDispatchObject(LayerObjectTypeId type_id) const; - void ReleaseAllDispatchObjects() const; - - vvl::concurrent_unordered_map>, 0> deferred_operation_post_completion; - vvl::concurrent_unordered_map&)>>, 0> - deferred_operation_post_check; - vvl::concurrent_unordered_map, 0> deferred_operation_pipelines; std::string layer_name = "CHASSIS"; ValidationObject() {} virtual ~ValidationObject() {} - void InitObjectDispatchVectors(); - mutable std::shared_mutex validation_object_mutex; virtual ReadLockGuard ReadLock() const { return ReadLockGuard(validation_object_mutex); } virtual WriteLockGuard WriteLock() { return WriteLockGuard(validation_object_mutex); } @@ -2357,18 +2500,6 @@ class ValidationObject { } } - ValidationObject* GetValidationObject(LayerObjectTypeId object_type) const { - for (auto validation_object : object_dispatch) { - if (validation_object->container_type == object_type) { - return validation_object; - } - } - return nullptr; - } - - template - ValidationObjectType* GetValidationObject() const; - // Debug Logging Helpers bool DECORATE_PRINTF(5, 6) LogError(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { @@ -2433,57 +2564,6 @@ class ValidationObject { LogError(vuid, obj_list, loc, "at %s: %s() was called in the Validation Layer state tracking and failed with result = %s.", failure_location.data(), entrypoint.data(), err_string.data()); } - - // Handle Wrapping Data - // Reverse map display handles - vvl::concurrent_unordered_map display_id_reverse_mapping; - // Wrapping Descriptor Template Update structures requires access to the template createinfo structs - vvl::unordered_map> desc_template_createinfo_map; - struct SubpassesUsageStates { - vvl::unordered_set subpasses_using_color_attachment; - vvl::unordered_set subpasses_using_depthstencil_attachment; - }; - // Uses unwrapped handles - vvl::unordered_map renderpasses_states; - // Map of wrapped swapchain handles to arrays of wrapped swapchain image IDs - // Each swapchain has an immutable list of wrapped swapchain image IDs -- always return these IDs if they exist - vvl::unordered_map> swapchain_wrapped_image_handle_map; - // Map of wrapped descriptor pools to set of wrapped descriptor sets allocated from each pool - vvl::unordered_map> pool_descriptor_sets_map; - - // Unwrap a handle. - template - HandleType Unwrap(HandleType wrapped_handle) { - if (wrapped_handle == (HandleType)VK_NULL_HANDLE) return wrapped_handle; - auto iter = unique_id_mapping.find(CastToUint64(wrapped_handle)); - if (iter == unique_id_mapping.end()) return (HandleType)0; - return (HandleType)iter->second; - } - - // Wrap a newly created handle with a new unique ID, and return the new ID. - template - HandleType WrapNew(HandleType new_created_handle) { - if (new_created_handle == (HandleType)VK_NULL_HANDLE) return new_created_handle; - auto unique_id = global_unique_id++; - unique_id = HashedUint64::hash(unique_id); - assert(unique_id != 0); // can't be 0, otherwise unwrap will apply special rule for VK_NULL_HANDLE - unique_id_mapping.insert_or_assign(unique_id, CastToUint64(new_created_handle)); - return (HandleType)unique_id; - } - - // VkDisplayKHR objects are statically created in the driver at VkCreateInstance. - // They live with the PhyiscalDevice and apps never created/destroy them. - // Apps needs will query for them and the first time we see it we wrap it - VkDisplayKHR MaybeWrapDisplay(VkDisplayKHR handle) { - // See if this display is already known - auto it = display_id_reverse_mapping.find(handle); - if (it != display_id_reverse_mapping.end()) return (VkDisplayKHR)it->second; - - // First time see this VkDisplayKHR, so wrap - const uint64_t unique_id = (uint64_t)WrapNew(handle); - display_id_reverse_mapping.insert_or_assign(handle, unique_id); - return (VkDisplayKHR)unique_id; - } // We make many internal dispatch calls to extended query functions which can depend on the API version void DispatchGetPhysicalDeviceFeatures2Helper(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) const; void DispatchGetPhysicalDeviceProperties2Helper(VkPhysicalDevice physicalDevice, @@ -4726,5 +4806,5 @@ class ValidationObject { } }; // clang-format on -extern small_unordered_map layer_data_map; +extern small_unordered_map layer_data_map; // NOLINTEND diff --git a/layers/vulkan/generated/chassis_dispatch_helper.h b/layers/vulkan/generated/chassis_dispatch_helper.h index 2456886b0b4..5a3cec32ebd 100644 --- a/layers/vulkan/generated/chassis_dispatch_helper.h +++ b/layers/vulkan/generated/chassis_dispatch_helper.h @@ -1758,7 +1758,7 @@ typedef enum InterceptId { } InterceptId; // clang-format off -void ValidationObject::InitObjectDispatchVectors() { +void DispatchObject::InitObjectDispatchVectors() { #define BUILD_DISPATCH_VECTOR(name) \ init_object_dispatch_vector(InterceptId ## name, \ @@ -1804,9 +1804,6 @@ void ValidationObject::InitObjectDispatchVectors() { case LayerObjectTypeSyncValidation: if (tsv_typeid != vo_typeid) intercept_vector->push_back(item); break; - case LayerObjectTypeInstance: - case LayerObjectTypeDevice: - break; default: /* Chassis codegen needs to be updated for unknown validation object type */ assert(0); diff --git a/layers/vulkan/generated/layer_chassis_dispatch.cpp b/layers/vulkan/generated/layer_chassis_dispatch.cpp index af5043d63cf..ae533ceb871 100644 --- a/layers/vulkan/generated/layer_chassis_dispatch.cpp +++ b/layers/vulkan/generated/layer_chassis_dispatch.cpp @@ -32,7 +32,7 @@ #define DISPATCH_MAX_STACK_ALLOCATIONS 32 // Unique Objects pNext extension handling function -void UnwrapPnextChainHandles(ValidationObject* layer_data, const void* pNext) { +void UnwrapPnextChainHandles(DispatchObject* layer_data, const void* pNext) { void* cur_pnext = const_cast(pNext); while (cur_pnext != nullptr) { VkBaseOutStructure* header = reinterpret_cast(cur_pnext); diff --git a/layers/vulkan/generated/layer_chassis_dispatch.h b/layers/vulkan/generated/layer_chassis_dispatch.h index d8d40009a9b..3ec395d1768 100644 --- a/layers/vulkan/generated/layer_chassis_dispatch.h +++ b/layers/vulkan/generated/layer_chassis_dispatch.h @@ -27,8 +27,8 @@ extern bool wrap_handles; -class ValidationObject; -void UnwrapPnextChainHandles(ValidationObject* layer_data, const void* pNext); +class DispatchObject; +void UnwrapPnextChainHandles(DispatchObject* layer_data, const void* pNext); VkResult DispatchCreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); diff --git a/scripts/generators/layer_chassis_dispatch_generator.py b/scripts/generators/layer_chassis_dispatch_generator.py index 7f1e67d9e39..300114c2576 100644 --- a/scripts/generators/layer_chassis_dispatch_generator.py +++ b/scripts/generators/layer_chassis_dispatch_generator.py @@ -176,8 +176,8 @@ def generateHeader(self): extern bool wrap_handles; - class ValidationObject; - void UnwrapPnextChainHandles(ValidationObject *layer_data, const void *pNext); + class DispatchObject; + void UnwrapPnextChainHandles(DispatchObject *layer_data, const void *pNext); ''') guard_helper = PlatformGuardHelper() @@ -213,7 +213,7 @@ def generateSource(self): #define DISPATCH_MAX_STACK_ALLOCATIONS 32 // Unique Objects pNext extension handling function - void UnwrapPnextChainHandles(ValidationObject *layer_data, const void *pNext) { + void UnwrapPnextChainHandles(DispatchObject *layer_data, const void *pNext) { void *cur_pnext = const_cast(pNext); while (cur_pnext != nullptr) { VkBaseOutStructure *header = reinterpret_cast(cur_pnext); diff --git a/scripts/generators/layer_chassis_generator.py b/scripts/generators/layer_chassis_generator.py index 6acf98d680c..94003f5f8ea 100644 --- a/scripts/generators/layer_chassis_generator.py +++ b/scripts/generators/layer_chassis_generator.py @@ -117,7 +117,7 @@ def genInitObjectDispatchVectorSource(targetApiName: str) -> str: case 'vulkan': return ''' // clang-format off -void ValidationObject::InitObjectDispatchVectors() { +void DispatchObject::InitObjectDispatchVectors() { #define BUILD_DISPATCH_VECTOR(name) \\ init_object_dispatch_vector(InterceptId ## name, \\ @@ -163,9 +163,6 @@ def genInitObjectDispatchVectorSource(targetApiName: str) -> str: case LayerObjectTypeSyncValidation: if (tsv_typeid != vo_typeid) intercept_vector->push_back(item); break; - case LayerObjectTypeInstance: - case LayerObjectTypeDevice: - break; default: /* Chassis codegen needs to be updated for unknown validation object type */ assert(0); @@ -366,8 +363,6 @@ class Pipeline; out.append(''' // Layer object type identifiers enum LayerObjectTypeId { - LayerObjectTypeInstance, // Container for an instance dispatch object - LayerObjectTypeDevice, // Container for a device dispatch object LayerObjectTypeThreading, // Instance or device threading layer object LayerObjectTypeParameterValidation, // Instance or device parameter validation layer object LayerObjectTypeObjectTracker, // Instance or device object tracker layer object @@ -401,52 +396,197 @@ class Pipeline; #else #define DECORATE_PRINTF(_fmt_num, _first_param_num) #endif + + class ValidationObject; + + class DispatchObject { + public: + APIVersion api_version; + DebugReport* debug_report = nullptr; + VkInstance instance = VK_NULL_HANDLE; + VkPhysicalDevice physical_device = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + + VkLayerInstanceDispatchTable instance_dispatch_table; + VkLayerDispatchTable device_dispatch_table; + + InstanceExtensions instance_extensions; + DeviceExtensions device_extensions = {}; + GlobalSettings global_settings = {}; + GpuAVSettings gpuav_settings = {}; + SyncValSettings syncval_settings = {}; + + CHECK_DISABLED disabled = {}; + CHECK_ENABLED enabled = {}; + + mutable std::vector> intercept_vectors; + mutable std::vector object_dispatch; + mutable std::vector aborted_object_dispatch; + + // Handle Wrapping Data + // Reverse map display handles + vvl::concurrent_unordered_map display_id_reverse_mapping; + // Wrapping Descriptor Template Update structures requires access to the template createinfo structs + vvl::unordered_map> desc_template_createinfo_map; + struct SubpassesUsageStates { + vvl::unordered_set subpasses_using_color_attachment; + vvl::unordered_set subpasses_using_depthstencil_attachment; + }; + // Uses unwrapped handles + vvl::unordered_map renderpasses_states; + // Map of wrapped swapchain handles to arrays of wrapped swapchain image IDs + // Each swapchain has an immutable list of wrapped swapchain image IDs -- always return these IDs if they exist + vvl::unordered_map> swapchain_wrapped_image_handle_map; + // Map of wrapped descriptor pools to set of wrapped descriptor sets allocated from each pool + vvl::unordered_map> pool_descriptor_sets_map; + + vvl::concurrent_unordered_map>, 0> deferred_operation_post_completion; + vvl::concurrent_unordered_map&)>>, 0> + deferred_operation_post_check; + vvl::concurrent_unordered_map, 0> deferred_operation_pipelines; + + void InitObjectDispatchVectors(); + void ReleaseDeviceValidationObject(LayerObjectTypeId type_id) const; + void ReleaseAllValidationObjects() const; + + ValidationObject* GetValidationObject(LayerObjectTypeId object_type) const; + + template + ValidationObjectType* GetValidationObject() const; + // Unwrap a handle. + template + HandleType Unwrap(HandleType wrapped_handle) { + if (wrapped_handle == (HandleType)VK_NULL_HANDLE) return wrapped_handle; + auto iter = unique_id_mapping.find(CastToUint64(wrapped_handle)); + if (iter == unique_id_mapping.end()) return (HandleType)0; + return (HandleType)iter->second; + } + + // Wrap a newly created handle with a new unique ID, and return the new ID. + template + HandleType WrapNew(HandleType new_created_handle) { + if (new_created_handle == (HandleType)VK_NULL_HANDLE) return new_created_handle; + auto unique_id = global_unique_id++; + unique_id = HashedUint64::hash(unique_id); + assert(unique_id != 0); // can't be 0, otherwise unwrap will apply special rule for VK_NULL_HANDLE + unique_id_mapping.insert_or_assign(unique_id, CastToUint64(new_created_handle)); + return (HandleType)unique_id; + } + + // VkDisplayKHR objects are statically created in the driver at VkCreateInstance. + // They live with the PhyiscalDevice and apps never created/destroy them. + // Apps needs will query for them and the first time we see it we wrap it + VkDisplayKHR MaybeWrapDisplay(VkDisplayKHR handle) { + // See if this display is already known + auto it = display_id_reverse_mapping.find(handle); + if (it != display_id_reverse_mapping.end()) return (VkDisplayKHR)it->second; + + // First time see this VkDisplayKHR, so wrap + const uint64_t unique_id = (uint64_t)WrapNew(handle); + display_id_reverse_mapping.insert_or_assign(handle, unique_id); + return (VkDisplayKHR)unique_id; + } + // Debug Logging Helpers + bool DECORATE_PRINTF(5, 6) + LogError(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kErrorBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + // Currently works like LogWarning, but allows developer to better categorize the warning + bool DECORATE_PRINTF(5, 6) LogUndefinedValue(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, + const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kWarningBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + bool DECORATE_PRINTF(5, 6) + LogWarning(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kWarningBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + bool DECORATE_PRINTF(5, 6) LogPerformanceWarning(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, + const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kPerformanceWarningBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + bool DECORATE_PRINTF(5, 6) + LogInfo(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kInformationBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + bool DECORATE_PRINTF(5, 6) + LogVerbose(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + va_list argptr; + va_start(argptr, format); + const bool result = debug_report->LogMsg(kVerboseBit, objlist, loc, vuid_text, format, argptr); + va_end(argptr); + return result; + } + + void LogInternalError(std::string_view failure_location, const LogObjectList& obj_list, const Location& loc, + std::string_view entrypoint, VkResult err) const { + const std::string_view err_string = string_VkResult(err); + std::string vuid = "INTERNAL-ERROR-"; + vuid += entrypoint; + LogError(vuid, obj_list, loc, "at %s: %s() was called in the Validation Layer state tracking and failed with result = %s.", + failure_location.data(), entrypoint.data(), err_string.data()); + } + }; + // Layer chassis validation object base class definition class ValidationObject { - public: + public: APIVersion api_version; DebugReport* debug_report = nullptr; template std::string FormatHandle(T&& h) const { return debug_report->FormatHandle(std::forward(h)); } - - std::vector> intercept_vectors; + DispatchObject* dispatch_{}; VkLayerInstanceDispatchTable instance_dispatch_table; VkLayerDispatchTable device_dispatch_table; InstanceExtensions instance_extensions; DeviceExtensions device_extensions = {}; - CHECK_DISABLED disabled = {}; - CHECK_ENABLED enabled = {}; GlobalSettings global_settings = {}; GpuAVSettings gpuav_settings = {}; SyncValSettings syncval_settings = {}; + CHECK_DISABLED disabled = {}; + CHECK_ENABLED enabled = {}; + VkInstance instance = VK_NULL_HANDLE; VkPhysicalDevice physical_device = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE; bool is_device_lost = false; - std::vector object_dispatch; - std::vector aborted_object_dispatch; LayerObjectTypeId container_type; - void ReleaseDeviceDispatchObject(LayerObjectTypeId type_id) const; - void ReleaseAllDispatchObjects() const; - - vvl::concurrent_unordered_map>, 0> deferred_operation_post_completion; - vvl::concurrent_unordered_map&)>>, 0> - deferred_operation_post_check; - vvl::concurrent_unordered_map, 0> deferred_operation_pipelines; std::string layer_name = "CHASSIS"; - ValidationObject() {} + ValidationObject( ) {} virtual ~ValidationObject() {} - void InitObjectDispatchVectors(); - mutable std::shared_mutex validation_object_mutex; virtual ReadLockGuard ReadLock() const { return ReadLockGuard(validation_object_mutex); } virtual WriteLockGuard WriteLock() { return WriteLockGuard(validation_object_mutex); } @@ -491,18 +631,6 @@ class ValidationObject { } } - ValidationObject* GetValidationObject(LayerObjectTypeId object_type) const { - for (auto validation_object : object_dispatch) { - if (validation_object->container_type == object_type) { - return validation_object; - } - } - return nullptr; - } - - template - ValidationObjectType* GetValidationObject() const; - // Debug Logging Helpers bool DECORATE_PRINTF(5, 6) LogError(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { @@ -514,7 +642,8 @@ class ValidationObject { } // Currently works like LogWarning, but allows developer to better categorize the warning - bool DECORATE_PRINTF(5, 6) LogUndefinedValue(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + bool DECORATE_PRINTF(5, 6) LogUndefinedValue(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, + const char* format, ...) const { va_list argptr; va_start(argptr, format); const bool result = debug_report->LogMsg(kWarningBit, objlist, loc, vuid_text, format, argptr); @@ -522,7 +651,8 @@ class ValidationObject { return result; } - bool DECORATE_PRINTF(5, 6) LogWarning(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + bool DECORATE_PRINTF(5, 6) + LogWarning(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { va_list argptr; va_start(argptr, format); const bool result = debug_report->LogMsg(kWarningBit, objlist, loc, vuid_text, format, argptr); @@ -530,7 +660,8 @@ class ValidationObject { return result; } - bool DECORATE_PRINTF(5, 6) LogPerformanceWarning(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + bool DECORATE_PRINTF(5, 6) LogPerformanceWarning(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, + const char* format, ...) const { va_list argptr; va_start(argptr, format); const bool result = debug_report->LogMsg(kPerformanceWarningBit, objlist, loc, vuid_text, format, argptr); @@ -538,7 +669,8 @@ class ValidationObject { return result; } - bool DECORATE_PRINTF(5, 6) LogInfo(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + bool DECORATE_PRINTF(5, 6) + LogInfo(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { va_list argptr; va_start(argptr, format); const bool result = debug_report->LogMsg(kInformationBit, objlist, loc, vuid_text, format, argptr); @@ -546,7 +678,8 @@ class ValidationObject { return result; } - bool DECORATE_PRINTF(5, 6) LogVerbose(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { + bool DECORATE_PRINTF(5, 6) + LogVerbose(std::string_view vuid_text, const LogObjectList& objlist, const Location& loc, const char* format, ...) const { va_list argptr; va_start(argptr, format); const bool result = debug_report->LogMsg(kVerboseBit, objlist, loc, vuid_text, format, argptr); @@ -554,64 +687,13 @@ class ValidationObject { return result; } - void LogInternalError(std::string_view failure_location, const LogObjectList& obj_list, const Location& loc, std::string_view entrypoint, - VkResult err) const { + void LogInternalError(std::string_view failure_location, const LogObjectList& obj_list, const Location& loc, + std::string_view entrypoint, VkResult err) const { const std::string_view err_string = string_VkResult(err); std::string vuid = "INTERNAL-ERROR-"; vuid += entrypoint; LogError(vuid, obj_list, loc, "at %s: %s() was called in the Validation Layer state tracking and failed with result = %s.", - failure_location.data(), entrypoint.data(), err_string.data()); - } - - // Handle Wrapping Data - // Reverse map display handles - vvl::concurrent_unordered_map display_id_reverse_mapping; - // Wrapping Descriptor Template Update structures requires access to the template createinfo structs - vvl::unordered_map> desc_template_createinfo_map; - struct SubpassesUsageStates { - vvl::unordered_set subpasses_using_color_attachment; - vvl::unordered_set subpasses_using_depthstencil_attachment; - }; - // Uses unwrapped handles - vvl::unordered_map renderpasses_states; - // Map of wrapped swapchain handles to arrays of wrapped swapchain image IDs - // Each swapchain has an immutable list of wrapped swapchain image IDs -- always return these IDs if they exist - vvl::unordered_map> swapchain_wrapped_image_handle_map; - // Map of wrapped descriptor pools to set of wrapped descriptor sets allocated from each pool - vvl::unordered_map> pool_descriptor_sets_map; - - // Unwrap a handle. - template - HandleType Unwrap(HandleType wrapped_handle) { - if (wrapped_handle == (HandleType)VK_NULL_HANDLE) return wrapped_handle; - auto iter = unique_id_mapping.find(CastToUint64(wrapped_handle)); - if (iter == unique_id_mapping.end()) return (HandleType)0; - return (HandleType)iter->second; - } - - // Wrap a newly created handle with a new unique ID, and return the new ID. - template - HandleType WrapNew(HandleType new_created_handle) { - if (new_created_handle == (HandleType)VK_NULL_HANDLE) return new_created_handle; - auto unique_id = global_unique_id++; - unique_id = HashedUint64::hash(unique_id); - assert(unique_id != 0); // can't be 0, otherwise unwrap will apply special rule for VK_NULL_HANDLE - unique_id_mapping.insert_or_assign(unique_id, CastToUint64(new_created_handle)); - return (HandleType)unique_id; - } - - // VkDisplayKHR objects are statically created in the driver at VkCreateInstance. - // They live with the PhyiscalDevice and apps never created/destroy them. - // Apps needs will query for them and the first time we see it we wrap it - VkDisplayKHR MaybeWrapDisplay(VkDisplayKHR handle) { - // See if this display is already known - auto it = display_id_reverse_mapping.find(handle); - if (it != display_id_reverse_mapping.end()) return (VkDisplayKHR)it->second; - - // First time see this VkDisplayKHR, so wrap - const uint64_t unique_id = (uint64_t)WrapNew(handle); - display_id_reverse_mapping.insert_or_assign(handle, unique_id); - return (VkDisplayKHR)unique_id; + failure_location.data(), entrypoint.data(), err_string.data()); } ''') @@ -727,7 +809,7 @@ class ValidationObject { // clang-format on ''') - out.append('extern small_unordered_map layer_data_map;') + out.append('extern small_unordered_map layer_data_map;') self.write("".join(out)) def generateSource(self): @@ -747,7 +829,7 @@ def generateSource(self): thread_local WriteLockGuard* ValidationObject::record_guard{}; - small_unordered_map layer_data_map; + small_unordered_map layer_data_map; // Global unique object identifier. std::atomic global_unique_id(1ULL); @@ -808,7 +890,7 @@ def generateSource(self): out.append('}\n') out.append(''' - static void InitDeviceObjectDispatch(ValidationObject *instance_interceptor, ValidationObject *device_interceptor) { + static void InitDeviceDispatchObject(DispatchObject *instance_interceptor, DispatchObject *device_interceptor) { auto disables = instance_interceptor->disabled; auto enables = instance_interceptor->enabled; @@ -845,8 +927,17 @@ def generateSource(self): return custom_stype_info; } + ValidationObject* DispatchObject::GetValidationObject(LayerObjectTypeId object_type) const { + for (auto validation_object : object_dispatch) { + if (validation_object->container_type == object_type) { + return validation_object; + } + } + return nullptr; + } + template - ValidationObjectType* ValidationObject::GetValidationObject() const { + ValidationObjectType* DispatchObject::GetValidationObject() const { LayerObjectTypeId type_id; if constexpr (std::is_same_v) { type_id = LayerObjectTypeThreading; @@ -862,23 +953,22 @@ def generateSource(self): return static_cast(GetValidationObject(type_id)); } - template ThreadSafety* ValidationObject::GetValidationObject() const; - template StatelessValidation* ValidationObject::GetValidationObject() const; - template ObjectLifetimes* ValidationObject::GetValidationObject() const; - template CoreChecks* ValidationObject::GetValidationObject() const; + template ThreadSafety* DispatchObject::GetValidationObject() const; + template StatelessValidation* DispatchObject::GetValidationObject() const; + template ObjectLifetimes* DispatchObject::GetValidationObject() const; + template CoreChecks* DispatchObject::GetValidationObject() const; - // Takes the layer and removes it from the chassis so it will not be called anymore + // Takes the validation type and removes it from the chassis so it will not be called anymore // Designed for things like GPU-AV to remove itself while keeping everything else alive - void ValidationObject::ReleaseDeviceDispatchObject(LayerObjectTypeId type_id) const { - auto layer_data = GetLayerDataPtr(GetDispatchKey(device), layer_data_map); - for (auto object_it = layer_data->object_dispatch.begin(); object_it != layer_data->object_dispatch.end(); object_it++) { + void DispatchObject::ReleaseDeviceValidationObject(LayerObjectTypeId type_id) const { + for (auto object_it = object_dispatch.begin(); object_it != object_dispatch.end(); object_it++) { if ((*object_it)->container_type == type_id) { ValidationObject* object = *object_it; - layer_data->object_dispatch.erase(object_it); + object_dispatch.erase(object_it); - for (auto intercept_vector_it = layer_data->intercept_vectors.begin(); - intercept_vector_it != layer_data->intercept_vectors.end(); intercept_vector_it++) { + for (auto intercept_vector_it = intercept_vectors.begin(); + intercept_vector_it != intercept_vectors.end(); intercept_vector_it++) { for (auto intercept_object_it = intercept_vector_it->begin(); intercept_object_it != intercept_vector_it->end(); intercept_object_it++) { if (object == *intercept_object_it) { @@ -890,7 +980,7 @@ def generateSource(self): // We can't destroy the object itself now as it might be unsafe (things are still being used) // If the rare case happens we need to release, we will cleanup later when we normally would have cleaned this up - layer_data->aborted_object_dispatch.push_back(object); + aborted_object_dispatch.push_back(object); break; } } @@ -898,21 +988,18 @@ def generateSource(self): // Incase we need to teardown things early, we want to do it safely, so we will keep the entrypoints into layer, but just remove all // the internal chassis hooks so that any call becomes a no-op (but still dispatches into the driver) - void ValidationObject::ReleaseAllDispatchObjects() const { - assert(container_type == LayerObjectTypeInstance || container_type == LayerObjectTypeDevice); - auto dispatch_key = container_type == LayerObjectTypeInstance ? GetDispatchKey(instance) : GetDispatchKey(device); - auto layer_data = GetLayerDataPtr(dispatch_key, layer_data_map); + void DispatchObject::ReleaseAllValidationObjects() const { // Some chassis loops use the intercept_vectors instead of looking up the object - for (auto& intercept_vector : layer_data->intercept_vectors) { + for (auto& intercept_vector : intercept_vectors) { intercept_vector.clear(); } - for (auto object_it = layer_data->object_dispatch.begin(); object_it != layer_data->object_dispatch.end(); object_it++) { + for (auto object_it = object_dispatch.begin(); object_it != object_dispatch.end(); object_it++) { ValidationObject* object = *object_it; - layer_data->aborted_object_dispatch.push_back(object); + aborted_object_dispatch.push_back(object); } - layer_data->object_dispatch.clear(); + object_dispatch.clear(); } namespace vulkan_layer_chassis { @@ -938,7 +1025,7 @@ def generateSource(self): // Manually written functions // Check enabled instance extensions against supported instance extension whitelist - static void InstanceExtensionWhitelist(ValidationObject* layer_data, const VkInstanceCreateInfo* pCreateInfo, VkInstance instance) { + static void InstanceExtensionWhitelist(DispatchObject* layer_data, const VkInstanceCreateInfo* pCreateInfo, VkInstance instance) { for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { // Check for recognized instance extensions vvl::Extension extension = GetExtension(pCreateInfo->ppEnabledExtensionNames[i]); @@ -953,7 +1040,7 @@ def generateSource(self): } // Check enabled device extensions against supported device extension whitelist - static void DeviceExtensionWhitelist(ValidationObject* layer_data, const VkDeviceCreateInfo* pCreateInfo, VkDevice device) { + static void DeviceExtensionWhitelist(DispatchObject* layer_data, const VkDeviceCreateInfo* pCreateInfo, VkDevice device) { for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { // Check for recognized device extensions vvl::Extension extension = GetExtension(pCreateInfo->ppEnabledExtensionNames[i]); @@ -967,7 +1054,7 @@ def generateSource(self): } } - void OutputLayerStatusInfo(ValidationObject* context) { + void OutputLayerStatusInfo(DispatchObject* context) { std::string list_of_enables; std::string list_of_disables; for (uint32_t i = 0; i < kMaxEnableFlags; i++) { @@ -1091,8 +1178,25 @@ def generateSource(self): return layer_data->instance_dispatch_table.EnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pCount, pProperties); } + // This is here as some applications will call exit() which results in all our static allocations (like std::map) having their + // destructor called and destroyed from under us. It is not possible to detect as sometimes (when using things like robin hood) the + // size()/empty() will give false positive that memory is there there. We add this global hook that will go through and remove all + // the function calls such that things can safely run in the case the applicaiton still wants to make Vulkan calls in their atexit() + // handler + void ApplicationAtExit() { + // On a "normal" application, this function is called after vkDestroyInstance and layer_data_map is empty + // + // If there are multiple devices we still want to delete them all as exit() is a global scope call + for (auto object : layer_data_map) { + object.second->ReleaseAllValidationObjects(); + } + } + VKAPI_ATTR VkResult VKAPI_CALL CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { + + atexit(ApplicationAtExit); + VVL_ZoneScoped; VkLayerInstanceCreateInfo* chain_info = GetChainInfo(pCreateInfo, VK_LAYER_LINK_INFO); @@ -1129,20 +1233,34 @@ def generateSource(self): wrap_handles = false; } + DispatchObject* framework = new DispatchObject(); + + framework->api_version = api_version; + framework->object_dispatch = local_object_dispatch; + framework->disabled = local_disables; + framework->enabled = local_enables; + framework->global_settings = local_global_settings; + framework->gpuav_settings = local_gpuav_settings; + framework->syncval_settings = local_syncval_settings; + framework->debug_report = debug_report; + framework->instance_extensions.InitFromInstanceCreateInfo(specified_version, pCreateInfo); + // Initialize the validation objects for (auto* intercept : local_object_dispatch) { intercept->api_version = api_version; intercept->debug_report = debug_report; + intercept->dispatch_ = framework; } // Define logic to cleanup everything in case of an error - auto cleanup_allocations = [debug_report, &local_object_dispatch]() { + auto cleanup_allocations = [debug_report, framework, &local_object_dispatch]() { DeactivateInstanceDebugCallbacks(debug_report); vku::FreePnextChain(debug_report->instance_pnext_chain); LayerDebugUtilsDestroyInstance(debug_report); for (ValidationObject* object : local_object_dispatch) { delete object; } + delete framework; }; // Init dispatch array and call registration functions @@ -1169,26 +1287,15 @@ def generateSource(self): return result; } record_obj.result = result; - auto framework = GetLayerDataPtr(GetDispatchKey(*pInstance), layer_data_map); - - framework->object_dispatch = local_object_dispatch; - framework->container_type = LayerObjectTypeInstance; - framework->disabled = local_disables; - framework->enabled = local_enables; - framework->global_settings = local_global_settings; - framework->gpuav_settings = local_gpuav_settings; - framework->syncval_settings = local_syncval_settings; - framework->instance = *pInstance; + layer_init_instance_dispatch_table(*pInstance, &framework->instance_dispatch_table, fpGetInstanceProcAddr); - framework->debug_report = debug_report; - framework->api_version = api_version; - framework->instance_extensions.InitFromInstanceCreateInfo(specified_version, pCreateInfo); // We need to call this to properly check which device extensions have been promoted when validating query functions // that take as input a physical device, which can be called before a logical device has been created. framework->device_extensions.InitFromDeviceCreateInfo(&framework->instance_extensions, specified_version); + layer_data_map[GetDispatchKey(*pInstance)] = framework; OutputLayerStatusInfo(framework); for (auto* intercept : framework->object_dispatch) { @@ -1199,6 +1306,8 @@ def generateSource(self): intercept->gpuav_settings = framework->gpuav_settings; intercept->syncval_settings = framework->syncval_settings; intercept->instance = *pInstance; + intercept->debug_report = debug_report; + intercept->api_version = api_version; } for (ValidationObject* intercept : framework->object_dispatch) { @@ -1287,10 +1396,21 @@ def generateSource(self): // Setup the validation tables based on the application API version from the instance and the capabilities of the device driver auto effective_api_version = std::min(APIVersion(device_properties.apiVersion), instance_interceptor->api_version); - DeviceExtensions device_extensions = {}; - device_extensions.InitFromDeviceCreateInfo(&instance_interceptor->instance_extensions, effective_api_version, pCreateInfo); - for (auto item : instance_interceptor->object_dispatch) { - item->device_extensions = device_extensions; + + DispatchObject* device_interceptor = new DispatchObject(); + + device_interceptor->device_extensions.InitFromDeviceCreateInfo(&instance_interceptor->instance_extensions, + effective_api_version, pCreateInfo); + device_interceptor->instance_dispatch_table = instance_interceptor->instance_dispatch_table; + device_interceptor->instance_extensions = instance_interceptor->instance_extensions; + device_interceptor->physical_device = gpu; + device_interceptor->instance = instance_interceptor->instance; + device_interceptor->debug_report = instance_interceptor->debug_report; + + // This is odd but we need to set the current device_extensions in all of the + // instance validation objects so that they are available for validating CreateDevice + for (auto* object : instance_interceptor->object_dispatch) { + object->device_extensions = device_interceptor->device_extensions; } // Make copy to modify as some ValidationObjects will want to add extensions/features on @@ -1301,7 +1421,10 @@ def generateSource(self): for (const ValidationObject* intercept : instance_interceptor->object_dispatch) { auto lock = intercept->ReadLock(); skip |= intercept->PreCallValidateCreateDevice(gpu, pCreateInfo, pAllocator, pDevice, error_obj); - if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; + if (skip) { + delete device_interceptor; + return VK_ERROR_VALIDATION_FAILED_EXT; + } } RecordObject record_obj(vvl::Func::vkCreateDevice); @@ -1312,31 +1435,26 @@ def generateSource(self): VkResult result = fpCreateDevice(gpu, reinterpret_cast(&modified_create_info), pAllocator, pDevice); if (result != VK_SUCCESS) { + delete device_interceptor; return result; } record_obj.result = result; - - auto device_interceptor = GetLayerDataPtr(GetDispatchKey(*pDevice), layer_data_map); - device_interceptor->container_type = LayerObjectTypeDevice; + device_interceptor->device = *pDevice; // Save local info in device object device_interceptor->api_version = device_interceptor->device_extensions.InitFromDeviceCreateInfo( &instance_interceptor->instance_extensions, effective_api_version, reinterpret_cast(&modified_create_info)); - device_interceptor->device_extensions = device_extensions; layer_init_device_dispatch_table(*pDevice, &device_interceptor->device_dispatch_table, fpGetDeviceProcAddr); - - device_interceptor->device = *pDevice; - device_interceptor->physical_device = gpu; - device_interceptor->instance = instance_interceptor->instance; - device_interceptor->debug_report = instance_interceptor->debug_report; + layer_data_map[GetDispatchKey(*pDevice)] = device_interceptor; instance_interceptor->debug_report->device_created++; - InitDeviceObjectDispatch(instance_interceptor, device_interceptor); + InitDeviceDispatchObject(instance_interceptor, device_interceptor); // Initialize all of the objects with the appropriate data for (auto* object : device_interceptor->object_dispatch) { + object->dispatch_ = device_interceptor; object->device = device_interceptor->device; object->physical_device = device_interceptor->physical_device; object->instance = instance_interceptor->instance;