| /* Copyright (c) 2018-2026 The Khronos Group Inc. |
| * Copyright (c) 2018-2026 Valve Corporation |
| * Copyright (c) 2018-2026 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 <vulkan/vulkan_core.h> |
| #include <array> |
| #include <string> |
| #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) |
| #include <unistd.h> |
| #endif |
| #include "chassis/dispatch_object.h" |
| #include "gpuav/core/gpuav.h" |
| #include "gpuav/core/gpuav_constants.h" |
| #include "gpuav/instrumentation/descriptor_checks.h" |
| #include "gpuav/resources/gpuav_state_trackers.h" |
| #include "gpuav/shaders/gpuav_error_header.h" |
| #include "gpuav/shaders/gpuav_shaders_constants.h" |
| #include "utils/dispatch_utils.h" |
| #include "utils/math_utils.h" |
| |
| namespace gpuav { |
| |
| // Location to add per-queue submit debug info if built with -D DEBUG_CAPTURE_KEYBOARD=ON |
| void Validator::DebugCapture() {} |
| |
| void Validator::Created(vvl::DescriptorSet& set) { |
| set.SetSubState(container_type, std::make_unique<DescriptorSetSubState>(set, *this)); |
| } |
| |
| void Validator::Created(vvl::CommandBuffer& cb_state) { |
| cb_state.SetSubState(container_type, std::make_unique<CommandBufferSubState>(*this, cb_state)); |
| } |
| |
| void Validator::Created(vvl::Queue& queue) { queue.SetSubState(container_type, std::make_unique<QueueSubState>(*this, queue)); } |
| |
| void Validator::Created(vvl::Image& obj) { |
| DescriptorHeap& desc_heap = shared_resources_cache.Get<DescriptorHeap>(); |
| obj.SetSubState(container_type, std::make_unique<ImageSubState>(obj, desc_heap)); |
| } |
| void Validator::Created(vvl::ImageView& obj) { |
| DescriptorHeap& desc_heap = shared_resources_cache.Get<DescriptorHeap>(); |
| obj.SetSubState(container_type, std::make_unique<ImageViewSubState>(obj, desc_heap)); |
| } |
| void Validator::Created(vvl::Buffer& obj) { |
| DescriptorHeap& desc_heap = shared_resources_cache.Get<DescriptorHeap>(); |
| obj.SetSubState(container_type, std::make_unique<BufferSubState>(obj, desc_heap)); |
| } |
| void Validator::Created(vvl::BufferView& obj) { |
| DescriptorHeap& desc_heap = shared_resources_cache.Get<DescriptorHeap>(); |
| obj.SetSubState(container_type, std::make_unique<BufferViewSubState>(obj, desc_heap)); |
| } |
| void Validator::Created(vvl::Sampler& obj) { |
| DescriptorHeap& desc_heap = shared_resources_cache.Get<DescriptorHeap>(); |
| obj.SetSubState(container_type, std::make_unique<SamplerSubState>(obj, desc_heap)); |
| } |
| void Validator::Created(vvl::AccelerationStructureNV& obj) { |
| DescriptorHeap& desc_heap = shared_resources_cache.Get<DescriptorHeap>(); |
| obj.SetSubState(container_type, std::make_unique<AccelerationStructureNVSubState>(obj, desc_heap)); |
| } |
| void Validator::Created(vvl::AccelerationStructureKHR& obj) { |
| DescriptorHeap& desc_heap = shared_resources_cache.Get<DescriptorHeap>(); |
| obj.SetSubState(container_type, std::make_unique<AccelerationStructureKHRSubState>(obj, desc_heap)); |
| } |
| void Validator::Created(vvl::Tensor& obj) { |
| DescriptorHeap& desc_heap = shared_resources_cache.Get<DescriptorHeap>(); |
| obj.SetSubState(container_type, std::make_unique<TensorSubState>(obj, desc_heap)); |
| } |
| void Validator::Created(vvl::TensorView& obj) { |
| DescriptorHeap& desc_heap = shared_resources_cache.Get<DescriptorHeap>(); |
| obj.SetSubState(container_type, std::make_unique<TensorViewSubState>(obj, desc_heap)); |
| } |
| void Validator::Created(vvl::ShaderObject& obj) { obj.SetSubState(container_type, std::make_unique<ShaderObjectSubState>(obj)); } |
| |
| void Validator::Created(vvl::Pipeline& obj) { obj.SetSubState(container_type, std::make_unique<PipelineSubState>(*this, obj)); } |
| |
| // 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, |
| VmaAllocator* pAllocator) { |
| VmaVulkanFunctions functions = {}; |
| VmaAllocatorCreateInfo allocator_info = {}; |
| allocator_info.instance = instance; |
| allocator_info.device = device; |
| allocator_info.physicalDevice = physical_device; |
| |
| 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 Instance::PreCallRecordCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, VkDevice* pDevice, const RecordObject& record_obj, |
| vku::safe_VkDeviceCreateInfo* modified_create_info) { |
| InstanceProxy::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; |
| } |
| |
| AddFeatures(physicalDevice, modified_create_info, record_obj.location); |
| } |
| |
| // Perform initializations that can be done at Create Device time. |
| void Validator::FinishDeviceSetup(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; |
| } |
| |
| 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 (a global buffer is used) |
| {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}, |
| // Vertex attribute fetch limits |
| {glsl::kBindingInstVertexAttributeFetchLimits, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr}, |
| // DebugDescriptor Output buffer |
| {glsl::kBindingInstDebugDescriptor, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, |
| }; |
| assert(instrumentation_bindings_.size() == glsl::kTotalBindings); |
| |
| // TODO - Now that GPU-AV and DebugPrintf are merged, we should just have a single FinishDeviceSetup if possible (or at least |
| // better divide what belongs where as it is easy to mess) |
| GpuShaderInstrumentor::FinishDeviceSetup(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, &vma_allocator_); |
| if (result != VK_SUCCESS) { |
| InternalVmaError(device, result, "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; |
| } |
| |
| DescriptorChecksOnFinishDeviceSetup(*this); |
| |
| // Create error logging buffer allocation pool |
| { |
| VkBufferCreateInfo error_buffer_ci = vku::InitStructHelper(); |
| error_buffer_ci.size = glsl::kErrorBufferByteSize; |
| error_buffer_ci.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; |
| VmaAllocationCreateInfo error_buffer_alloc_ci = {}; |
| error_buffer_alloc_ci.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| error_buffer_alloc_ci.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| uint32_t mem_type_index; |
| result = vmaFindMemoryTypeIndexForBufferInfo(vma_allocator_, &error_buffer_ci, &error_buffer_alloc_ci, &mem_type_index); |
| if (result != VK_SUCCESS) { |
| InternalVmaError(device, result, "Unable to find memory type index."); |
| return; |
| } |
| } |
| |
| // Create command indices buffer |
| { |
| const uint32_t index_size = sizeof(uint32_t); |
| indices_buffer_alignment_ = Align(index_size, (uint32_t)phys_dev_props.limits.minStorageBufferOffsetAlignment); |
| |
| VkBufferCreateInfo buffer_info = vku::InitStructHelper(); |
| buffer_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; |
| buffer_info.size = gpuav_settings.indices_buffer_count * indices_buffer_alignment_; |
| VmaAllocationCreateInfo alloc_info = {}; |
| alloc_info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| alloc_info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| result = global_indices_buffer_.Create(&buffer_info, &alloc_info); |
| if (result != VK_SUCCESS) { |
| return; |
| } |
| |
| uint32_t stride = indices_buffer_alignment_ / sizeof(uint32_t); |
| uint32_t* indices_ptr = (uint32_t*)global_indices_buffer_.GetMappedPtr(); |
| for (uint32_t i = 0; i < gpuav_settings.indices_buffer_count; ++i) { |
| const uint32_t offset = i * stride; |
| indices_ptr[offset] = i; |
| } |
| } |
| } |
| |
| vko::Buffer& Validator::GetGlobalDescriptorBuffer() { |
| if (global_resource_descriptor_buffer_.IsDestroyed()) { |
| VkBufferCreateInfo buffer_info = vku::InitStructHelper(); |
| buffer_info.size = phys_dev_ext_props.descriptor_buffer_props.storageBufferDescriptorSize * cst::total_internal_descriptors; |
| buffer_info.usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; |
| VmaAllocationCreateInfo alloc_info = {}; |
| alloc_info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| alloc_info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| const VkResult result = global_resource_descriptor_buffer_.Create(&buffer_info, &alloc_info); |
| if (result != VK_SUCCESS) { |
| InternalVmaError(device, result, "Failed to create an internal resource Descriptor Buffer."); |
| } |
| } |
| return global_resource_descriptor_buffer_; |
| } |
| |
| vko::Buffer& Validator::GetGlobalDescriptorHeap() { |
| if (global_resource_descriptor_heap_.IsDestroyed()) { |
| VkBufferCreateInfo buffer_info = vku::InitStructHelper(); |
| buffer_info.size = resource_heap_reserved_bytes_ + phys_dev_ext_props.descriptor_heap_props.minResourceHeapReservedRange; |
| buffer_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; |
| VmaAllocationCreateInfo alloc_info = {}; |
| alloc_info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| alloc_info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| const VkResult result = global_resource_descriptor_heap_.Create(&buffer_info, &alloc_info); |
| if (result != VK_SUCCESS) { |
| InternalVmaError(device, result, "Failed to create an internal resource Descriptor Heap."); |
| } |
| } |
| return global_resource_descriptor_heap_; |
| } |
| |
| namespace setting { |
| |
| // Each setting in GPU-AV has a common interface to make adding a new setting easier |
| struct Setting { |
| virtual bool IsEnabled(const GpuAVSettings& settings) = 0; |
| virtual bool HasRequiredFeatures(const DeviceFeatures& features) = 0; |
| virtual void Disable(GpuAVSettings& settings) = 0; |
| virtual std::string DisableMessage() = 0; |
| }; |
| |
| struct BufferDeviceAddress : public Setting { |
| bool IsEnabled(const GpuAVSettings& settings) { return settings.shader_instrumentation.buffer_device_address; } |
| bool HasRequiredFeatures(const DeviceFeatures& features) { return features.shaderInt64; } |
| void Disable(GpuAVSettings& settings) { settings.shader_instrumentation.buffer_device_address = false; } |
| std::string DisableMessage() { |
| return "\tBuffer Device Address validation option was enabled, but the shaderInt64 feature is not supported. [Disabling " |
| "gpuav_buffer_address_oob]\n"; |
| } |
| }; |
| |
| struct RayQuery : public Setting { |
| bool IsEnabled(const GpuAVSettings& settings) { return settings.shader_instrumentation.ray_query; } |
| bool HasRequiredFeatures(const DeviceFeatures& features) { return features.rayQuery; } |
| void Disable(GpuAVSettings& settings) { settings.shader_instrumentation.ray_query = false; } |
| std::string DisableMessage() { |
| return "\tRay Query validation option was enabled, but the rayQuery feature is not supported. [Disabling " |
| "gpuav_validate_ray_query]\n"; |
| } |
| }; |
| |
| struct TraceRay : public Setting { |
| bool IsEnabled(const GpuAVSettings& settings) { return settings.shader_instrumentation.trace_ray; } |
| bool HasRequiredFeatures(const DeviceFeatures& features) { return features.rayTracingPipeline; } |
| void Disable(GpuAVSettings& settings) { settings.shader_instrumentation.trace_ray = false; } |
| std::string DisableMessage() { |
| return "\tTrace Ray validation option was enabled, but the rayTracingPipeline feature is not supported. [Disabling " |
| "gpuav_validate_trace_ray]\n"; |
| } |
| }; |
| |
| struct MeshShading : public Setting { |
| bool IsEnabled(const GpuAVSettings& settings) { return settings.shader_instrumentation.mesh_shading; } |
| bool HasRequiredFeatures(const DeviceFeatures& features) { return features.meshShader; } |
| void Disable(GpuAVSettings& settings) { settings.shader_instrumentation.mesh_shading = false; } |
| std::string DisableMessage() { |
| return "\tMesh Shading validation option was enabled, but the meshShader feature is not supported. [Disabling " |
| "gpuav_mesh_shading]\n"; |
| } |
| }; |
| |
| struct BufferCopies : public Setting { |
| bool IsEnabled(const GpuAVSettings& settings) { return settings.validate_buffer_copies; } |
| // copy_buffer_to_image.comp relies on uint8_t buffers to perform validation |
| bool HasRequiredFeatures(const DeviceFeatures& features) { return features.storageBuffer8BitAccess; } |
| void Disable(GpuAVSettings& settings) { settings.validate_buffer_copies = false; } |
| std::string DisableMessage() { |
| return "\tBuffer copies option was enabled, but the storageBuffer8BitAccess feature is not supported. [Disabling " |
| "gpuav_buffer_copies]\n"; |
| } |
| }; |
| |
| struct BufferContent : public Setting { |
| bool IsEnabled(const GpuAVSettings& settings) { return settings.IsBufferValidationEnabled(); } |
| bool HasRequiredFeatures(const DeviceFeatures& features) { return features.shaderInt64; } |
| void Disable(GpuAVSettings& settings) { settings.SetBufferValidationEnabled(false); } |
| std::string DisableMessage() { |
| return "\tBuffer content validation option was enabled, but the shaderInt64 feature is not supported. [Disabling " |
| "gpuav_buffers_validation]\n"; |
| } |
| }; |
| |
| struct AccelerationStructuresBuild : public Setting { |
| bool IsEnabled(const GpuAVSettings& settings) { return settings.validate_acceleration_structures_builds; } |
| // Validation shader branches on a push constant value to fetch different descriptors |
| bool HasRequiredFeatures(const DeviceFeatures& features) { |
| return features.shaderInt64 && features.storageBuffer8BitAccess && features.storageBuffer16BitAccess; |
| } |
| void Disable(GpuAVSettings& settings) { settings.validate_acceleration_structures_builds = false; } |
| std::string DisableMessage() { |
| return "\tAcceleration structure builds validation option was enabled, but the shaderInt64 or storageBuffer8BitAccess or " |
| "storageBuffer16BitAccess features are not " |
| "supported. [Disabling " |
| "gpuav_acceleration_structures_builds]\n"; |
| } |
| }; |
| |
| struct RayTracingBuffersConsistency : public Setting { |
| bool IsEnabled(const GpuAVSettings& settings) { return settings.ray_tracing_buffers_consistency; } |
| // Validation shader branches on a push constant value to fetch different descriptors |
| bool HasRequiredFeatures(const DeviceFeatures& features) { |
| return features.shaderInt64 && features.shaderInt16 && features.shaderInt8; |
| } |
| void Disable(GpuAVSettings& settings) { settings.ray_tracing_buffers_consistency = false; } |
| std::string DisableMessage() { |
| return "\tRay tracing buffers consistency option was enabled, but the shaderInt64 or shaderInt16 or shaderInt8 " |
| "features are not " |
| "supported. [Disabling " |
| "gpuav_ray_tracing_buffers_consistency]\n"; |
| } |
| }; |
| |
| struct DebugDescriptor : public Setting { |
| bool IsEnabled(const GpuAVSettings& settings) { return settings.debug_descriptor_enabled; } |
| bool HasRequiredFeatures(const DeviceFeatures& features) { return features.shaderInt64; } |
| void Disable(GpuAVSettings& settings) { settings.debug_descriptor_enabled = false; } |
| std::string DisableMessage() { |
| return "\tDebug Descriptor option was enabled, but the shaderInt64 feature is not supported. [Disabling " |
| "debug_descriptor]\n"; |
| } |
| }; |
| |
| } // namespace setting |
| |
| // 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) { |
| setting::BufferDeviceAddress buffer_device_address; |
| setting::RayQuery ray_query; |
| setting::TraceRay trace_ray; |
| setting::MeshShading mesh_shading; |
| setting::BufferCopies buffer_copies; |
| setting::BufferContent buffer_content; |
| setting::AccelerationStructuresBuild as_builds; |
| setting::RayTracingBuffersConsistency rt_buffers_consistency; |
| setting::DebugDescriptor debug_descriptor; |
| std::array<setting::Setting*, 9> all_settings = {&buffer_device_address, &ray_query, &trace_ray, &mesh_shading, |
| &buffer_copies, &buffer_content, &as_builds, &rt_buffers_consistency, |
| &debug_descriptor}; |
| |
| std::string adjustment_warnings; |
| for (auto& setting_object : all_settings) { |
| if (setting_object->IsEnabled(gpuav_settings) && !setting_object->HasRequiredFeatures(modified_features)) { |
| setting_object->Disable(gpuav_settings); |
| adjustment_warnings += setting_object->DisableMessage(); |
| } |
| } |
| if (!adjustment_warnings.empty()) { |
| AdjustmentWarning(device, loc, adjustment_warnings.c_str()); |
| } |
| |
| if (IsExtEnabled(extensions.vk_ext_descriptor_buffer) && phys_dev_ext_props.descriptor_buffer_props.maxResourceDescriptorBufferBindings == 1) { |
| AdjustmentWarning( |
| device, loc, |
| "VK_EXT_descriptor_buffer is enabled with a device that only supports maxResourceDescriptorBufferBindings of " |
| "1\nCurrently the limiation to support VK_EXT_descriptor_buffer is to use our own internal binding to inject a buffer into the shader. [Disabling debug_printf] [Disabling gpuav_shader_instrumentation]"); |
| gpuav_settings.debug_printf_enabled = false; |
| gpuav_settings.DisableShaderInstrumentationAndOptions(); |
| } |
| |
| if (gpuav_settings.debug_descriptor_enabled && !IsExtEnabled(extensions.vk_ext_descriptor_buffer) && |
| !IsExtEnabled(extensions.vk_ext_descriptor_heap)) { |
| AdjustmentWarning(device, loc, |
| "VK_EXT_descriptor_buffer and VK_EXT_descriptor_heap were both not enabled, there is no need/reason to " |
| "use DebugDescriptor. [Disabling debug_descriptor_enabled]"); |
| gpuav_settings.debug_descriptor_enabled = false; |
| } |
| |
| // If we have turned off all the possible things to instrument, turn off everything fully |
| if (!gpuav_settings.IsShaderInstrumentationEnabled()) { |
| gpuav_settings.DisableShaderInstrumentationAndOptions(); |
| } |
| |
| gpuav_settings.TracyLogSettings(); |
| } |
| |
| void Validator::InternalVmaError(LogObjectList objlist, VkResult result, 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); |
| |
| const char* layer_name = gpuav_settings.debug_printf_only ? "DebugPrintf" : "GPU-AV"; |
| const char* vuid = gpuav_settings.debug_printf_only ? "UNASSIGNED-DEBUG-PRINTF" : "UNASSIGNED-GPU-Assisted-Validation"; |
| |
| LogError(vuid, objlist, Location(vvl::Func::Empty), "Internal VMA Error (%s), %s is being disabled. Details:\n%s", |
| string_VkResult(result), 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); |
| } |
| |
| // On machines where all memory types have both DEVICE_LOCAL and HOST_VISIBLE we need to let VMA know there will be host access, |
| // otherwise it will assert https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/issues/515 |
| bool Validator::IsAllDeviceLocalMappable() const { |
| VkPhysicalDeviceMemoryProperties mem_props; |
| DispatchGetPhysicalDeviceMemoryProperties(physical_device, &mem_props); |
| |
| for (uint32_t i = 0; i < mem_props.memoryTypeCount; ++i) { |
| const VkMemoryPropertyFlags property_flags = mem_props.memoryTypes[i].propertyFlags; |
| const bool has_device_local = (property_flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0; |
| const bool has_host_visible = (property_flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; |
| if (has_device_local && !has_host_visible) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| // Things like DescriptorHeap are singleton class that lives in GPU-AV, but are used when state tracking adds/destroy new resources |
| // we need to track. One issue is on vkDestroyDevice we need to teardown the GPU-AV class, then after we try and destroy leaked |
| // state objects (ex. user forgot to call vkDestroySampler). |
| void Validator::DestroySubstate() { |
| if (!dispatch_device_ || aborted_) { |
| return; |
| } |
| |
| // While this is not ideal, it is more important to keep normal code fast and do extra cleanup on teardown |
| for (auto object_it = dispatch_device_->object_dispatch.begin(); object_it != dispatch_device_->object_dispatch.end(); |
| object_it++) { |
| if ((*object_it)->container_type == LayerObjectTypeStateTracker) { |
| auto& state_tracker = dynamic_cast<vvl::DeviceState&>(**object_it); |
| state_tracker.RemoveSubState(LayerObjectTypeGpuAssisted); |
| } |
| } |
| } |
| |
| } // namespace gpuav |