blob: 2a82343985ae54e1e9044658197134d3a0c6b7d4 [file] [log] [blame]
/***************************************************************************
*
* Copyright (c) 2015-2024 The Khronos Group Inc.
* Copyright (c) 2015-2024 Valve Corporation
* Copyright (c) 2015-2024 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 <array>
#include <cstring>
#include <mutex>
#include "chassis/dispatch_object.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"
namespace vulkan_layer_chassis {
// Check enabled instance extensions against supported instance extension whitelist
static void InstanceExtensionWhitelist(vvl::dispatch::Instance* 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]);
if (!IsInstanceExtension(extension)) {
Location loc(vvl::Func::vkCreateInstance);
layer_data->LogWarning(kVUIDUndefined, layer_data->instance,
loc.dot(vvl::Field::pCreateInfo).dot(vvl::Field::ppEnabledExtensionNames, i),
"%s is not supported by this layer. Using this extension may adversely affect validation "
"results and/or produce undefined behavior.",
pCreateInfo->ppEnabledExtensionNames[i]);
}
}
}
// Check enabled device extensions against supported device extension whitelist
static void DeviceExtensionWhitelist(vvl::dispatch::Device* 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]);
if (!IsDeviceExtension(extension)) {
Location loc(vvl::Func::vkCreateDevice);
layer_data->LogWarning(kVUIDUndefined, layer_data->device,
loc.dot(vvl::Field::pCreateInfo).dot(vvl::Field::ppEnabledExtensionNames, i),
"%s is not supported by this layer. Using this extension may adversely affect validation "
"results and/or produce undefined behavior.",
pCreateInfo->ppEnabledExtensionNames[i]);
}
}
}
void OutputLayerStatusInfo(vvl::dispatch::Instance* 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::dispatch::GetData(device);
if (!ApiParentExtensionEnabled(funcName, &layer_data->device_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);
}
auto layer_data = vvl::dispatch::GetData(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::dispatch::GetData(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::dispatch::FreeAllData();
}
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::dispatch::Instance>(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) {
auto lock = vo->ReadLock();
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) {
auto lock = vo->WriteLock();
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) {
vo->CopyDispatchState();
}
layer_init_instance_dispatch_table(*pInstance, &instance_dispatch->instance_dispatch_table, fpGetInstanceProcAddr);
// 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.
instance_dispatch->device_extensions.InitFromDeviceCreateInfo(&instance_dispatch->instance_extensions,
instance_dispatch->api_version);
OutputLayerStatusInfo(instance_dispatch.get());
InstanceExtensionWhitelist(instance_dispatch.get(), pCreateInfo, *pInstance);
// save a raw pointer since the unique_ptr will be invalidate by the move() below
auto* id = instance_dispatch.get();
vvl::dispatch::SetData(*pInstance, std::move(instance_dispatch));
for (auto& vo : id->object_dispatch) {
auto lock = vo->WriteLock();
vo->PostCallRecordCreateInstance(pCreateInfo, pAllocator, pInstance, record_obj);
}
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::dispatch::GetData(instance);
ActivateInstanceDebugCallbacks(instance_dispatch->debug_report);
ErrorObject error_obj(vvl::Func::vkDestroyInstance, VulkanTypedHandle(instance, kVulkanObjectTypeInstance));
for (const auto& vo : instance_dispatch->object_dispatch) {
auto lock = vo->ReadLock();
vo->PreCallValidateDestroyInstance(instance, pAllocator, error_obj);
}
RecordObject record_obj(vvl::Func::vkDestroyInstance);
for (auto& vo : instance_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->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) {
auto lock = vo->WriteLock();
vo->PostCallRecordDestroyInstance(instance, pAllocator, record_obj);
}
DeactivateInstanceDebugCallbacks(instance_dispatch->debug_report);
vvl::dispatch::FreeData(key, instance);
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::dispatch::GetData(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::dispatch::Device>(instance_dispatch, gpu, pCreateInfo);
// 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& vo : instance_dispatch->object_dispatch) {
vo->device_extensions = device_dispatch->device_extensions;
}
// Make copy to modify as some ValidationObjects will want to add extensions/features on
vku::safe_VkDeviceCreateInfo modified_create_info(pCreateInfo);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkCreateDevice, VulkanTypedHandle(gpu, kVulkanObjectTypePhysicalDevice));
for (const auto& vo : instance_dispatch->object_dispatch) {
auto lock = vo->ReadLock();
skip |= vo->PreCallValidateCreateDevice(gpu, pCreateInfo, pAllocator, pDevice, error_obj);
if (skip) {
return VK_ERROR_VALIDATION_FAILED_EXT;
}
}
RecordObject record_obj(vvl::Func::vkCreateDevice);
for (auto& vo : instance_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
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) {
return result;
}
record_obj.result = result;
device_dispatch->device = *pDevice;
// Save local info in device object
device_dispatch->api_version = device_dispatch->device_extensions.InitFromDeviceCreateInfo(
&instance_dispatch->instance_extensions, device_dispatch->api_version,
reinterpret_cast<VkDeviceCreateInfo*>(&modified_create_info));
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) {
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.
vvl::dispatch::SetData(*pDevice, std::move(device_dispatch));
for (auto& vo : instance_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
// Send down modified create info as we want to mark enabled features that we sent down on behalf of the app
vo->PostCallRecordCreateDevice(gpu, reinterpret_cast<VkDeviceCreateInfo*>(&modified_create_info), pAllocator, pDevice,
record_obj);
}
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::dispatch::GetData(device);
ErrorObject error_obj(vvl::Func::vkDestroyDevice, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
for (const auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->ReadLock();
vo->PreCallValidateDestroyDevice(device, pAllocator, error_obj);
}
RecordObject record_obj(vvl::Func::vkDestroyDevice);
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PreCallRecordDestroyDevice(device, pAllocator, record_obj);
}
// Before device is destroyed, allow aborted objects to clean up
for (auto& vo : device_dispatch->aborted_object_dispatch) {
auto lock = vo->WriteLock();
vo->PreCallRecordDestroyDevice(device, pAllocator, record_obj);
}
device_dispatch->DestroyDevice(device, pAllocator);
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PostCallRecordDestroyDevice(device, pAllocator, record_obj);
}
auto instance_dispatch = vvl::dispatch::GetData(device_dispatch->physical_device);
instance_dispatch->debug_report->device_created--;
vvl::dispatch::FreeData(key, device);
}
// 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::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkCreateGraphicsPipelines, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
PipelineStates pipeline_states[LayerObjectTypeMaxEnum];
chassis::CreateGraphicsPipelines chassis_state(pCreateInfos);
{
VVL_ZoneScopedN("PreCallValidate");
for (const auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->ReadLock();
skip |= vo->PreCallValidateCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator,
pPipelines, error_obj, pipeline_states[vo->container_type],
chassis_state);
if (skip) return VK_ERROR_VALIDATION_FAILED_EXT;
}
}
RecordObject record_obj(vvl::Func::vkCreateGraphicsPipelines);
{
VVL_ZoneScopedN("PreCallRecord");
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PreCallRecordCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines,
record_obj, pipeline_states[vo->container_type], chassis_state);
}
}
VkResult result;
{
VVL_ZoneScopedN("Dispatch");
result = device_dispatch->CreateGraphicsPipelines(device, 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->PostCallRecordCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines,
record_obj, pipeline_states[vo->container_type], 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::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkCreateComputePipelines, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
PipelineStates pipeline_states[LayerObjectTypeMaxEnum];
chassis::CreateComputePipelines chassis_state(pCreateInfos);
{
VVL_ZoneScopedN("PreCallValidate");
for (const auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->ReadLock();
skip |= vo->PreCallValidateCreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator,
pPipelines, error_obj, pipeline_states[vo->container_type],
chassis_state);
if (skip) return VK_ERROR_VALIDATION_FAILED_EXT;
}
}
RecordObject record_obj(vvl::Func::vkCreateComputePipelines);
{
VVL_ZoneScopedN("PreCallRecord");
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PreCallRecordCreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines,
record_obj, pipeline_states[vo->container_type], chassis_state);
}
}
VkResult result;
{
VVL_ZoneScopedN("Dispatch");
result = device_dispatch->CreateComputePipelines(device, 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->PostCallRecordCreateComputePipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines,
record_obj, pipeline_states[vo->container_type], 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::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkCreateRayTracingPipelinesNV, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
PipelineStates pipeline_states[LayerObjectTypeMaxEnum];
chassis::CreateRayTracingPipelinesNV chassis_state(pCreateInfos);
for (const auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->ReadLock();
skip |= vo->PreCallValidateCreateRayTracingPipelinesNV(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator,
pPipelines, error_obj, pipeline_states[vo->container_type],
chassis_state);
if (skip) return VK_ERROR_VALIDATION_FAILED_EXT;
}
RecordObject record_obj(vvl::Func::vkCreateRayTracingPipelinesNV);
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PreCallRecordCreateRayTracingPipelinesNV(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines,
record_obj, pipeline_states[vo->container_type], chassis_state);
}
VkResult result = device_dispatch->CreateRayTracingPipelinesNV(device, pipelineCache, createInfoCount,
chassis_state.pCreateInfos, pAllocator, pPipelines);
record_obj.result = result;
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PostCallRecordCreateRayTracingPipelinesNV(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines,
record_obj, pipeline_states[vo->container_type], chassis_state);
}
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::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkCreateRayTracingPipelinesKHR, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
PipelineStates pipeline_states[LayerObjectTypeMaxEnum];
auto chassis_state = std::make_shared<chassis::CreateRayTracingPipelinesKHR>(pCreateInfos);
{
VVL_ZoneScopedN("PreCallValidate");
for (const auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->ReadLock();
skip |= vo->PreCallValidateCreateRayTracingPipelinesKHR(device, deferredOperation, pipelineCache, createInfoCount,
pCreateInfos, pAllocator, pPipelines, error_obj,
pipeline_states[vo->container_type], *chassis_state);
if (skip) return VK_ERROR_VALIDATION_FAILED_EXT;
}
}
RecordObject record_obj(vvl::Func::vkCreateRayTracingPipelinesKHR);
{
VVL_ZoneScopedN("PreCallRecord");
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PreCallRecordCreateRayTracingPipelinesKHR(device, deferredOperation, pipelineCache, createInfoCount, pCreateInfos,
pAllocator, pPipelines, record_obj, pipeline_states[vo->container_type],
*chassis_state);
}
}
VkResult result;
{
VVL_ZoneScopedN("Dispatch");
result = device_dispatch->CreateRayTracingPipelinesKHR(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->PostCallRecordCreateRayTracingPipelinesKHR(device, deferredOperation, pipelineCache, createInfoCount, pCreateInfos,
pAllocator, pPipelines, record_obj, pipeline_states[vo->container_type],
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::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkCreatePipelineLayout, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
{
VVL_ZoneScopedN("PreCallValidate");
for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateCreatePipelineLayout]) {
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");
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PreCallRecordCreatePipelineLayout(device, pCreateInfo, pAllocator, pPipelineLayout, record_obj, chassis_state);
}
}
VkResult result;
{
VVL_ZoneScopedN("Dispatch");
result = device_dispatch->CreatePipelineLayout(device, &chassis_state.modified_create_info, pAllocator, pPipelineLayout);
}
record_obj.result = result;
{
VVL_ZoneScopedN("PostCallRecord");
for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordCreatePipelineLayout]) {
auto lock = vo->WriteLock();
vo->PostCallRecordCreatePipelineLayout(device, pCreateInfo, pAllocator, pPipelineLayout, 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::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkCreateShaderModule, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
{
VVL_ZoneScopedN("PreCallValidate");
for (const auto& vo : device_dispatch->object_dispatch) {
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");
for (auto& vo : device_dispatch->object_dispatch) {
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");
result = device_dispatch->CreateShaderModule(device, pCreateInfo, pAllocator, pShaderModule);
}
record_obj.result = result;
{
VVL_ZoneScopedN("PostCallRecord");
for (auto& vo : device_dispatch->object_dispatch) {
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::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkCreateShadersEXT, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
chassis::ShaderObject chassis_state(createInfoCount, pCreateInfos);
{
VVL_ZoneScopedN("PreCallValidate");
for (const auto& vo : device_dispatch->object_dispatch) {
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");
for (auto& vo : device_dispatch->object_dispatch) {
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");
result = device_dispatch->CreateShadersEXT(device, createInfoCount, chassis_state.pCreateInfos, pAllocator, pShaders);
}
record_obj.result = result;
{
VVL_ZoneScopedN("PostCallRecord");
for (auto& vo : device_dispatch->object_dispatch) {
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::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkAllocateDescriptorSets, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
vvl::AllocateDescriptorSetsData ads_state[LayerObjectTypeMaxEnum];
{
VVL_ZoneScopedN("PreCallValidate");
for (const auto& vo : device_dispatch->object_dispatch) {
ads_state[vo->container_type].Init(pAllocateInfo->descriptorSetCount);
auto lock = vo->ReadLock();
skip |= vo->PreCallValidateAllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets, error_obj,
ads_state[vo->container_type]);
if (skip) return VK_ERROR_VALIDATION_FAILED_EXT;
}
}
RecordObject record_obj(vvl::Func::vkAllocateDescriptorSets);
{
VVL_ZoneScopedN("PreCallRecord");
for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallRecordAllocateDescriptorSets]) {
auto lock = vo->WriteLock();
vo->PreCallRecordAllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets, record_obj);
}
}
VkResult result;
{
VVL_ZoneScopedN("Dispatch");
result = device_dispatch->AllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets);
}
record_obj.result = result;
{
VVL_ZoneScopedN("PostCallRecord");
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PostCallRecordAllocateDescriptorSets(device, pAllocateInfo, pDescriptorSets, record_obj,
ads_state[vo->container_type]);
}
}
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::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkCreateBuffer, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
{
VVL_ZoneScopedN("PreCallValidate");
for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateCreateBuffer]) {
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.modified_create_info = *pCreateInfo;
RecordObject record_obj(vvl::Func::vkCreateBuffer);
{
VVL_ZoneScopedN("PreCallRecord");
for (auto& vo : device_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PreCallRecordCreateBuffer(device, pCreateInfo, pAllocator, pBuffer, record_obj, chassis_state);
}
}
VkResult result;
{
VVL_ZoneScopedN("Dispatch");
result = device_dispatch->CreateBuffer(device, &chassis_state.modified_create_info, pAllocator, pBuffer);
}
record_obj.result = result;
{
VVL_ZoneScopedN("PostCallRecord");
for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordCreateBuffer]) {
auto lock = vo->WriteLock();
vo->PostCallRecordCreateBuffer(device, pCreateInfo, 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::dispatch::GetData(queue);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkQueuePresentKHR, VulkanTypedHandle(queue, kVulkanObjectTypeQueue));
{
VVL_ZoneScopedN("PreCallValidate");
for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateQueuePresentKHR]) {
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");
for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallRecordQueuePresentKHR]) {
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");
result = device_dispatch->QueuePresentKHR(queue, pPresentInfo);
}
VVL_TracyCFrameMark;
record_obj.result = result;
{
VVL_ZoneScopedN("PostCallRecord");
for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordQueuePresentKHR]) {
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::dispatch::GetData(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");
for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateBeginCommandBuffer]) {
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");
for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallRecordBeginCommandBuffer]) {
auto lock = vo->WriteLock();
vo->PreCallRecordBeginCommandBuffer(commandBuffer, pBeginInfo, record_obj);
}
}
VkResult result;
{
VVL_ZoneScopedN("Dispatch");
result = device_dispatch->BeginCommandBuffer(commandBuffer, pBeginInfo);
}
record_obj.result = result;
{
VVL_ZoneScopedN("PostCallRecord");
for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordBeginCommandBuffer]) {
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::dispatch::GetData(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) {
auto lock = vo->ReadLock();
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) {
auto lock = vo->WriteLock();
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 != std::numeric_limits<uint32_t>::max());
(*pToolCount)++;
for (auto& vo : instance_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
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::dispatch::GetData(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) {
auto lock = vo->ReadLock();
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) {
auto lock = vo->WriteLock();
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 != std::numeric_limits<uint32_t>::max());
(*pToolCount)++;
for (auto& vo : instance_dispatch->object_dispatch) {
auto lock = vo->WriteLock();
vo->PostCallRecordGetPhysicalDeviceToolProperties(physicalDevice, pToolCount, pToolProperties, record_obj);
}
return result;
}
// 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::dispatch::GetData(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::dispatch::GetData(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::dispatch::GetData(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::dispatch::GetData(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