Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ vvl_sources = [
"layers/core_checks/cc_wsi.cpp",
"layers/core_checks/cc_ycbcr.cpp",
"layers/core_checks/core_validation.h",
"layers/drawdispatch/descriptor_heap_validator.cpp",
"layers/drawdispatch/descriptor_heap_validator.h",
"layers/drawdispatch/descriptor_validator.cpp",
"layers/drawdispatch/descriptor_validator.h",
"layers/drawdispatch/drawdispatch_vuids.cpp",
Expand Down Expand Up @@ -212,6 +214,8 @@ vvl_sources = [
"layers/gpuav/instrumentation/descriptor_checks.cpp",
"layers/gpuav/instrumentation/descriptor_checks.h",
"layers/gpuav/instrumentation/mesh_shading.cpp",
"layers/gpuav/instrumentation/post_process_descriptor_heap.cpp",
"layers/gpuav/instrumentation/post_process_descriptor_heap.h",
"layers/gpuav/instrumentation/post_process_descriptor_indexing.cpp",
"layers/gpuav/instrumentation/ray_query.cpp",
"layers/gpuav/instrumentation/ray_hit_object.cpp",
Expand All @@ -236,6 +240,8 @@ vvl_sources = [
"layers/gpuav/spirv/descriptor_class_general_buffer_pass.h",
"layers/gpuav/spirv/descriptor_class_texel_buffer_pass.cpp",
"layers/gpuav/spirv/descriptor_class_texel_buffer_pass.h",
"layers/gpuav/spirv/descriptor_heap_pass.cpp",
"layers/gpuav/spirv/descriptor_heap_pass.h",
"layers/gpuav/spirv/buffer_device_address_pass.cpp",
"layers/gpuav/spirv/buffer_device_address_pass.h",
"layers/gpuav/spirv/mesh_shading_pass.cpp",
Expand Down
4 changes: 4 additions & 0 deletions layers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ target_sources(vvl PRIVATE
core_checks/cc_vuid_maps.h
core_checks/cc_wsi.cpp
core_checks/cc_ycbcr.cpp
drawdispatch/descriptor_heap_validator.cpp
drawdispatch/descriptor_heap_validator.h
drawdispatch/descriptor_validator.cpp
drawdispatch/descriptor_validator.h
drawdispatch/drawdispatch_vuids.cpp
Expand Down Expand Up @@ -338,6 +340,8 @@ target_sources(vvl PRIVATE
gpuav/instrumentation/descriptor_checks.cpp
gpuav/instrumentation/mesh_shading.cpp
gpuav/instrumentation/post_process_descriptor_indexing.cpp
gpuav/instrumentation/post_process_descriptor_heap.h
gpuav/instrumentation/post_process_descriptor_heap.cpp
gpuav/instrumentation/ray_query.cpp
gpuav/instrumentation/ray_hit_object.cpp
gpuav/instrumentation/register_validation.h
Expand Down
111 changes: 111 additions & 0 deletions layers/drawdispatch/descriptor_heap_validator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/* Copyright (c) 2025-2026 The Khronos Group Inc.
* Copyright (c) 2025-2026 Valve Corporation
* Copyright (c) 2025-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 "descriptor_heap_validator.h"
#include "state_tracker/pipeline_state.h"
#include "drawdispatch/drawdispatch_vuids.h"
#include "gpuav/core/gpuav.h"
#include "gpuav/resources/gpuav_state_trackers.h"
#include "state_tracker/shader_module.h"
#include "utils/math_utils.h"
#include "utils/shader_utils.h"
#include "../layers/containers/container_utils.h"
#include "../layers/core_checks/cc_buffer_address.h"
#include <xxhash.h>

namespace vvl {

DescriptorHeapValidator::DescriptorHeapValidator(vvl::DeviceProxy& dev, gpuav::CommandBufferSubState& cb_state, uint32_t set_index,
const LogObjectList* objlist, const Location& loc)
: Logger(dev.debug_report),
cb_state(cb_state),
loc(loc),
vuids(&GetDrawDispatchVuid(loc.function)),
objlist(objlist) {}

bool DescriptorHeapValidator::ValidateBinding(gpuav::Validator &gpuav, const spirv::ResourceInterfaceVariable &resource_variable,
const VkDescriptorSetAndBindingMappingEXT &mapping,
const gpuav::vko::IndirectAccessMap &indirect_access, uint32_t byte_offset,
bool pipeline, bool robustness) {
bool skip = false;

if (IsValueIn(mapping.source,
{VK_DESCRIPTOR_MAPPING_SOURCE_PUSH_ADDRESS_EXT, VK_DESCRIPTOR_MAPPING_SOURCE_SHADER_RECORD_ADDRESS_EXT,
VK_DESCRIPTOR_MAPPING_SOURCE_INDIRECT_ADDRESS_EXT})) {
VkDeviceAddress address = 0;
if (mapping.source == VK_DESCRIPTOR_MAPPING_SOURCE_PUSH_ADDRESS_EXT) {
address = cb_state.GetPushData<VkDeviceAddress>(mapping.sourceData.pushAddressOffset);
} else if (mapping.source == VK_DESCRIPTOR_MAPPING_SOURCE_INDIRECT_ADDRESS_EXT) {
gpuav::vko::IndirectKey key = {false, mapping.sourceData.indirectAddress.pushOffset,
mapping.sourceData.indirectAddress.addressOffset};
if (auto buffer = indirect_access->find(key); buffer != indirect_access->end()) {
VkDeviceAddress *indirect_address = static_cast<VkDeviceAddress *>(buffer->second.GetHostBufferPtr());
address = *indirect_address;
}
} else if (mapping.source == VK_DESCRIPTOR_MAPPING_SOURCE_SHADER_RECORD_ADDRESS_EXT) {
gpuav::vko::IndirectKey key = {true, 0, mapping.sourceData.shaderRecordAddressOffset};
if (auto buffer = indirect_access->find(key); buffer != indirect_access->end()) {
address = *static_cast<VkDeviceAddress *>(buffer->second.GetHostBufferPtr());
}
}
std::string usage_vuid = {};
VkBufferUsageFlagBits2 required_usage_bit = 0;

if (resource_variable.is_uniform_buffer) {
usage_vuid = CreateActionVuid(vuids->function, ActionVUID::HEAP_USAGE_11438);
}

if (mapping.source == VK_DESCRIPTOR_MAPPING_SOURCE_PUSH_ADDRESS_EXT ||
mapping.source == VK_DESCRIPTOR_MAPPING_SOURCE_INDIRECT_ADDRESS_EXT) {
if (!usage_vuid.empty()) {
std::string msg =
"The following buffers are missing " + std::string(string_VkBufferUsageFlagBits2(required_usage_bit));
BufferAddressValidation<1> buffer_address_validator = {{{{usage_vuid,
[required_usage_bit](const vvl::Buffer &buffer_state) {
return (buffer_state.usage & required_usage_bit) == 0;
},
[msg]() { return msg; }, kUsageErrorMsgBuffer}}}};

skip |=
buffer_address_validator.ValidateDeviceAddress(gpuav, loc.Get(), *objlist, address, 0u);
}
}
}

return skip;
}

bool DescriptorHeapValidator::ValidateBinding(gpuav::Validator &gpuav, const spirv::ResourceInterfaceVariable &resource_variable,
const VkShaderDescriptorSetAndBindingMappingInfoEXT &mappings,
const gpuav::vko::IndirectAccessMap &indirect_access, uint32_t byte_offset,
bool pipeline, bool robustness) {
bool skip = false;

for (uint32_t i = 0; i < mappings.mappingCount; ++i) {
const auto &mapping = mappings.pMappings[i];
if (mapping.descriptorSet == resource_variable.decorations.set &&
mapping.firstBinding == resource_variable.decorations.binding &&
ResourceTypeMatchesBinding(mapping.resourceMask, resource_variable)) {
skip |= ValidateBinding(gpuav, resource_variable, mapping, indirect_access, byte_offset, pipeline, robustness);
break;
}
}

return skip;
}

} // namespace vvl
58 changes: 58 additions & 0 deletions layers/drawdispatch/descriptor_heap_validator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* Copyright (c) 2025-2026 The Khronos Group Inc.
* Copyright (c) 2025-2026 Valve Corporation
* Copyright (c) 2025-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.
*/

#pragma once
#include <vulkan/vulkan.h>
#include "error_message/error_location.h"
#include "gpuav/resources/gpuav_vulkan_objects.h"

namespace spirv {
struct ResourceInterfaceVariable;
} // namespace spirv

namespace gpuav {
class Validator;
class CommandBufferSubState;
} // namespace gpuav

namespace vvl {
struct DrawDispatchVuid;
class DeviceProxy;

class DescriptorHeapValidator : public Logger {
public:
DescriptorHeapValidator(DeviceProxy& dev, gpuav::CommandBufferSubState& cb_state, uint32_t set_index,
const LogObjectList* objlist, const Location& loc);

bool ValidateBinding(gpuav::Validator& gpuav, const spirv::ResourceInterfaceVariable& resource_variable,
const VkShaderDescriptorSetAndBindingMappingInfoEXT& mappings,
const gpuav::vko::IndirectAccessMap& indirect_access, uint32_t byte_offset, bool pipeline,
bool robustness);

void SetObjlistForGpuAv(const LogObjectList* objlist) { this->objlist = objlist; }

private:
bool ValidateBinding(gpuav::Validator& gpuav, const spirv::ResourceInterfaceVariable& resource_variable,
const VkDescriptorSetAndBindingMappingEXT& mapping, const gpuav::vko::IndirectAccessMap& indirect_access,
uint32_t byte_offset, bool pipeline, bool robustness);

gpuav::CommandBufferSubState& cb_state;
LocationCapture loc;
const DrawDispatchVuid* vuids;
const LogObjectList* objlist; // VkPipeline or VkShaderObject
};
} // namespace vvl
2 changes: 2 additions & 0 deletions layers/drawdispatch/drawdispatch_vuids.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,8 @@ std::string CreateActionVuid(vvl::Func function, const ActionVUID id) {
case ActionVUID::DESCRIPTOR_INDEX_OOB_10068: suffix = "None-10068"; break;
// ### VUID-vkCmdDrawIndirectCount-countBuffer-02717
case ActionVUID::INDIRECT_COUNT_LIMIT: suffix = "countBuffer-02717"; break;
// ### VUID-vkCmdDraw-None-11438
case ActionVUID::HEAP_USAGE_11438: suffix = "None-11438"; break;
}
// clang-format on

Expand Down
1 change: 1 addition & 0 deletions layers/drawdispatch/drawdispatch_vuids.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ enum class ActionVUID {
INVALID_DESCRIPTOR_08114,
DESCRIPTOR_INDEX_OOB_10068,
INDIRECT_COUNT_LIMIT,
HEAP_USAGE_11438,
};

std::string CreateActionVuid(vvl::Func function, const ActionVUID id);
Expand Down
4 changes: 3 additions & 1 deletion layers/gpuav/core/gpuav.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ class Validator : public GpuShaderInstrumentor {
const VkDescriptorBufferBindingInfoEXT* pBindingInfos,
const RecordObject& record_obj,
chassis::CmdBindDescriptorBuffers& chassis_state) final;
void PreCallRecordCmdPushDataEXT(VkCommandBuffer commandBuffer, const VkPushDataInfoEXT* pPushDataInfo,
const RecordObject& record_obj) final;

void PreCallActionCommand(Validator& gpuav, CommandBufferSubState& cb_state, const LastBound& last_bound, const Location& loc);

Expand Down Expand Up @@ -294,7 +296,7 @@ class Validator : public GpuShaderInstrumentor {
// VK_EXT_descriptor_heap global tracking
struct ResourceHeap {
const vvl::Buffer* buffer_state_ = nullptr;
VkDeviceSize reserved_offset_ = 0;
VkDeviceSize size_ = 0;
} resource_heap;

vko::Buffer& GetGlobalDescriptorBuffer();
Expand Down
19 changes: 19 additions & 0 deletions layers/gpuav/core/gpuav_record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "gpuav/instrumentation/descriptor_checks.h"
#include "gpuav/instrumentation/gpuav_instrumentation.h"
#include "gpuav/instrumentation/register_validation.h"
#include "gpuav/instrumentation/post_process_descriptor_heap.h"
#include "gpuav/resources/gpuav_state_trackers.h"
#include "gpuav/shaders/gpuav_shaders_constants.h"
#include "gpuav/validation_cmd/gpuav_copy_buffer_to_image.h"
Expand Down Expand Up @@ -81,6 +82,13 @@ void Validator::PreCallRecordCreateBuffer(VkDevice device, const VkBufferCreateI
chassis_state.modified_create_info.size = Align<VkDeviceSize>(chassis_state.modified_create_info.size, 4);
}

// If descriptor heaps are used, uniform buffers can be used for indirect indices and we need to be able to copy from them
if (gpuav_settings.shader_instrumentation.descriptor_heap) {
if (in_usage & VK_BUFFER_USAGE_2_UNIFORM_BUFFER_BIT) {
chassis_state.modified_create_info.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
}
}

chassis_state.create_info_copy = chassis_state.modified_create_info.ptr();
}

Expand Down Expand Up @@ -153,13 +161,24 @@ void Validator::PreCallRecordCmdBindDescriptorBuffersEXT(VkCommandBuffer command
chassis_state.pBindInfos = reinterpret_cast<VkDescriptorBufferBindingInfoEXT*>(chassis_state.modified_binding_infos.data());
}

void Validator::PreCallRecordCmdPushDataEXT(VkCommandBuffer commandBuffer, const VkPushDataInfoEXT* pPushDataInfo,
const RecordObject& record_obj) {
auto cb_state = GetWrite<vvl::CommandBuffer>(commandBuffer);
CommandBufferSubState& gpuav_cb_state = SubState(*cb_state);
if (pPushDataInfo->offset + pPushDataInfo->data.size > gpuav_cb_state.push_data_.size()) {
gpuav_cb_state.push_data_.resize(pPushDataInfo->offset + pPushDataInfo->data.size);
}
memcpy(gpuav_cb_state.push_data_.data() + pPushDataInfo->offset, pPushDataInfo->data.address, pPushDataInfo->data.size);
}

void Validator::PreCallRecordBeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo,
const RecordObject& record_obj) {
auto cb_state = GetWrite<vvl::CommandBuffer>(commandBuffer);

CommandBufferSubState& gpuav_cb_state = SubState(*cb_state);
RegisterDescriptorChecksValidation(*this, gpuav_cb_state);
RegisterPostProcessingValidation(*this, gpuav_cb_state);
RegisterPostProcessingDescriptorHeap(*this, gpuav_cb_state);
RegisterBufferDeviceAddressValidation(*this, gpuav_cb_state);
RegisterVertexAttributeFetchOobValidation(*this, gpuav_cb_state);
RegisterMeshShadingValidation(*this, gpuav_cb_state);
Expand Down
4 changes: 3 additions & 1 deletion layers/gpuav/core/gpuav_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ bool GpuAVSettings::IsShaderInstrumentationEnabled() const {
return shader_instrumentation.descriptor_checks || shader_instrumentation.buffer_device_address ||
shader_instrumentation.ray_query || shader_instrumentation.ray_hit_object || shader_instrumentation.mesh_shading ||
shader_instrumentation.post_process_descriptor_indexing || shader_instrumentation.vertex_attribute_fetch_oob ||
shader_instrumentation.sanitizer || shader_instrumentation.shared_memory_data_race;
shader_instrumentation.sanitizer || shader_instrumentation.shared_memory_data_race ||
shader_instrumentation.descriptor_heap;
}
bool GpuAVSettings::IsSpirvModified() const { return IsShaderInstrumentationEnabled() || debug_printf_enabled; }

Expand All @@ -48,6 +49,7 @@ void GpuAVSettings::DisableShaderInstrumentationAndOptions() {
shader_instrumentation.vertex_attribute_fetch_oob = false;
shader_instrumentation.sanitizer = false;
shader_instrumentation.shared_memory_data_race = false;
shader_instrumentation.descriptor_heap = false;
// Because of this setting, cannot really have an "enabled" parameter to pass to this method
select_instrumented_shaders = false;
}
Expand Down
1 change: 1 addition & 0 deletions layers/gpuav/core/gpuav_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ struct GpuAVSettings {
bool vertex_attribute_fetch_oob = true;
bool sanitizer = true;
bool shared_memory_data_race = true;
bool descriptor_heap = true;
} shader_instrumentation;

bool IsShaderInstrumentationEnabled() const;
Expand Down
4 changes: 4 additions & 0 deletions layers/gpuav/core/gpuav_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ void Validator::FinishDeviceSetup(const VkDeviceCreateInfo* pCreateInfo, const L
{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},
// Descriptor heap post processing
{glsl::kBindingInstDescriptorHeapPostProcess, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
// Descriptor heap valid descriptors buffer
{glsl::kBindingInstDescriptorHeapValidDescriptors, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
};
assert(instrumentation_bindings_.size() == glsl::kTotalBindings);

Expand Down
15 changes: 14 additions & 1 deletion layers/gpuav/instrumentation/gpuav_shader_instrumentor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "gpuav/spirv/post_process_descriptor_indexing_pass.h"
#include "gpuav/spirv/vertex_attribute_fetch_oob_pass.h"
#include "gpuav/spirv/sanitizer_pass.h"
#include "gpuav/spirv/descriptor_heap_pass.h"

#include <cassert>
#include <string>
Expand Down Expand Up @@ -204,7 +205,8 @@ void GpuShaderInstrumentor::SetupDescriptorHeap(const Location& loc) {

resource_heap_reserved_bytes_ = bytes_to_reserve;
buffer_descriptor_size_ = descriptor_heap_props.bufferDescriptorSize;
buffer_descriptor_alignment_ = descriptor_heap_props.bufferDescriptorAlignment;
image_descriptor_size_ = descriptor_heap_props.imageDescriptorSize;
sampler_descriptor_size_ = descriptor_heap_props.samplerDescriptorSize;
push_data_offset_ = static_cast<uint32_t>(descriptor_heap_props.maxPushDataSize) - 8u;
}

Expand Down Expand Up @@ -1704,6 +1706,17 @@ bool GpuShaderInstrumentor::InstrumentShader(const vvl::span<const uint32_t>& in
modified |= pass.Run();
}

if (gpuav_settings.shader_instrumentation.descriptor_heap) {
if (interface.descriptor_mode == vvl::DescriptorModeHeap) {
VkPhysicalDeviceDescriptorHeapTensorPropertiesARM heap_tensor_props = vku::InitStructHelper();
VkPhysicalDeviceDescriptorHeapPropertiesEXT heap_props = vku::InitStructHelper(&heap_tensor_props);
VkPhysicalDeviceProperties2 props2 = vku::InitStructHelper(&heap_props);
DispatchGetPhysicalDeviceProperties2(physical_device, &props2);
spirv::DescriptorHeapPass descriptor_heap_pass(module, heap_props, heap_tensor_props);
modified |= descriptor_heap_pass.Run();
}
}

// If we have passes that require inject LogError before the shader end we do it now.
// We have a dedicated pass to ensure the LogError is only added once
if (module.need_log_error_) {
Expand Down
3 changes: 2 additions & 1 deletion layers/gpuav/instrumentation/gpuav_shader_instrumentor.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ class GpuShaderInstrumentor : public vvl::DeviceProxy {
// Size to reserve in front of reserved range in resource heap
VkDeviceSize resource_heap_reserved_bytes_ = 0;
VkDeviceSize buffer_descriptor_size_ = 0;
VkDeviceSize buffer_descriptor_alignment_ = 0;
VkDeviceSize image_descriptor_size_ = 0;
VkDeviceSize sampler_descriptor_size_ = 0;
uint32_t push_data_offset_ = 0;

// These are the same as enabled_features, but may have been altered at setup time. This should be use for any feature GPU-AV
Expand Down
Loading
Loading