blob: 763921ce555fdc22d35105a9dff4a500688632a7 [file]
/* Copyright (c) 2018-2025 The Khronos Group Inc.
* Copyright (c) 2018-2025 Valve Corporation
* Copyright (c) 2018-2025 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cmath>
#include <cstring>
#include <string>
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__)
#include <unistd.h>
#endif
#include "gpu/core/gpuav_constants.h"
#include "gpu/core/gpuav.h"
#include "gpu/resources/gpuav_state_trackers.h"
#include "chassis/dispatch_object.h"
#include "gpu/shaders/gpuav_error_header.h"
#include "gpu/shaders/gpuav_shaders_constants.h"
namespace gpuav {
std::shared_ptr<vvl::Buffer> Validator::CreateBufferState(VkBuffer handle, const VkBufferCreateInfo *create_info) {
return std::make_shared<Buffer>(*this, handle, create_info, *desc_heap_);
}
std::shared_ptr<vvl::BufferView> Validator::CreateBufferViewState(const std::shared_ptr<vvl::Buffer> &buffer, VkBufferView handle,
const VkBufferViewCreateInfo *create_info,
VkFormatFeatureFlags2 format_features) {
return std::make_shared<BufferView>(buffer, handle, create_info, format_features, *desc_heap_);
}
std::shared_ptr<vvl::ImageView> Validator::CreateImageViewState(const std::shared_ptr<vvl::Image> &image_state, VkImageView handle,
const VkImageViewCreateInfo *create_info,
VkFormatFeatureFlags2 format_features,
const VkFilterCubicImageViewImageFormatPropertiesEXT &cubic_props) {
return std::make_shared<ImageView>(image_state, handle, create_info, format_features, cubic_props, *desc_heap_);
}
std::shared_ptr<vvl::Sampler> Validator::CreateSamplerState(VkSampler handle, const VkSamplerCreateInfo *create_info) {
return std::make_shared<Sampler>(handle, create_info, *desc_heap_);
}
std::shared_ptr<vvl::AccelerationStructureKHR> Validator::CreateAccelerationStructureState(
VkAccelerationStructureKHR handle, const VkAccelerationStructureCreateInfoKHR *create_info,
std::shared_ptr<vvl::Buffer> &&buf_state) {
return std::make_shared<AccelerationStructureKHR>(handle, create_info, std::move(buf_state), *desc_heap_);
}
std::shared_ptr<vvl::DescriptorSet> Validator::CreateDescriptorSet(VkDescriptorSet handle, vvl::DescriptorPool *pool,
const std::shared_ptr<vvl::DescriptorSetLayout const> &layout,
uint32_t variable_count) {
return std::static_pointer_cast<vvl::DescriptorSet>(
std::make_shared<DescriptorSet>(handle, pool, layout, variable_count, this));
}
std::shared_ptr<vvl::CommandBuffer> Validator::CreateCmdBufferState(VkCommandBuffer handle,
const VkCommandBufferAllocateInfo *allocate_info,
const vvl::CommandPool *pool) {
return std::static_pointer_cast<vvl::CommandBuffer>(std::make_shared<CommandBuffer>(*this, handle, allocate_info, pool));
}
std::shared_ptr<vvl::Queue> Validator::CreateQueue(VkQueue handle, uint32_t family_index, uint32_t queue_index,
VkDeviceQueueCreateFlags flags,
const VkQueueFamilyProperties &queueFamilyProperties) {
return std::static_pointer_cast<vvl::Queue>(
std::make_shared<Queue>(*this, handle, family_index, queue_index, flags, queueFamilyProperties, timeline_khr_));
}
static std::vector<VkExtensionProperties> GetExtensions(VkPhysicalDevice physical_device) {
VkResult err;
uint32_t extension_count = 512;
std::vector<VkExtensionProperties> extensions(extension_count);
for (;;) {
err = DispatchEnumerateDeviceExtensionProperties(physical_device, nullptr, &extension_count, extensions.data());
if (err == VK_SUCCESS) {
extensions.resize(extension_count);
return extensions;
} else if (err == VK_INCOMPLETE) {
extension_count *= 2; // wasn't enough space, increase it
extensions.resize(extension_count);
} else {
return {};
}
}
}
static bool IsExtensionAvailable(const char *extension_name, const std::vector<VkExtensionProperties> &available_extensions) {
for (const VkExtensionProperties &ext : available_extensions) {
if (strncmp(extension_name, ext.extensionName, VK_MAX_EXTENSION_NAME_SIZE) == 0) {
return true;
}
}
return false;
}
// Trampolines to make VMA call Dispatch for Vulkan calls
static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL gpuVkGetInstanceProcAddr(VkInstance inst, const char *name) {
return DispatchGetInstanceProcAddr(inst, name);
}
static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL gpuVkGetDeviceProcAddr(VkDevice dev, const char *name) {
return DispatchGetDeviceProcAddr(dev, name);
}
static VKAPI_ATTR void VKAPI_CALL gpuVkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceProperties *pProperties) {
DispatchGetPhysicalDeviceProperties(physicalDevice, pProperties);
}
static VKAPI_ATTR void VKAPI_CALL gpuVkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceMemoryProperties *pMemoryProperties) {
DispatchGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
}
static VKAPI_ATTR VkResult VKAPI_CALL gpuVkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo,
const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMemory) {
return DispatchAllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
}
static VKAPI_ATTR void VKAPI_CALL gpuVkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks *pAllocator) {
DispatchFreeMemory(device, memory, pAllocator);
}
static VKAPI_ATTR VkResult VKAPI_CALL gpuVkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size,
VkMemoryMapFlags flags, void **ppData) {
return DispatchMapMemory(device, memory, offset, size, flags, ppData);
}
static VKAPI_ATTR void VKAPI_CALL gpuVkUnmapMemory(VkDevice device, VkDeviceMemory memory) { DispatchUnmapMemory(device, memory); }
static VKAPI_ATTR VkResult VKAPI_CALL gpuVkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount,
const VkMappedMemoryRange *pMemoryRanges) {
return DispatchFlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
}
static VKAPI_ATTR VkResult VKAPI_CALL gpuVkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount,
const VkMappedMemoryRange *pMemoryRanges) {
return DispatchInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
}
static VKAPI_ATTR VkResult VKAPI_CALL gpuVkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory,
VkDeviceSize memoryOffset) {
return DispatchBindBufferMemory(device, buffer, memory, memoryOffset);
}
static VKAPI_ATTR VkResult VKAPI_CALL gpuVkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory,
VkDeviceSize memoryOffset) {
return DispatchBindImageMemory(device, image, memory, memoryOffset);
}
static VKAPI_ATTR void VKAPI_CALL gpuVkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer,
VkMemoryRequirements *pMemoryRequirements) {
DispatchGetBufferMemoryRequirements(device, buffer, pMemoryRequirements);
}
static VKAPI_ATTR void VKAPI_CALL gpuVkGetImageMemoryRequirements(VkDevice device, VkImage image,
VkMemoryRequirements *pMemoryRequirements) {
DispatchGetImageMemoryRequirements(device, image, pMemoryRequirements);
}
static VKAPI_ATTR VkResult VKAPI_CALL gpuVkCreateBuffer(VkDevice device, const VkBufferCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer) {
return DispatchCreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
}
static VKAPI_ATTR void VKAPI_CALL gpuVkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks *pAllocator) {
return DispatchDestroyBuffer(device, buffer, pAllocator);
}
static VKAPI_ATTR VkResult VKAPI_CALL gpuVkCreateImage(VkDevice device, const VkImageCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkImage *pImage) {
return DispatchCreateImage(device, pCreateInfo, pAllocator, pImage);
}
static VKAPI_ATTR void VKAPI_CALL gpuVkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator) {
DispatchDestroyImage(device, image, pAllocator);
}
static VKAPI_ATTR void VKAPI_CALL gpuVkCmdCopyBuffer(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer,
uint32_t regionCount, const VkBufferCopy *pRegions) {
DispatchCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, regionCount, pRegions);
}
static VkResult UtilInitializeVma(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device,
bool use_buffer_device_address, VmaAllocator *pAllocator) {
VmaVulkanFunctions functions;
VmaAllocatorCreateInfo allocator_info = {};
allocator_info.instance = instance;
allocator_info.device = device;
allocator_info.physicalDevice = physical_device;
if (use_buffer_device_address) {
allocator_info.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
}
functions.vkGetInstanceProcAddr = static_cast<PFN_vkGetInstanceProcAddr>(gpuVkGetInstanceProcAddr);
functions.vkGetDeviceProcAddr = static_cast<PFN_vkGetDeviceProcAddr>(gpuVkGetDeviceProcAddr);
functions.vkGetPhysicalDeviceProperties = static_cast<PFN_vkGetPhysicalDeviceProperties>(gpuVkGetPhysicalDeviceProperties);
functions.vkGetPhysicalDeviceMemoryProperties =
static_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(gpuVkGetPhysicalDeviceMemoryProperties);
functions.vkAllocateMemory = static_cast<PFN_vkAllocateMemory>(gpuVkAllocateMemory);
functions.vkFreeMemory = static_cast<PFN_vkFreeMemory>(gpuVkFreeMemory);
functions.vkMapMemory = static_cast<PFN_vkMapMemory>(gpuVkMapMemory);
functions.vkUnmapMemory = static_cast<PFN_vkUnmapMemory>(gpuVkUnmapMemory);
functions.vkFlushMappedMemoryRanges = static_cast<PFN_vkFlushMappedMemoryRanges>(gpuVkFlushMappedMemoryRanges);
functions.vkInvalidateMappedMemoryRanges = static_cast<PFN_vkInvalidateMappedMemoryRanges>(gpuVkInvalidateMappedMemoryRanges);
functions.vkBindBufferMemory = static_cast<PFN_vkBindBufferMemory>(gpuVkBindBufferMemory);
functions.vkBindImageMemory = static_cast<PFN_vkBindImageMemory>(gpuVkBindImageMemory);
functions.vkGetBufferMemoryRequirements = static_cast<PFN_vkGetBufferMemoryRequirements>(gpuVkGetBufferMemoryRequirements);
functions.vkGetImageMemoryRequirements = static_cast<PFN_vkGetImageMemoryRequirements>(gpuVkGetImageMemoryRequirements);
functions.vkCreateBuffer = static_cast<PFN_vkCreateBuffer>(gpuVkCreateBuffer);
functions.vkDestroyBuffer = static_cast<PFN_vkDestroyBuffer>(gpuVkDestroyBuffer);
functions.vkCreateImage = static_cast<PFN_vkCreateImage>(gpuVkCreateImage);
functions.vkDestroyImage = static_cast<PFN_vkDestroyImage>(gpuVkDestroyImage);
functions.vkCmdCopyBuffer = static_cast<PFN_vkCmdCopyBuffer>(gpuVkCmdCopyBuffer);
allocator_info.pVulkanFunctions = &functions;
return vmaCreateAllocator(&allocator_info, pAllocator);
}
void Validator::PreCallRecordCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkDevice *pDevice,
const RecordObject &record_obj, vku::safe_VkDeviceCreateInfo *modified_create_info) {
BaseClass::PreCallRecordCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice, record_obj, modified_create_info);
// GPU-AV requirements not met, exit early or future Vulkan calls may be invalid
if (api_version < VK_API_VERSION_1_1) {
return;
}
// In PreCallRecord this is all about trying to turn on as many feature/extension as possible on behalf of the app
// If the features are not supported, can't internal error until post device creation
VkPhysicalDeviceFeatures supported_features{};
DispatchGetPhysicalDeviceFeatures(physicalDevice, &supported_features);
VkPhysicalDeviceFeatures *enabled_features = const_cast<VkPhysicalDeviceFeatures *>(modified_create_info->pEnabledFeatures);
if (!enabled_features) {
// Look in a potential VkPhysicalDeviceFeatures2 feature struct
if (auto *enabled_features_2 = const_cast<VkPhysicalDeviceFeatures2 *>(
vku::FindStructInPNextChain<VkPhysicalDeviceFeatures2>(modified_create_info->pNext))) {
enabled_features = &enabled_features_2->features;
}
}
if (enabled_features) {
if (supported_features.fragmentStoresAndAtomics && !enabled_features->fragmentStoresAndAtomics) {
InternalWarning(device, record_obj.location, "Forcing fragmentStoresAndAtomics to VK_TRUE");
enabled_features->fragmentStoresAndAtomics = VK_TRUE;
}
if (supported_features.vertexPipelineStoresAndAtomics && !enabled_features->vertexPipelineStoresAndAtomics) {
InternalWarning(device, record_obj.location, "Forcing vertexPipelineStoresAndAtomics to VK_TRUE");
enabled_features->vertexPipelineStoresAndAtomics = VK_TRUE;
}
if (supported_features.shaderInt64 && !enabled_features->shaderInt64) {
InternalWarning(device, record_obj.location, "Forcing shaderInt64 to VK_TRUE");
enabled_features->shaderInt64 = VK_TRUE;
}
}
std::vector<VkExtensionProperties> available_extensions = GetExtensions(physicalDevice);
auto add_missing_features = [this, &record_obj, modified_create_info]() {
// Add timeline semaphore feature - This is required as we use it to manage when command buffers are submitted at queue
// submit time
if (auto *ts_features = const_cast<VkPhysicalDeviceTimelineSemaphoreFeatures *>(
vku::FindStructInPNextChain<VkPhysicalDeviceTimelineSemaphoreFeatures>(modified_create_info))) {
if (ts_features->timelineSemaphore == VK_FALSE) {
InternalWarning(device, record_obj.location,
"Forcing VkPhysicalDeviceTimelineSemaphoreFeatures::timelineSemaphore to VK_TRUE");
ts_features->timelineSemaphore = VK_TRUE;
}
} else {
InternalWarning(device, record_obj.location,
"Adding a VkPhysicalDeviceTimelineSemaphoreFeatures to pNext with timelineSemaphore set to VK_TRUE");
VkPhysicalDeviceTimelineSemaphoreFeatures new_ts_features = vku::InitStructHelper();
new_ts_features.timelineSemaphore = VK_TRUE;
vku::AddToPnext(*modified_create_info, new_ts_features);
}
// need vulkanMemoryModelDeviceScope feature to let us call atomicAdd to the output buffer
if (auto *mm_features = const_cast<VkPhysicalDeviceVulkanMemoryModelFeatures *>(
vku::FindStructInPNextChain<VkPhysicalDeviceVulkanMemoryModelFeatures>(modified_create_info))) {
if (mm_features->vulkanMemoryModel == VK_FALSE) {
InternalWarning(device, record_obj.location,
"Forcing VkPhysicalDeviceVulkanMemoryModelFeatures::vulkanMemoryModel to VK_TRUE");
mm_features->vulkanMemoryModel = VK_TRUE;
}
if (mm_features->vulkanMemoryModelDeviceScope == VK_FALSE) {
InternalWarning(device, record_obj.location,
"Forcing VkPhysicalDeviceVulkanMemoryModelFeatures::vulkanMemoryModelDeviceScope to VK_TRUE");
mm_features->vulkanMemoryModelDeviceScope = VK_TRUE;
}
} else {
InternalWarning(device, record_obj.location,
"Adding a VkPhysicalDeviceVulkanMemoryModelFeatures to pNext with vulkanMemoryModel and "
"vulkanMemoryModelDeviceScope set to VK_TRUE");
VkPhysicalDeviceVulkanMemoryModelFeatures new_mm_features = vku::InitStructHelper();
new_mm_features.vulkanMemoryModel = VK_TRUE;
new_mm_features.vulkanMemoryModelDeviceScope = VK_TRUE;
vku::AddToPnext(*modified_create_info, new_mm_features);
}
};
if (api_version > VK_API_VERSION_1_1) {
if (auto *features12 = const_cast<VkPhysicalDeviceVulkan12Features *>(
vku::FindStructInPNextChain<VkPhysicalDeviceVulkan12Features>(modified_create_info->pNext))) {
if (features12->timelineSemaphore == VK_FALSE) {
InternalWarning(device, record_obj.location,
"Forcing VkPhysicalDeviceVulkan12Features::timelineSemaphore to VK_TRUE");
features12->timelineSemaphore = VK_TRUE;
}
if (features12->vulkanMemoryModel == VK_FALSE) {
InternalWarning(device, record_obj.location,
"Forcing VkPhysicalDeviceVulkan12Features::vulkanMemoryModel to VK_TRUE");
features12->vulkanMemoryModel = VK_TRUE;
}
if (features12->vulkanMemoryModelDeviceScope == VK_FALSE) {
InternalWarning(device, record_obj.location,
"Forcing VkPhysicalDeviceVulkan12Features::vulkanMemoryModelDeviceScope to VK_TRUE");
features12->vulkanMemoryModelDeviceScope = VK_TRUE;
}
} else {
add_missing_features();
}
} else if (api_version == VK_API_VERSION_1_1) {
// Add our new extensions (will only add if found)
const std::string_view ts_ext{"VK_KHR_timeline_semaphore"};
vku::AddExtension(*modified_create_info, ts_ext.data());
const std::string_view mm_ext{"VK_KHR_vulkan_memory_model"};
vku::AddExtension(*modified_create_info, mm_ext.data());
add_missing_features();
timeline_khr_ = true;
}
// Force bufferDeviceAddress feature if available
// ---
auto add_bda_feature = [this, &record_obj, modified_create_info]() {
// Add buffer device address feature
if (auto *bda_features = const_cast<VkPhysicalDeviceBufferDeviceAddressFeatures *>(
vku::FindStructInPNextChain<VkPhysicalDeviceBufferDeviceAddressFeatures>(modified_create_info))) {
if (!bda_features->bufferDeviceAddress) {
InternalWarning(device, record_obj.location,
"Forcing VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress to VK_TRUE");
bda_features->bufferDeviceAddress = VK_TRUE;
}
} else {
InternalWarning(
device, record_obj.location,
"Adding a VkPhysicalDeviceBufferDeviceAddressFeatures to pNext with bufferDeviceAddress set to VK_TRUE");
VkPhysicalDeviceBufferDeviceAddressFeatures new_bda_features = vku::InitStructHelper();
new_bda_features.bufferDeviceAddress = VK_TRUE;
vku::AddToPnext(*modified_create_info, new_bda_features);
}
};
if (api_version >= VK_API_VERSION_1_2) {
if (auto *features12 = const_cast<VkPhysicalDeviceVulkan12Features *>(
vku::FindStructInPNextChain<VkPhysicalDeviceVulkan12Features>(modified_create_info->pNext))) {
if (!features12->bufferDeviceAddress) {
InternalWarning(device, record_obj.location,
"Forcing VkPhysicalDeviceVulkan12Features::bufferDeviceAddress to VK_TRUE");
features12->bufferDeviceAddress = VK_TRUE;
}
} else {
add_bda_feature();
}
} else if (IsExtensionAvailable(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, available_extensions)) {
// Add our new extensions, only add if not found
vku::AddExtension(*modified_create_info, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
add_bda_feature();
}
// Force uniformAndStorageBuffer8BitAccess feature if available and needed
// ---
if (gpuav_settings.validate_buffer_copies) {
VkPhysicalDevice8BitStorageFeatures eight_bit_feature = vku::InitStructHelper();
VkPhysicalDeviceFeatures2 features_2 = vku::InitStructHelper(&eight_bit_feature);
DispatchGetPhysicalDeviceFeatures2(physicalDevice, &features_2);
// uniformAndStorageBuffer8BitAccess is optional in 1.2. Only force on if available.
if (eight_bit_feature.uniformAndStorageBuffer8BitAccess) {
auto add_8bit_access_feature = [this, &record_obj, modified_create_info]() {
// Add uniformAndStorageBuffer8BitAccess feature
if (auto *eight_bit_access_feature = const_cast<VkPhysicalDevice8BitStorageFeatures *>(
vku::FindStructInPNextChain<VkPhysicalDevice8BitStorageFeatures>(modified_create_info))) {
if (!eight_bit_access_feature->uniformAndStorageBuffer8BitAccess) {
InternalWarning(
device, record_obj.location,
"Forcing VkPhysicalDevice8BitStorageFeatures::uniformAndStorageBuffer8BitAccess to VK_TRUE");
eight_bit_access_feature->uniformAndStorageBuffer8BitAccess = VK_TRUE;
}
} else {
InternalWarning(device, record_obj.location,
"Adding a VkPhysicalDevice8BitStorageFeatures to pNext with uniformAndStorageBuffer8BitAccess "
"set to VK_TRUE");
VkPhysicalDevice8BitStorageFeatures new_bda_features = vku::InitStructHelper();
new_bda_features.uniformAndStorageBuffer8BitAccess = VK_TRUE;
vku::AddToPnext(*modified_create_info, new_bda_features);
}
};
if (api_version >= VK_API_VERSION_1_2) {
if (auto *features12 = const_cast<VkPhysicalDeviceVulkan12Features *>(
vku::FindStructInPNextChain<VkPhysicalDeviceVulkan12Features>(modified_create_info->pNext))) {
if (!features12->uniformAndStorageBuffer8BitAccess) {
InternalWarning(device, record_obj.location,
"Forcing VkPhysicalDeviceVulkan12Features::uniformAndStorageBuffer8BitAccess to VK_TRUE");
features12->uniformAndStorageBuffer8BitAccess = VK_TRUE;
}
} else {
add_8bit_access_feature();
}
} else if (IsExtensionAvailable(VK_KHR_8BIT_STORAGE_EXTENSION_NAME, available_extensions)) {
// Add our new extensions, only if not found
vku::AddExtension(*modified_create_info, VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
add_8bit_access_feature();
}
}
}
// shaderInt64
// ---
VkPhysicalDeviceFeatures physical_device_features{};
DispatchGetPhysicalDeviceFeatures(physicalDevice, &physical_device_features);
if (physical_device_features.shaderInt64) {
if (enabled_features) {
enabled_features->shaderInt64 = VK_TRUE;
} else if (auto *features = const_cast<VkPhysicalDeviceFeatures2 *>(
vku::FindStructInPNextChain<VkPhysicalDeviceFeatures2>(modified_create_info->pNext))) {
features->features.shaderInt64 = VK_TRUE;
} else {
// Need to add a VkPhysicalDeviceFeatures pointer
enabled_features = new VkPhysicalDeviceFeatures;
memset(enabled_features, 0, sizeof(VkPhysicalDeviceFeatures));
enabled_features->shaderInt64 = VK_TRUE;
modified_create_info->pEnabledFeatures = enabled_features;
}
}
}
// Perform initializations that can be done at Create Device time.
void Validator::PostCreateDevice(const VkDeviceCreateInfo *pCreateInfo, const Location &loc) {
// GPU-AV not supported, exit early to prevent errors inside Validator::PostCallRecordCreateDevice
if (api_version < VK_API_VERSION_1_1) {
InternalError(device, loc, "GPU Shader Instrumentation requires Vulkan 1.1 or later.");
return;
}
// Set up a stub implementation of the descriptor heap in case we abort.
desc_heap_.emplace(*this, 0, loc);
instrumentation_bindings_ = {
// DebugPrintf Output buffer
{glsl::kBindingInstDebugPrintf, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
// Error output buffer
{glsl::kBindingInstErrorBuffer, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
// Buffer holding output from GPU to do processing on the CPU
{glsl::kBindingInstPostProcess, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
// Buffer holding input from CPU into the shader for descriptor indexing
{glsl::kBindingInstDescriptorIndexingOOB, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
// Buffer holding buffer device addresses
{glsl::kBindingInstBufferDeviceAddress, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
// Buffer holding action command index in command buffer
{glsl::kBindingInstActionIndex, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, nullptr},
// Buffer holding a resource index from the per command buffer command resources list
{glsl::kBindingInstCmdResourceIndex, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL, nullptr},
// Commands errors counts buffer
{glsl::kBindingInstCmdErrorsCount, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
};
// TODO - Now that GPU-AV and DebugPrintf are merged, we should just have a single PostCreateDevice if possible (or at least
// better divide what belongs where as it is easy to mess)
BaseClass::PostCreateDevice(pCreateInfo, loc);
// We might fail in parent class device creation if global requirements are not met
if (aborted_) return;
// Need the device to be created before we can query features for settings
InitSettings(loc);
VkResult result = UtilInitializeVma(instance, physical_device, device, enabled_features.bufferDeviceAddress, &vma_allocator_);
if (result != VK_SUCCESS) {
InternalVmaError(device, loc, "Could not initialize VMA");
return;
}
desc_set_manager_ =
std::make_unique<vko::DescriptorSetManager>(device, static_cast<uint32_t>(instrumentation_bindings_.size()));
// If api version 1.1 or later, SetDeviceLoaderData will be in the loader
{
auto chain_info = GetChainInfo(pCreateInfo, VK_LOADER_DATA_CALLBACK);
assert(chain_info->u.pfnSetDeviceLoaderData);
vk_set_device_loader_data_ = chain_info->u.pfnSetDeviceLoaderData;
}
if (gpuav_settings.shader_instrumentation.descriptor_checks) {
VkPhysicalDeviceDescriptorIndexingProperties desc_indexing_props = vku::InitStructHelper();
VkPhysicalDeviceProperties2 props2 = vku::InitStructHelper(&desc_indexing_props);
DispatchGetPhysicalDeviceProperties2Helper(physical_device, &props2);
uint32_t num_descs = desc_indexing_props.maxUpdateAfterBindDescriptorsInAllPools;
if (num_descs == 0 || num_descs > glsl::kDebugInputBindlessMaxDescriptors) {
num_descs = glsl::kDebugInputBindlessMaxDescriptors;
}
desc_heap_.emplace(*this, num_descs, loc);
}
VkBufferCreateInfo error_buffer_ci = vku::InitStructHelper();
error_buffer_ci.size = glsl::kErrorBufferByteSize;
error_buffer_ci.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
VmaAllocationCreateInfo alloc_create_info = {};
alloc_create_info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
uint32_t mem_type_index;
result = vmaFindMemoryTypeIndexForBufferInfo(vma_allocator_, &error_buffer_ci, &alloc_create_info, &mem_type_index);
if (result != VK_SUCCESS) {
InternalVmaError(device, loc, "Unable to find memory type index.");
return;
}
VmaPoolCreateInfo vma_pool_ci = {};
vma_pool_ci.memoryTypeIndex = mem_type_index;
vma_pool_ci.blockSize = 0;
vma_pool_ci.maxBlockCount = 0;
if (gpuav_settings.vma_linear_output) {
vma_pool_ci.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
}
result = vmaCreatePool(vma_allocator_, &vma_pool_ci, &output_buffer_pool_);
if (result != VK_SUCCESS) {
InternalVmaError(device, loc, "Unable to create VMA memory pool.");
return;
}
// Create command indices buffer
{
indices_buffer_alignment_ = sizeof(uint32_t) * static_cast<uint32_t>(phys_dev_props.limits.minStorageBufferOffsetAlignment);
VkBufferCreateInfo buffer_info = vku::InitStructHelper();
buffer_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
buffer_info.size = cst::indices_count * indices_buffer_alignment_;
VmaAllocationCreateInfo alloc_info = {};
assert(output_buffer_pool_);
alloc_info.pool = output_buffer_pool_;
alloc_info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
const bool success = indices_buffer_.Create(loc, &buffer_info, &alloc_info);
if (!success) {
return;
}
auto indices_ptr = (uint32_t *)indices_buffer_.MapMemory(loc);
for (uint32_t i = 0; i < buffer_info.size / sizeof(uint32_t); ++i) {
indices_ptr[i] = i / (indices_buffer_alignment_ / sizeof(uint32_t));
}
indices_buffer_.UnmapMemory();
}
}
// At this point extensions/features may have been turned on by us in PreCallRecord.
// Now that we have all the information, here is where we might disable GPU-AV settings that are missing requirements
void Validator::InitSettings(const Location &loc) {
VkPhysicalDeviceFeatures supported_features{};
DispatchGetPhysicalDeviceFeatures(physical_device, &supported_features);
GpuAVSettings::ShaderInstrumentation &shader_instrumentation = gpuav_settings.shader_instrumentation;
if (shader_instrumentation.descriptor_checks || shader_instrumentation.post_process_descriptor_index) {
if (!enabled_features.bufferDeviceAddress) {
shader_instrumentation.descriptor_checks = false;
shader_instrumentation.post_process_descriptor_index = false;
InternalWarning(device, loc,
"Descriptors Indexing Validation optin was enabled. but the bufferDeviceAddress was not supported "
"[Disabling gpuav_descriptor_checks and gpuav_post_process_descriptor_indexing]");
}
}
if (shader_instrumentation.buffer_device_address) {
const bool bda_validation_possible = ((IsExtEnabled(device_extensions.vk_ext_buffer_device_address) ||
IsExtEnabled(device_extensions.vk_khr_buffer_device_address)) &&
enabled_features.shaderInt64 && enabled_features.bufferDeviceAddress);
if (!bda_validation_possible) {
shader_instrumentation.buffer_device_address = false;
if (!enabled_features.shaderInt64) {
InternalWarning(
device, loc,
"Buffer device address validation option was enabled, but the shaderInt64 feature is not supported. "
"[Disabling gpuav_buffer_address_oob].");
} else {
InternalWarning(device, loc,
"Buffer device address validation option was enabled, but required buffer device address extension "
"and/or features are not enabled. [Disabling gpuav_buffer_address_oob]");
}
}
}
if (shader_instrumentation.ray_query) {
if (!enabled_features.rayQuery) {
// TODO - Force on if possible, issue is we need to potentially enable all the dependency extensions
shader_instrumentation.ray_query = false;
InternalWarning(device, loc,
"Ray Query validation option was enabled, but the rayQuery feature is not enabled. "
"[Disabling gpuav_validate_ray_query]");
}
}
// copy_buffer_to_image.comp relies on uint8_t buffers to perform validation
if (gpuav_settings.validate_buffer_copies) {
if (!enabled_features.uniformAndStorageBuffer8BitAccess) {
gpuav_settings.validate_buffer_copies = false;
InternalWarning(device, loc,
"Buffer copies option was enabled, but the uniformAndStorageBuffer8BitAccess feature is not supported. "
"[Disabling gpuav_buffer_copies]");
}
}
if (IsExtEnabled(device_extensions.vk_ext_descriptor_buffer)) {
InternalWarning(
device, loc,
"VK_EXT_descriptor_buffer is enabled, but GPU-AV does not currently support validation of descriptor buffers. "
"[Disabling all shader instrumentation checks]");
// Because of VUs like VUID-VkPipelineLayoutCreateInfo-pSetLayouts-08008 we currently would need to rework the entire shader
// instrumentation logic
gpuav_settings.DisableShaderInstrumentationAndOptions();
}
if (gpuav_settings.IsBufferValidationEnabled()) {
if (phys_dev_props.limits.maxPushConstantsSize < 9 * sizeof(uint32_t)) {
gpuav_settings.SetBufferValidationEnabled(false);
InternalWarning(device, loc,
"Device does not support required minimum maxPushConstantsSize. Buffer validation needs at least 9 * "
"sizeof(uint32_t) bytes. No indirect buffer checking will be "
"attempted");
} else if (!supported_features.shaderInt64) {
InternalWarning(
device, loc,
"shaderInt64 feature not available, indirect trace rays validation and countBuffer (as seen in commands like "
"vkCmdDrawIndexedIndirectCount) validation cannot be performed.");
}
}
// If we have turned off all the possible things to instrument, turn off everything fully
if (!gpuav_settings.IsShaderInstrumentationEnabled()) {
gpuav_settings.DisableShaderInstrumentationAndOptions();
}
}
void Validator::InternalVmaError(LogObjectList objlist, const Location &loc, const char *const specific_message) const {
aborted_ = true;
std::string error_message = specific_message;
char *stats_string;
vmaBuildStatsString(vma_allocator_, &stats_string, false);
error_message += " VMA statistics = ";
error_message += stats_string;
vmaFreeStatsString(vma_allocator_, stats_string);
char const *layer_name = gpuav_settings.debug_printf_only ? "DebugPrintf" : "GPU-AV";
char const *vuid = gpuav_settings.debug_printf_only ? "UNASSIGNED-DEBUG-PRINTF" : "UNASSIGNED-GPU-Assisted-Validation";
LogError(vuid, objlist, loc, "Internal VMA Error, %s is being disabled. Details:\n%s", layer_name, error_message.c_str());
// Once we encounter an internal issue disconnect everything.
// This prevents need to check "if (aborted)" (which is awful when we easily forget to check somewhere and the user gets spammed
// with errors making it hard to see the first error with the real source of the problem).
dispatch_device_->ReleaseValidationObject(LayerObjectTypeGpuAssisted);
}
VkDeviceAddress Validator::GetBufferDeviceAddressHelper(VkBuffer buffer) const {
VkBufferDeviceAddressInfo address_info = vku::InitStructHelper();
address_info.buffer = buffer;
if (api_version >= VK_API_VERSION_1_2) {
return DispatchGetBufferDeviceAddress(device, &address_info);
} else {
return DispatchGetBufferDeviceAddressKHR(device, &address_info);
}
}
} // namespace gpuav