| /*************************************************************************** |
| * |
| * Copyright (c) 2015-2026 The Khronos Group Inc. |
| * Copyright (c) 2015-2026 Valve Corporation |
| * Copyright (c) 2015-2026 LunarG, Inc. |
| * Copyright (c) 2015-2024 Google Inc. |
| * Copyright (c) 2023-2024 RasterGrid Kft. |
| * |
| * 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 "chassis.h" |
| |
| #include <cstring> |
| |
| #include "chassis/dispatch_object.h" |
| #include "generated/dispatch_vector.h" |
| #include "chassis/validation_object.h" |
| #include "layer_options.h" |
| #include "state_tracker/descriptor_sets.h" |
| #include "chassis/chassis_modification_state.h" |
| #include "core_checks/core_validation.h" |
| #include "profiling/profiling.h" |
| #include "containers/small_vector.h" |
| #include "utils/dispatch_utils.h" |
| |
| #define STRINGIFY(s) STRINGIFY_HELPER(s) |
| #define STRINGIFY_HELPER(s) #s |
| |
| namespace vulkan_layer_chassis { |
| |
| // Check enabled instance extensions against supported instance extension whitelist |
| static void InstanceExtensionWhitelist(vvl::DispatchInstance* layer_data, const VkInstanceCreateInfo* pCreateInfo, |
| VkInstance instance) { |
| Location loc(vvl::Func::vkCreateInstance); |
| for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { |
| vvl::Extension extension = GetExtension(pCreateInfo->ppEnabledExtensionNames[i]); |
| if (extension == vvl::Extension::Empty) { |
| layer_data->LogWarning("WARNING-CreateInstance-extension-not-found", layer_data->instance, |
| loc.dot(vvl::Field::pCreateInfo).dot(vvl::Field::ppEnabledExtensionNames, i), |
| "%s is not a known extension string. Please make sure the spelling is correct. (If working on a " |
| "new extension, the validation code generation needs to be ran with the new vk.xml)", |
| pCreateInfo->ppEnabledExtensionNames[i]); |
| } else if (!IsInstanceExtension(extension)) { |
| layer_data->LogWarning( |
| "WARNING-CreateInstance-extension-wrong-type", layer_data->instance, |
| loc.dot(vvl::Field::pCreateInfo).dot(vvl::Field::ppEnabledExtensionNames, i), |
| "%s is a device extension, not a instance extension. It should be set later during vkCreateDevice() time. (If you " |
| "are trying to enable the device extensions because it has some physical device level info, it is valid to query " |
| "the VkPhysicalDevice regardless of the extensions it supports.)", |
| pCreateInfo->ppEnabledExtensionNames[i]); |
| } |
| } |
| } |
| |
| // Check enabled device extensions against supported device extension whitelist |
| static void DeviceExtensionWhitelist(vvl::DispatchDevice* layer_data, const VkDeviceCreateInfo* pCreateInfo, VkDevice device) { |
| Location loc(vvl::Func::vkCreateDevice); |
| for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { |
| vvl::Extension extension = GetExtension(pCreateInfo->ppEnabledExtensionNames[i]); |
| if (extension == vvl::Extension::Empty) { |
| layer_data->LogWarning("WARNING-CreateDevice-extension-not-found", layer_data->device, |
| loc.dot(vvl::Field::pCreateInfo).dot(vvl::Field::ppEnabledExtensionNames, i), |
| "%s is not a known extension string. Please make sure the spelling is correct. (If working on a " |
| "new extension, the validation code generation needs to be ran with the new vk.xml)", |
| pCreateInfo->ppEnabledExtensionNames[i]); |
| } else if (!IsDeviceExtension(extension)) { |
| layer_data->LogWarning( |
| "WARNING-CreateDevice-extension-wrong-type", layer_data->device, |
| loc.dot(vvl::Field::pCreateInfo).dot(vvl::Field::ppEnabledExtensionNames, i), |
| "%s is a instance extension, not a device extension. It should be set before at vkCreateInstance().", |
| pCreateInfo->ppEnabledExtensionNames[i]); |
| } |
| } |
| } |
| |
| void OutputLayerStatusInfo(vvl::DispatchInstance* context) { |
| std::string list_of_enables; |
| std::string list_of_disables; |
| for (uint32_t i = 0; i < kMaxEnableFlags; i++) { |
| if (context->settings.enabled[i]) { |
| if (list_of_enables.size()) list_of_enables.append(", "); |
| list_of_enables.append(GetEnableFlagNameHelper()[i]); |
| } |
| } |
| if (list_of_enables.empty()) { |
| list_of_enables.append("None"); |
| } |
| for (uint32_t i = 0; i < kMaxDisableFlags; i++) { |
| if (context->settings.disabled[i]) { |
| if (list_of_disables.size()) list_of_disables.append(", "); |
| list_of_disables.append(GetDisableFlagNameHelper()[i]); |
| } |
| } |
| if (list_of_disables.empty()) { |
| list_of_disables.append("None"); |
| } |
| |
| Location loc(vvl::Func::vkCreateInstance); |
| // Output layer status information message |
| // TODO - We should just dump all settings to a file (see https://github.com/KhronosGroup/Vulkan-Utility-Libraries/issues/188) |
| context->LogInfo("WARNING-CreateInstance-status-message", context->instance, loc, |
| "Khronos Validation Layer Active:\n Current Enables: %s.\n Current Disables: %s.\n", |
| list_of_enables.c_str(), list_of_disables.c_str()); |
| |
| // Create warning message if user is running debug layers. |
| #ifndef NDEBUG |
| context->LogPerformanceWarning("WARNING-CreateInstance-debug-warning", context->instance, loc, |
| "Using debug builds of the validation layers *will* adversely affect performance."); |
| #endif |
| if (!context->settings.global_settings.fine_grained_locking) { |
| context->LogPerformanceWarning( |
| "WARNING-CreateInstance-locking-warning", context->instance, loc, |
| "Fine-grained locking is disabled, this will adversely affect performance of multithreaded applications."); |
| } |
| } |
| const vvl::unordered_map<std::string, function_data>& GetNameToFuncPtrMap(); |
| |
| VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice device, const char* funcName) { |
| auto layer_data = vvl::GetDispatchDevice(device); |
| if (!ApiParentExtensionEnabled(funcName, &layer_data->extensions)) { |
| return nullptr; |
| } |
| const auto& item = GetNameToFuncPtrMap().find(funcName); |
| if (item != GetNameToFuncPtrMap().end()) { |
| if (item->second.function_type != kFuncTypeDev) { |
| Location loc(vvl::Func::vkGetDeviceProcAddr); |
| // Was discussed in https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/6583 |
| // This has "valid" behavior to return null, but still worth warning users for this unqiue function |
| layer_data->LogWarning("WARNING-vkGetDeviceProcAddr-device", device, loc.dot(vvl::Field::pName), |
| "is trying to grab %s which is an instance level function", funcName); |
| return nullptr; |
| } else { |
| return reinterpret_cast<PFN_vkVoidFunction>(item->second.funcptr); |
| } |
| } |
| auto& table = layer_data->device_dispatch_table; |
| if (!table.GetDeviceProcAddr) return nullptr; |
| return table.GetDeviceProcAddr(device, funcName); |
| } |
| |
| VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetInstanceProcAddr(VkInstance instance, const char* funcName) { |
| const auto& item = GetNameToFuncPtrMap().find(funcName); |
| if (item != GetNameToFuncPtrMap().end()) { |
| return reinterpret_cast<PFN_vkVoidFunction>(item->second.funcptr); |
| } |
| // Only global functions can pass in nullptr as the instance parameter - and GetNameToFuncPtrMap contains all global functions |
| if (instance == nullptr) return nullptr; |
| |
| auto layer_data = vvl::GetDispatchInstance(instance); |
| auto& table = layer_data->instance_dispatch_table; |
| if (!table.GetInstanceProcAddr) return nullptr; |
| return table.GetInstanceProcAddr(instance, funcName); |
| } |
| |
| VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetPhysicalDeviceProcAddr(VkInstance instance, const char* funcName) { |
| const auto& item = GetNameToFuncPtrMap().find(funcName); |
| if (item != GetNameToFuncPtrMap().end()) { |
| if (item->second.function_type != kFuncTypePdev) { |
| return nullptr; |
| } else { |
| return reinterpret_cast<PFN_vkVoidFunction>(item->second.funcptr); |
| } |
| } |
| auto layer_data = vvl::GetDispatchInstance(instance); |
| auto& table = layer_data->instance_dispatch_table; |
| if (!table.GetPhysicalDeviceProcAddr) return nullptr; |
| return table.GetPhysicalDeviceProcAddr(instance, funcName); |
| } |
| |
| // 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 |
| vvl::FreeAllDispatchObjects(); |
| } |
| |
| 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); |
| |
| assert(chain_info->u.pLayerInfo); |
| PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; |
| PFN_vkCreateInstance fpCreateInstance = (PFN_vkCreateInstance)fpGetInstanceProcAddr(nullptr, "vkCreateInstance"); |
| if (fpCreateInstance == nullptr) return VK_ERROR_INITIALIZATION_FAILED; |
| chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; |
| |
| auto instance_dispatch = std::make_unique<vvl::DispatchInstance>(pCreateInfo); |
| |
| // Init dispatch array and call registration functions |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateInstance, VulkanTypedHandle()); |
| for (const auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| skip |= vo->PreCallValidateCreateInstance(pCreateInfo, pAllocator, pInstance, error_obj); |
| if (skip) { |
| return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| RecordObject record_obj(vvl::Func::vkCreateInstance); |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PreCallRecordCreateInstance(pCreateInfo, pAllocator, pInstance, record_obj); |
| } |
| |
| VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance); |
| if (result != VK_SUCCESS) { |
| return result; |
| } |
| record_obj.result = result; |
| instance_dispatch->instance = *pInstance; |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->CopyDispatchState(); |
| } |
| |
| layer_init_instance_dispatch_table(*pInstance, &instance_dispatch->instance_dispatch_table, fpGetInstanceProcAddr); |
| |
| OutputLayerStatusInfo(instance_dispatch.get()); |
| InstanceExtensionWhitelist(instance_dispatch.get(), pCreateInfo, *pInstance); |
| instance_dispatch->FindSupportedExtensions(); |
| |
| // save a raw pointer since the unique_ptr will be invalidate by the move() below |
| auto* id = instance_dispatch.get(); |
| vvl::SetDispatchInstance(*pInstance, std::move(instance_dispatch)); |
| |
| for (auto& vo : id->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PostCallRecordCreateInstance(pCreateInfo, pAllocator, pInstance, record_obj); |
| vo->CopyExtensions(); |
| } |
| |
| DeactivateInstanceDebugCallbacks(id->debug_report); |
| |
| return result; |
| } |
| |
| VKAPI_ATTR void VKAPI_CALL DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) { |
| VVL_TracyCZone(tracy_zone_precall, true); |
| auto* key = GetDispatchKey(instance); |
| auto instance_dispatch = vvl::GetDispatchInstance(instance); |
| ActivateInstanceDebugCallbacks(instance_dispatch->debug_report); |
| ErrorObject error_obj(vvl::Func::vkDestroyInstance, VulkanTypedHandle(instance, kVulkanObjectTypeInstance)); |
| |
| for (const auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PreCallValidateDestroyInstance(instance, pAllocator, error_obj); |
| } |
| vvl::BaseInstance* state_tracker = nullptr; |
| RecordObject record_obj(vvl::Func::vkDestroyInstance); |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| if (vo->container_type == LayerObjectTypeStateTracker) { |
| state_tracker = vo.get(); |
| continue; |
| } |
| vo->PreCallRecordDestroyInstance(instance, pAllocator, record_obj); |
| } |
| if (state_tracker) { |
| state_tracker->PreCallRecordDestroyInstance(instance, pAllocator, record_obj); |
| } |
| |
| VVL_TracyCZoneEnd(tracy_zone_precall); |
| VVL_TracyCZone(tracy_zone_dispatch, true); |
| instance_dispatch->instance_dispatch_table.DestroyInstance(instance, pAllocator); |
| VVL_TracyCZoneEnd(tracy_zone_dispatch); |
| |
| VVL_TracyCZone(tracy_zone_postcall, true); |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| if (vo->container_type == LayerObjectTypeStateTracker) { |
| continue; |
| } |
| vo->PostCallRecordDestroyInstance(instance, pAllocator, record_obj); |
| } |
| if (state_tracker) { |
| state_tracker->PostCallRecordDestroyInstance(instance, pAllocator, record_obj); |
| } |
| |
| DeactivateInstanceDebugCallbacks(instance_dispatch->debug_report); |
| vvl::FreeDispatchInstance(key); |
| |
| VVL_TracyCZoneEnd(tracy_zone_postcall); |
| |
| #if TRACY_MANUAL_LIFETIME |
| tracy::ShutdownProfiler(); |
| #endif |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL CreateDevice(VkPhysicalDevice gpu, const VkDeviceCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { |
| VkLayerDeviceCreateInfo* chain_info = GetChainInfo(pCreateInfo, VK_LAYER_LINK_INFO); |
| |
| auto instance_dispatch = vvl::GetDispatchInstance(gpu); |
| |
| PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr; |
| PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr; |
| PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(instance_dispatch->instance, "vkCreateDevice"); |
| if (fpCreateDevice == nullptr) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext; |
| |
| // use a unique pointer to make sure we destroy this object on error |
| auto device_dispatch = std::make_unique<vvl::DispatchDevice>(instance_dispatch, gpu, pCreateInfo); |
| |
| // This is odd but we need to set the current extensions in all of the |
| // instance validation objects so that they are available for validating CreateDevice |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->extensions = device_dispatch->extensions; |
| } |
| |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateDevice, VulkanTypedHandle(gpu, kVulkanObjectTypePhysicalDevice)); |
| for (const auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| skip |= vo->PreCallValidateCreateDevice(gpu, pCreateInfo, pAllocator, pDevice, error_obj); |
| if (skip) { |
| return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| // Make copy to modify as some ValidationObjects will want to add extensions/features on |
| // After PreCallValidate incase it is invalid |
| vku::safe_VkDeviceCreateInfo modified_create_info(pCreateInfo); |
| |
| RecordObject record_obj(vvl::Func::vkCreateDevice); |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PreCallRecordCreateDevice(gpu, pCreateInfo, pAllocator, pDevice, record_obj, &modified_create_info); |
| } |
| |
| VkResult result = fpCreateDevice(gpu, reinterpret_cast<VkDeviceCreateInfo*>(&modified_create_info), pAllocator, pDevice); |
| |
| if (result != VK_SUCCESS) { |
| // From https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/10472 |
| // While it is not invalid to have unsupported features, we use this time to help the developer bisect what they added that |
| // caused the error from the driver |
| // |
| // Note that VK_ERROR_EXTENSION_NOT_PRESENT will be validated and reported in the Vulkan-Loader |
| if (result == VK_ERROR_FEATURE_NOT_PRESENT) { |
| instance_dispatch->ReportErrorFeatureNotPresent(gpu, *modified_create_info.ptr()); |
| } |
| |
| // If not successful, don't keep going as things will likely crash |
| return result; |
| } |
| |
| record_obj.result = result; |
| device_dispatch->device = *pDevice; |
| |
| layer_init_device_dispatch_table(*pDevice, &device_dispatch->device_dispatch_table, fpGetDeviceProcAddr); |
| |
| instance_dispatch->debug_report->device_created++; |
| |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->CopyDispatchState(); |
| } |
| DeviceExtensionWhitelist(device_dispatch.get(), pCreateInfo, *pDevice); |
| // NOTE: many PostCallRecords expect to be able to look up the device dispatch object so we need to populate the map here. |
| #if defined(VVL_TRACY_GPU) |
| InitTracyVk(instance_dispatch->instance, gpu, *pDevice, fpGetInstanceProcAddr, fpGetDeviceProcAddr, |
| device_dispatch->device_dispatch_table); |
| #endif |
| |
| vvl::SetDispatchDevice(*pDevice, std::move(device_dispatch)); |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| // Send down modified create info as we want to mark enabled features that we sent down on behalf of the app |
| vo->PostCallRecordCreateDevice(gpu, modified_create_info.ptr(), pAllocator, pDevice, record_obj); |
| } |
| // Note: device_dispatch is no longer valid since it was a std::move source above. |
| for (auto& vo : vvl::GetDispatchDevice(*pDevice)->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->FinishDeviceSetup(modified_create_info.ptr(), record_obj.location); |
| } |
| |
| return result; |
| } |
| |
| // NOTE: Do _not_ skip the dispatch call when destroying a device. Whether or not there was a validation error, |
| // the loader will destroy the device, and know nothing about future references to this device making it |
| // impossible for the caller to use this device handle further. IOW, this is our _only_ chance to (potentially) |
| // dispatch the driver's DestroyDevice function. |
| VKAPI_ATTR void VKAPI_CALL DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) { |
| auto* key = GetDispatchKey(device); |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| ErrorObject error_obj(vvl::Func::vkDestroyDevice, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| for (const auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PreCallValidateDestroyDevice(device, pAllocator, error_obj); |
| } |
| RecordObject record_obj(vvl::Func::vkDestroyDevice); |
| |
| // Even though layer object types reference the base device state tracker, |
| // it needs to be destroyed first: it stores the various object maps, |
| // those need to be destroyed by the time layer objects destroy their |
| // own device state. |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| |
| vo->PreCallRecordDestroyDevice(device, pAllocator, record_obj); |
| } |
| // Before device is destroyed, allow aborted objects to clean up |
| for (auto& vo : device_dispatch->aborted_object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PreCallRecordDestroyDevice(device, pAllocator, record_obj); |
| } |
| |
| #if defined(VVL_TRACY_GPU) |
| CleanupTracyVk(device); |
| #endif |
| |
| device_dispatch->DestroyDevice(device, pAllocator); |
| |
| // The teardown order is |
| // 1. All active validation objects |
| // 2. All aborted validation objects (ex. if GPU-AV had to turn off mid run) |
| // 3. State Tracker validation object |
| // 4. Cleanup leaked handles that user didn't destroy |
| vvl::BaseDevice* state_tracker = nullptr; |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| if (vo->container_type == LayerObjectTypeStateTracker) { |
| state_tracker = vo.get(); |
| continue; |
| } |
| vo->PostCallRecordDestroyDevice(device, pAllocator, record_obj); |
| } |
| for (auto& vo : device_dispatch->aborted_object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PostCallRecordDestroyDevice(device, pAllocator, record_obj); |
| } |
| if (state_tracker) { |
| state_tracker->PostCallRecordDestroyDevice(device, pAllocator, record_obj); |
| } |
| |
| auto instance_dispatch = vvl::GetDispatchInstance(device_dispatch->physical_device); |
| instance_dispatch->debug_report->device_created--; |
| |
| vvl::FreeDispatchDevice(key); |
| } |
| |
| // Special-case APIs for which core_validation needs custom parameter lists and/or modifies parameters |
| |
| VKAPI_ATTR VkResult VKAPI_CALL CreateGraphicsPipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, |
| const VkGraphicsPipelineCreateInfo* pCreateInfos, |
| const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateGraphicsPipelines, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| PipelineStates pipeline_states; |
| chassis::CreateGraphicsPipelines chassis_state(pCreateInfos); |
| |
| { |
| VVL_ZoneScopedN("PreCallValidate_CreateGraphicsPipelines"); |
| for (const auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, |
| pPipelines, error_obj, pipeline_states, chassis_state); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| RecordObject record_obj(vvl::Func::vkCreateGraphicsPipelines); |
| { |
| VVL_ZoneScopedN("PreCallRecord_CreateGraphicsPipelines"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines, |
| record_obj, pipeline_states, chassis_state); |
| } |
| } |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_CreateGraphicsPipelines"); |
| result = device_dispatch->CreateGraphicsPipelines(device, pipelineCache, createInfoCount, chassis_state.pCreateInfos, |
| pAllocator, pPipelines); |
| |
| // If we have modified the pCreateInfos caused things to fail, revert to allow the app to continue |
| if (result < VK_SUCCESS && chassis_state.is_modified) { |
| chassis_state.is_modified = false; |
| result = device_dispatch->CreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, |
| pPipelines); |
| } |
| } |
| record_obj.result = result; |
| |
| { |
| VVL_ZoneScopedN("PostCallRecord_CreateGraphicsPipelines"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines, |
| record_obj, pipeline_states, chassis_state); |
| } |
| } |
| return result; |
| } |
| |
| // This API saves some core_validation pipeline state state on the stack for performance purposes |
| VKAPI_ATTR VkResult VKAPI_CALL CreateComputePipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, |
| const VkComputePipelineCreateInfo* pCreateInfos, |
| const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateComputePipelines, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| PipelineStates pipeline_states; |
| chassis::CreateComputePipelines chassis_state(pCreateInfos); |
| |
| { |
| VVL_ZoneScopedN("PreCallValidate_CreateComputePipelines"); |
| for (const auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, |
| pPipelines, error_obj, pipeline_states, chassis_state); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| RecordObject record_obj(vvl::Func::vkCreateComputePipelines); |
| { |
| VVL_ZoneScopedN("PreCallRecord_CreateComputePipelines"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines, |
| record_obj, pipeline_states, chassis_state); |
| } |
| } |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_CreateComputePipelines"); |
| result = device_dispatch->CreateComputePipelines(device, pipelineCache, createInfoCount, chassis_state.pCreateInfos, |
| pAllocator, pPipelines); |
| // If we have modified the pCreateInfos caused things to fail, revert to allow the app to continue |
| if (result < VK_SUCCESS && chassis_state.is_modified) { |
| chassis_state.is_modified = false; |
| result = device_dispatch->CreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, |
| pPipelines); |
| } |
| } |
| record_obj.result = result; |
| |
| { |
| VVL_ZoneScopedN("PostCallRecord_CreateComputePipelines"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordCreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines, |
| record_obj, pipeline_states, chassis_state); |
| } |
| } |
| return result; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL CreateRayTracingPipelinesNV(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, |
| const VkRayTracingPipelineCreateInfoNV* pCreateInfos, |
| const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) { |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateRayTracingPipelinesNV, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| PipelineStates pipeline_states; |
| |
| for (const auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCreateRayTracingPipelinesNV(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, |
| pPipelines, error_obj, pipeline_states); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| |
| RecordObject record_obj(vvl::Func::vkCreateRayTracingPipelinesNV); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCreateRayTracingPipelinesNV(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines, |
| record_obj, pipeline_states); |
| } |
| |
| VkResult result; |
| result = |
| device_dispatch->CreateRayTracingPipelinesNV(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines); |
| record_obj.result = result; |
| |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordCreateRayTracingPipelinesNV(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines, |
| record_obj, pipeline_states); |
| } |
| return result; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL CreateRayTracingPipelinesKHR(VkDevice device, VkDeferredOperationKHR deferredOperation, |
| VkPipelineCache pipelineCache, uint32_t createInfoCount, |
| const VkRayTracingPipelineCreateInfoKHR* pCreateInfos, |
| const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateRayTracingPipelinesKHR, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| PipelineStates pipeline_states; |
| auto chassis_state = std::make_shared<chassis::CreateRayTracingPipelinesKHR>(pCreateInfos); |
| |
| { |
| VVL_ZoneScopedN("PreCallValidate_CreateRayTracingPipelinesKHR"); |
| for (const auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCreateRayTracingPipelinesKHR(device, deferredOperation, pipelineCache, createInfoCount, |
| pCreateInfos, pAllocator, pPipelines, error_obj, |
| pipeline_states, *chassis_state); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| RecordObject record_obj(vvl::Func::vkCreateRayTracingPipelinesKHR); |
| { |
| VVL_ZoneScopedN("PreCallRecord_CreateRayTracingPipelinesKHR"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCreateRayTracingPipelinesKHR(device, deferredOperation, pipelineCache, createInfoCount, pCreateInfos, |
| pAllocator, pPipelines, record_obj, pipeline_states, *chassis_state); |
| } |
| } |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_CreateRayTracingPipelinesKHR"); |
| result = device_dispatch->CreateRayTracingPipelinesKHR(device, deferredOperation, pipelineCache, createInfoCount, |
| chassis_state->pCreateInfos, pAllocator, pPipelines); |
| |
| // If we have modified the pCreateInfos caused things to fail, revert to allow the app to continue |
| // Note VK_OPERATION_DEFERRED_KHR is not "failing" as the pipeline will still be created |
| if (result < VK_SUCCESS && chassis_state->is_modified) { |
| chassis_state->is_modified = false; |
| result = device_dispatch->CreateRayTracingPipelinesKHR(device, deferredOperation, pipelineCache, createInfoCount, |
| pCreateInfos, pAllocator, pPipelines); |
| } |
| } |
| record_obj.result = result; |
| |
| { |
| VVL_ZoneScopedN("PostCallRecord_CreateRayTracingPipelinesKHR"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordCreateRayTracingPipelinesKHR(device, deferredOperation, pipelineCache, createInfoCount, pCreateInfos, |
| pAllocator, pPipelines, record_obj, pipeline_states, chassis_state); |
| } |
| } |
| return result; |
| } |
| |
| // This API saves some core_validation pipeline state state on the stack for performance purposes |
| VKAPI_ATTR VkResult VKAPI_CALL CreateDataGraphPipelinesARM(VkDevice device, VkDeferredOperationKHR deferredOperation, |
| VkPipelineCache pipelineCache, uint32_t createInfoCount, |
| const VkDataGraphPipelineCreateInfoARM* pCreateInfos, |
| const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateDataGraphPipelinesARM, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| PipelineStates pipeline_states; |
| chassis::CreateDataGraphPipelinesARM chassis_state(pCreateInfos); |
| |
| { |
| VVL_ZoneScopedN("PreCallValidate"); |
| for (const auto& vo : device_dispatch->object_dispatch) { |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCreateDataGraphPipelinesARM(device, deferredOperation, pipelineCache, createInfoCount, |
| pCreateInfos, pAllocator, pPipelines, error_obj, pipeline_states, |
| chassis_state); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| RecordObject record_obj(vvl::Func::vkCreateDataGraphPipelinesARM); |
| { |
| VVL_ZoneScopedN("PreCallRecord"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCreateDataGraphPipelinesARM(device, deferredOperation, pipelineCache, createInfoCount, pCreateInfos, |
| pAllocator, pPipelines, record_obj, pipeline_states, chassis_state); |
| } |
| } |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch"); |
| result = device_dispatch->CreateDataGraphPipelinesARM(device, deferredOperation, pipelineCache, createInfoCount, |
| chassis_state.pCreateInfos, pAllocator, pPipelines); |
| } |
| record_obj.result = result; |
| |
| { |
| VVL_ZoneScopedN("PostCallRecord"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordCreateDataGraphPipelinesARM(device, deferredOperation, pipelineCache, createInfoCount, pCreateInfos, |
| pAllocator, pPipelines, record_obj, pipeline_states, chassis_state); |
| } |
| } |
| return result; |
| } |
| |
| // This API needs the ability to modify a down-chain parameter |
| VKAPI_ATTR VkResult VKAPI_CALL CreatePipelineLayout(VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreatePipelineLayout, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| { |
| VVL_ZoneScopedN("PreCallValidate_CreatePipelineLayout"); |
| for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateCreatePipelineLayout]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCreatePipelineLayout(device, pCreateInfo, pAllocator, pPipelineLayout, error_obj); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| chassis::CreatePipelineLayout chassis_state{}; |
| chassis_state.modified_create_info = *pCreateInfo; |
| |
| RecordObject record_obj(vvl::Func::vkCreatePipelineLayout); |
| { |
| VVL_ZoneScopedN("PreCallRecord_CreatePipelineLayout"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCreatePipelineLayout(device, pCreateInfo, pAllocator, pPipelineLayout, record_obj, chassis_state); |
| } |
| } |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_CreatePipelineLayout"); |
| result = device_dispatch->CreatePipelineLayout(device, &chassis_state.modified_create_info, pAllocator, pPipelineLayout); |
| } |
| record_obj.result = result; |
| |
| { |
| VVL_ZoneScopedN("PostCallRecord_CreatePipelineLayout"); |
| for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordCreatePipelineLayout]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordCreatePipelineLayout(device, pCreateInfo, pAllocator, pPipelineLayout, record_obj); |
| } |
| } |
| return result; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL GetShaderBinaryDataEXT(VkDevice device, VkShaderEXT shader, size_t* pDataSize, void* pData) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkGetShaderBinaryDataEXT, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| { |
| VVL_ZoneScopedN("PreCallValidate_vkGetShaderBinaryDataEXT"); |
| for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateGetShaderBinaryDataEXT]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateGetShaderBinaryDataEXT(device, shader, pDataSize, pData, error_obj); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| chassis::ShaderBinaryData chassis_state{shader}; |
| |
| RecordObject record_obj(vvl::Func::vkGetShaderBinaryDataEXT); |
| { |
| VVL_ZoneScopedN("PreCallRecord_vkGetShaderBinaryDataEXT"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordGetShaderBinaryDataEXT(device, shader, pDataSize, pData, record_obj, chassis_state); |
| } |
| } |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_vkGetShaderBinaryDataEXT"); |
| result = device_dispatch->GetShaderBinaryDataEXT(device, chassis_state.modified_shader_handle, pDataSize, pData); |
| } |
| record_obj.result = result; |
| { |
| VVL_ZoneScopedN("PostCallRecord_vkGetShaderBinaryDataEXT"); |
| for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordGetShaderBinaryDataEXT]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordGetShaderBinaryDataEXT(device, shader, pDataSize, pData, record_obj); |
| } |
| } |
| return result; |
| } |
| |
| // This API needs some local stack data for performance reasons and also may modify a parameter |
| VKAPI_ATTR VkResult VKAPI_CALL CreateShaderModule(VkDevice device, const VkShaderModuleCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, VkShaderModule* pShaderModule) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateShaderModule, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| { |
| VVL_ZoneScopedN("PreCallValidate_CreateShaderModule"); |
| for (const auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCreateShaderModule(device, pCreateInfo, pAllocator, pShaderModule, error_obj); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| chassis::CreateShaderModule chassis_state{}; |
| |
| RecordObject record_obj(vvl::Func::vkCreateShaderModule); |
| { |
| VVL_ZoneScopedN("PreCallRecord_CreateShaderModule"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCreateShaderModule(device, pCreateInfo, pAllocator, pShaderModule, record_obj, chassis_state); |
| } |
| } |
| |
| // Special extra check if SPIR-V itself fails runtime validation in PreCallRecord |
| if (chassis_state.skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_CreateShaderModule"); |
| result = device_dispatch->CreateShaderModule(device, pCreateInfo, pAllocator, pShaderModule); |
| } |
| record_obj.result = result; |
| { |
| VVL_ZoneScopedN("PostCallRecord_CreateShaderModule"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordCreateShaderModule(device, pCreateInfo, pAllocator, pShaderModule, record_obj, chassis_state); |
| } |
| } |
| return result; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL CreateShadersEXT(VkDevice device, uint32_t createInfoCount, |
| const VkShaderCreateInfoEXT* pCreateInfos, const VkAllocationCallbacks* pAllocator, |
| VkShaderEXT* pShaders) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateShadersEXT, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| chassis::ShaderObject chassis_state(createInfoCount, pCreateInfos); |
| |
| { |
| VVL_ZoneScopedN("PreCallValidate_CreateShadersEXT"); |
| for (const auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCreateShadersEXT(device, createInfoCount, pCreateInfos, pAllocator, pShaders, error_obj); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| RecordObject record_obj(vvl::Func::vkCreateShadersEXT); |
| { |
| VVL_ZoneScopedN("PreCallRecord_CreateShadersEXT"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCreateShadersEXT(device, createInfoCount, pCreateInfos, pAllocator, pShaders, record_obj, |
| chassis_state); |
| } |
| } |
| |
| // Special extra check if SPIR-V itself fails runtime validation in PreCallRecord |
| if (chassis_state.skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_CreateShadersEXT"); |
| result = device_dispatch->CreateShadersEXT(device, createInfoCount, chassis_state.pCreateInfos, pAllocator, pShaders); |
| |
| // If we have modified the pCreateInfos caused things to fail, revert to allow the app to continue |
| if (result < VK_SUCCESS && chassis_state.is_modified) { |
| chassis_state.is_modified = false; |
| result = device_dispatch->CreateShadersEXT(device, createInfoCount, pCreateInfos, pAllocator, pShaders); |
| } |
| } |
| record_obj.result = result; |
| |
| { |
| VVL_ZoneScopedN("PostCallRecord_CreateShadersEXT"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordCreateShadersEXT(device, createInfoCount, pCreateInfos, pAllocator, pShaders, record_obj, |
| chassis_state); |
| } |
| } |
| return result; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL AllocateDescriptorSets(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, |
| VkDescriptorSet* pDescriptorSets) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkAllocateDescriptorSets, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| // Because this is a high frequency function call and want to save time and populate this struct once. |
| // Both CoreCheck and Best Practice need this information during PreCallValidate time. |
| // During State Tracker PreCallValidate (instead of PreCallRecord), we populate this so that everyone else can use it. |
| vvl::AllocateDescriptorSetsData ads_state; |
| |
| { |
| VVL_ZoneScopedN("PreCallValidate_AllocateDescriptorSets"); |
| for (const auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateAllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets, error_obj, ads_state); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| RecordObject record_obj(vvl::Func::vkAllocateDescriptorSets); |
| { |
| VVL_ZoneScopedN("PreCallRecord_AllocateDescriptorSets"); |
| for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallRecordAllocateDescriptorSets]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordAllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets, record_obj); |
| } |
| } |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_AllocateDescriptorSets"); |
| result = device_dispatch->AllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets); |
| } |
| record_obj.result = result; |
| |
| { |
| VVL_ZoneScopedN("PostCallRecord_AllocateDescriptorSets"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordAllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets, record_obj, ads_state); |
| } |
| } |
| return result; |
| } |
| |
| // This API needs the ability to modify a down-chain parameter |
| VKAPI_ATTR VkResult VKAPI_CALL CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCreateBuffer, VulkanTypedHandle(device, kVulkanObjectTypeDevice)); |
| |
| { |
| VVL_ZoneScopedN("PreCallValidate_CreateBuffer"); |
| for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateCreateBuffer]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCreateBuffer(device, pCreateInfo, pAllocator, pBuffer, error_obj); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| chassis::CreateBuffer chassis_state{}; |
| chassis_state.create_info_copy = pCreateInfo; |
| |
| RecordObject record_obj(vvl::Func::vkCreateBuffer); |
| { |
| VVL_ZoneScopedN("PreCallRecord_CreateBuffer"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCreateBuffer(device, pCreateInfo, pAllocator, pBuffer, record_obj, chassis_state); |
| } |
| } |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_CreateBuffer"); |
| result = device_dispatch->CreateBuffer(device, chassis_state.create_info_copy, pAllocator, pBuffer); |
| } |
| record_obj.result = result; |
| |
| { |
| VVL_ZoneScopedN("PostCallRecord_CreateBuffer"); |
| for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordCreateBuffer]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| // If we don't pass into PostCallRecord, CoreCheck may give false positives when using GPU-AV |
| vo->PostCallRecordCreateBuffer(device, chassis_state.create_info_copy, pAllocator, pBuffer, record_obj); |
| } |
| } |
| return result; |
| } |
| |
| // This API needs to ensure that per-swapchain VkResult results are available |
| VKAPI_ATTR VkResult VKAPI_CALL QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(queue); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkQueuePresentKHR, VulkanTypedHandle(queue, kVulkanObjectTypeQueue)); |
| { |
| VVL_ZoneScopedN("PreCallValidate_QueuePresentKHR"); |
| for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateQueuePresentKHR]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateQueuePresentKHR(queue, pPresentInfo, error_obj); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| RecordObject record_obj(vvl::Func::vkQueuePresentKHR); |
| { |
| VVL_ZoneScopedN("PreCallRecord_QueuePresentKHR"); |
| for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallRecordQueuePresentKHR]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordQueuePresentKHR(queue, pPresentInfo, record_obj); |
| } |
| } |
| |
| // Track per-swapchain results when there is more than one swapchain and VkPresentInfoKHR::pResults is null |
| small_vector<VkResult, 2> present_results; |
| VkPresentInfoKHR modified_present_info; |
| if (pPresentInfo && pPresentInfo->swapchainCount > 1 && pPresentInfo->pResults == nullptr) { |
| present_results.resize(pPresentInfo->swapchainCount); |
| modified_present_info = *pPresentInfo; |
| modified_present_info.pResults = present_results.data(); |
| pPresentInfo = &modified_present_info; |
| } |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_QueuePresentKHR"); |
| result = device_dispatch->QueuePresentKHR(queue, pPresentInfo); |
| } |
| VVL_TracyCFrameMark; |
| #if defined(VVL_TRACY_GPU) |
| TracyVkCollector::GetTracyVkCollector(queue).Collect(); |
| #endif |
| record_obj.result = result; |
| { |
| VVL_ZoneScopedN("PostCallRecord_QueuePresentKHR"); |
| for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordQueuePresentKHR]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| |
| if (result == VK_ERROR_DEVICE_LOST) { |
| vo->is_device_lost = true; |
| } |
| vo->PostCallRecordQueuePresentKHR(queue, pPresentInfo, record_obj); |
| } |
| } |
| return result; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL BeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(commandBuffer); |
| bool skip = false; |
| chassis::HandleData handle_data; |
| |
| ErrorObject error_obj(vvl::Func::vkBeginCommandBuffer, VulkanTypedHandle(commandBuffer, kVulkanObjectTypeCommandBuffer), |
| &handle_data); |
| handle_data.command_buffer.is_secondary = device_dispatch->IsSecondary(commandBuffer); |
| { |
| VVL_ZoneScopedN("PreCallValidate_BeginCommandBuffer"); |
| for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateBeginCommandBuffer]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateBeginCommandBuffer(commandBuffer, pBeginInfo, error_obj); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| |
| RecordObject record_obj(vvl::Func::vkBeginCommandBuffer, &handle_data); |
| { |
| VVL_ZoneScopedN("PreCallRecord_BeginCommandBuffer"); |
| for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallRecordBeginCommandBuffer]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordBeginCommandBuffer(commandBuffer, pBeginInfo, record_obj); |
| } |
| } |
| |
| VkResult result; |
| { |
| VVL_ZoneScopedN("Dispatch_BeginCommandBuffer"); |
| result = device_dispatch->BeginCommandBuffer(commandBuffer, pBeginInfo); |
| } |
| record_obj.result = result; |
| |
| { |
| VVL_ZoneScopedN("PostCallRecord_BeginCommandBuffer"); |
| for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordBeginCommandBuffer]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordBeginCommandBuffer(commandBuffer, pBeginInfo, record_obj); |
| } |
| } |
| return result; |
| } |
| |
| // Handle tooling queries manually as this is a request for layer information |
| static const VkPhysicalDeviceToolPropertiesEXT khronos_layer_tool_props = { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TOOL_PROPERTIES_EXT, |
| nullptr, |
| "Khronos Validation Layer", |
| STRINGIFY(VK_HEADER_VERSION), |
| VK_TOOL_PURPOSE_VALIDATION_BIT | VK_TOOL_PURPOSE_ADDITIONAL_FEATURES_BIT | VK_TOOL_PURPOSE_DEBUG_REPORTING_BIT_EXT | |
| VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT, |
| "Khronos Validation Layer", |
| OBJECT_LAYER_NAME}; |
| |
| VKAPI_ATTR VkResult VKAPI_CALL GetPhysicalDeviceToolPropertiesEXT(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, |
| VkPhysicalDeviceToolPropertiesEXT* pToolProperties) { |
| auto instance_dispatch = vvl::GetDispatchInstance(physicalDevice); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkGetPhysicalDeviceToolPropertiesEXT, |
| VulkanTypedHandle(physicalDevice, kVulkanObjectTypePhysicalDevice)); |
| |
| auto original_pToolProperties = pToolProperties; |
| |
| if (pToolProperties != nullptr && *pToolCount > 0) { |
| *pToolProperties = khronos_layer_tool_props; |
| pToolProperties = ((*pToolCount > 1) ? &pToolProperties[1] : nullptr); |
| (*pToolCount)--; |
| } |
| |
| for (const auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| skip |= vo->PreCallValidateGetPhysicalDeviceToolPropertiesEXT(physicalDevice, pToolCount, pToolProperties, error_obj); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| |
| RecordObject record_obj(vvl::Func::vkGetPhysicalDeviceToolPropertiesEXT); |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PreCallRecordGetPhysicalDeviceToolPropertiesEXT(physicalDevice, pToolCount, pToolProperties, record_obj); |
| } |
| |
| VkResult result = instance_dispatch->GetPhysicalDeviceToolPropertiesEXT(physicalDevice, pToolCount, pToolProperties); |
| record_obj.result = result; |
| |
| if (original_pToolProperties != nullptr) { |
| pToolProperties = original_pToolProperties; |
| } |
| assert(*pToolCount != vvl::kU32Max); |
| (*pToolCount)++; |
| |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PostCallRecordGetPhysicalDeviceToolPropertiesEXT(physicalDevice, pToolCount, pToolProperties, record_obj); |
| } |
| return result; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL GetPhysicalDeviceToolProperties(VkPhysicalDevice physicalDevice, uint32_t* pToolCount, |
| VkPhysicalDeviceToolProperties* pToolProperties) { |
| auto instance_dispatch = vvl::GetDispatchInstance(physicalDevice); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkGetPhysicalDeviceToolProperties, |
| VulkanTypedHandle(physicalDevice, kVulkanObjectTypePhysicalDevice)); |
| |
| auto original_pToolProperties = pToolProperties; |
| |
| if (pToolProperties != nullptr && *pToolCount > 0) { |
| *pToolProperties = khronos_layer_tool_props; |
| pToolProperties = ((*pToolCount > 1) ? &pToolProperties[1] : nullptr); |
| (*pToolCount)--; |
| } |
| |
| for (const auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| skip |= vo->PreCallValidateGetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties, error_obj); |
| if (skip) return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| |
| RecordObject record_obj(vvl::Func::vkGetPhysicalDeviceToolProperties); |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PreCallRecordGetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties, record_obj); |
| } |
| |
| VkResult result = instance_dispatch->GetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties); |
| record_obj.result = result; |
| |
| if (original_pToolProperties != nullptr) { |
| pToolProperties = original_pToolProperties; |
| } |
| assert(*pToolCount != vvl::kU32Max); |
| (*pToolCount)++; |
| |
| for (auto& vo : instance_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| vo->PostCallRecordGetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties, record_obj); |
| } |
| return result; |
| } |
| |
| VKAPI_ATTR void VKAPI_CALL CmdBindDescriptorBuffersEXT(VkCommandBuffer commandBuffer, uint32_t bufferCount, |
| const VkDescriptorBufferBindingInfoEXT* pBindingInfos) { |
| VVL_ZoneScoped; |
| |
| auto device_dispatch = vvl::GetDispatchDevice(commandBuffer); |
| bool skip = false; |
| ErrorObject error_obj(vvl::Func::vkCmdBindDescriptorBuffersEXT, |
| VulkanTypedHandle(commandBuffer, kVulkanObjectTypeCommandBuffer)); |
| { |
| VVL_ZoneScopedN("PreCallValidate_vkCmdBindDescriptorBuffersEXT"); |
| for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateCmdBindDescriptorBuffersEXT]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->ReadLock(); |
| skip |= vo->PreCallValidateCmdBindDescriptorBuffersEXT(commandBuffer, bufferCount, pBindingInfos, error_obj); |
| if (skip) { |
| return; |
| } |
| } |
| } |
| |
| chassis::CmdBindDescriptorBuffers chassis_state(bufferCount, pBindingInfos); |
| |
| RecordObject record_obj(vvl::Func::vkCmdBindDescriptorBuffersEXT); |
| { |
| VVL_ZoneScopedN("PreCallRecord_vkCmdBindDescriptorBuffersEXT"); |
| for (auto& vo : device_dispatch->object_dispatch) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PreCallRecordCmdBindDescriptorBuffersEXT(commandBuffer, bufferCount, pBindingInfos, record_obj, chassis_state); |
| } |
| } |
| |
| // Only done in GPU-AV |
| const uint32_t modified_count = (uint32_t)chassis_state.modified_binding_infos.size(); |
| { |
| VVL_ZoneScopedN("Dispatch_vkCmdBindDescriptorBuffersEXT"); |
| device_dispatch->CmdBindDescriptorBuffersEXT(commandBuffer, modified_count, chassis_state.pBindInfos); |
| } |
| { |
| VVL_ZoneScopedN("PostCallRecord_vkCmdBindDescriptorBuffersEXT"); |
| for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordCmdBindDescriptorBuffersEXT]) { |
| if (!vo) { |
| continue; |
| } |
| auto lock = vo->WriteLock(); |
| vo->PostCallRecordCmdBindDescriptorBuffersEXT(commandBuffer, modified_count, chassis_state.pBindInfos, record_obj); |
| } |
| } |
| } |
| |
| // ValidationCache APIs do not dispatch |
| |
| VKAPI_ATTR VkResult VKAPI_CALL CreateValidationCacheEXT(VkDevice device, const VkValidationCacheCreateInfoEXT* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkValidationCacheEXT* pValidationCache) { |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| if (auto core_checks = static_cast<CoreChecks*>(device_dispatch->GetValidationObject(LayerObjectTypeCoreValidation))) { |
| auto lock = core_checks->WriteLock(); |
| return core_checks->CoreLayerCreateValidationCacheEXT(device, pCreateInfo, pAllocator, pValidationCache); |
| } |
| return VK_SUCCESS; |
| } |
| |
| VKAPI_ATTR void VKAPI_CALL DestroyValidationCacheEXT(VkDevice device, VkValidationCacheEXT validationCache, |
| const VkAllocationCallbacks* pAllocator) { |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| if (auto core_checks = static_cast<CoreChecks*>(device_dispatch->GetValidationObject(LayerObjectTypeCoreValidation))) { |
| auto lock = core_checks->WriteLock(); |
| core_checks->CoreLayerDestroyValidationCacheEXT(device, validationCache, pAllocator); |
| } |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL MergeValidationCachesEXT(VkDevice device, VkValidationCacheEXT dstCache, uint32_t srcCacheCount, |
| const VkValidationCacheEXT* pSrcCaches) { |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| if (auto core_checks = static_cast<CoreChecks*>(device_dispatch->GetValidationObject(LayerObjectTypeCoreValidation))) { |
| auto lock = core_checks->WriteLock(); |
| return core_checks->CoreLayerMergeValidationCachesEXT(device, dstCache, srcCacheCount, pSrcCaches); |
| } |
| return VK_SUCCESS; |
| } |
| |
| VKAPI_ATTR VkResult VKAPI_CALL GetValidationCacheDataEXT(VkDevice device, VkValidationCacheEXT validationCache, size_t* pDataSize, |
| void* pData) { |
| auto device_dispatch = vvl::GetDispatchDevice(device); |
| if (auto core_checks = static_cast<CoreChecks*>(device_dispatch->GetValidationObject(LayerObjectTypeCoreValidation))) { |
| auto lock = core_checks->WriteLock(); |
| return core_checks->CoreLayerGetValidationCacheDataEXT(device, validationCache, pDataSize, pData); |
| } |
| return VK_SUCCESS; |
| } |
| } // namespace vulkan_layer_chassis |