Skip to content
Open
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
1 change: 1 addition & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ vvl_sources = [
"layers/gpuav/shaders/gpuav_error_codes.h",
"layers/gpuav/shaders/gpuav_error_header.h",
"layers/gpuav/shaders/gpuav_shaders_constants.h",
"layers/gpuav/shaders/root_node.h",
"layers/gpuav/spirv/descriptor_indexing_oob_pass.cpp",
"layers/gpuav/spirv/descriptor_indexing_oob_pass.h",
"layers/gpuav/spirv/descriptor_class_general_buffer_pass.cpp",
Expand Down
1 change: 1 addition & 0 deletions layers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ target_sources(vvl PRIVATE
gpuav/shaders/gpuav_error_codes.h
gpuav/shaders/gpuav_error_header.h
gpuav/shaders/gpuav_shaders_constants.h
gpuav/shaders/root_node.h
gpuav/shaders/validation_cmd/push_data.h
object_tracker/object_lifetime_validation.h
object_tracker/object_tracker_utils.cpp
Expand Down
2 changes: 2 additions & 0 deletions layers/chassis/chassis.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ VKAPI_ATTR VkResult VKAPI_CALL AllocateDescriptorSets(VkDevice device, const VkD
VkDescriptorSet* pDescriptorSets);
VKAPI_ATTR VkResult VKAPI_CALL CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
VKAPI_ATTR VkResult VKAPI_CALL AllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo,
const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory);
VKAPI_ATTR VkResult VKAPI_CALL QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo);
VKAPI_ATTR VkResult VKAPI_CALL BeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo);
VKAPI_ATTR VkResult VKAPI_CALL GetPhysicalDeviceToolPropertiesEXT(VkPhysicalDevice physicalDevice, uint32_t* pToolCount,
Expand Down
52 changes: 52 additions & 0 deletions layers/chassis/chassis_manual.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,58 @@ VKAPI_ATTR VkResult VKAPI_CALL CreateBuffer(VkDevice device, const VkBufferCreat
return result;
}

VKAPI_ATTR VkResult VKAPI_CALL AllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo,
const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory) {
VVL_ZoneScoped;

auto device_dispatch = vvl::dispatch::GetData(device);
bool skip = false;
ErrorObject error_obj(vvl::Func::vkAllocateMemory, VulkanTypedHandle(device, kVulkanObjectTypeDevice));
{
VVL_ZoneScopedN("PreCallValidate_vkAllocateMemory");
for (const auto& vo : device_dispatch->intercept_vectors[InterceptIdPreCallValidateAllocateMemory]) {
if (!vo) {
continue;
}
auto lock = vo->ReadLock();
skip |= vo->PreCallValidateAllocateMemory(device, pAllocateInfo, pAllocator, pMemory, error_obj);
if (skip) return VK_ERROR_VALIDATION_FAILED_EXT;
}
}

chassis::AllocateMemory chassis_state{};
chassis_state.allocate_info = *pAllocateInfo;

RecordObject record_obj(vvl::Func::vkAllocateMemory);
{
VVL_ZoneScopedN("PreCallRecord_vkAllocateMemory");
for (auto& vo : device_dispatch->object_dispatch) {
if (!vo) {
continue;
}
auto lock = vo->WriteLock();
vo->PreCallRecordAllocateMemory(device, pAllocateInfo, pAllocator, pMemory, record_obj, chassis_state);
}
}
VkResult result;
{
VVL_ZoneScopedN("Dispatch_vkAllocateMemory");
result = device_dispatch->AllocateMemory(device, &chassis_state.allocate_info, pAllocator, pMemory);
}
record_obj.result = result;
{
VVL_ZoneScopedN("PostCallRecord_vkAllocateMemory");
for (auto& vo : device_dispatch->intercept_vectors[InterceptIdPostCallRecordAllocateMemory]) {
if (!vo) {
continue;
}
auto lock = vo->WriteLock();
vo->PostCallRecordAllocateMemory(device, pAllocateInfo, pAllocator, pMemory, 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;
Expand Down
5 changes: 5 additions & 0 deletions layers/chassis/chassis_modification_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,9 @@ struct CreateBuffer {
VkBufferCreateInfo modified_create_info;
};

struct AllocateMemory {
VkMemoryAllocateInfo allocate_info{};
VkMemoryAllocateFlagsInfo allocate_flags_info{};
};

} // namespace chassis
7 changes: 7 additions & 0 deletions layers/chassis/validation_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ struct ShaderObject;
struct ShaderBinaryData;
struct CreatePipelineLayout;
struct CreateBuffer;
struct AllocateMemory;
} // namespace chassis

namespace vvl {
Expand Down Expand Up @@ -409,6 +410,12 @@ class Device : public Logger {
PreCallRecordCreateBuffer(device, pCreateInfo, pAllocator, pBuffer, record_obj);
}

virtual void PreCallRecordAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo,
const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory,
const RecordObject& record_obj, chassis::AllocateMemory& chassis_state) {
PreCallRecordAllocateMemory(device, pAllocateInfo, pAllocator, pMemory, record_obj);
}

#include "generated/validation_object_device_methods.h"
};

Expand Down
3 changes: 3 additions & 0 deletions layers/gpuav/core/gpuav.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ class Validator : public GpuShaderInstrumentor {
void PreCallRecordDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator, const RecordObject& record_obj) final;
void PreCallRecordCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator,
VkBuffer* pBuffer, const RecordObject& record_obj, chassis::CreateBuffer& chassis_state) final;
void PreCallRecordAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo,
const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory,
const RecordObject& record_obj, chassis::AllocateMemory& chassis_state) final;
void PreCallRecordBeginCommandBuffer(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo,
const RecordObject& record_obj) final;

Expand Down
35 changes: 13 additions & 22 deletions layers/gpuav/core/gpuav_constants.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* Copyright (c) 2018-2024 The Khronos Group Inc.
* Copyright (c) 2018-2024 Valve Corporation
* Copyright (c) 2018-2024 LunarG, Inc.
/* 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.
Expand All @@ -25,28 +25,19 @@ namespace cst {
// Number of indices held in the buffer used to index commands and validation resources
inline constexpr uint32_t indices_count = 16384;

// Stream Output Buffer Offsets
//
// The following values provide offsets into the output buffer struct
// ------------------------------
// Error Output Buffer Offsets
// ---

// The 1st member of the debug output buffer contains a set of flags
// controlling the behavior of instrumentation code.
inline constexpr uint32_t stream_output_flags_offset = 0;
// Error output buffer size
inline constexpr uint32_t error_output_buffer_size_offset = 0;

// Values stored at output_flags_offset
inline constexpr uint32_t inst_buffer_oob_enabled = 0x1;
// Written words count in error output buffer.
// Shaders will atomically read and update this value so as not to overwrite each others records. This value must be initialized to
// zero
inline constexpr uint32_t error_output_buffer_written_words_count_offset = 1;

// The 2nd member of the debug output buffer contains the next available word
// in the data stream to be written. Shaders will atomically read and update
// this value so as not to overwrite each others records. This value must be
// initialized to zero
inline constexpr uint32_t stream_output_size_offset = 1;

// The 3rd member of the output buffer is the start of the stream of records
// written by the instrumented shaders. Each record represents a validation
// error. The format of the records is documented below.
inline constexpr uint32_t stream_output_data_offset = 2;
// Error output buffer
inline constexpr uint32_t error_output_buffer_error_records_offset = 2;

} // namespace cst
} // namespace gpuav
38 changes: 19 additions & 19 deletions layers/gpuav/core/gpuav_record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,30 @@ void Validator::PreCallRecordCreateBuffer(VkDevice device, const VkBufferCreateI
const auto *flags2 = vku::FindStructInPNextChain<VkBufferUsageFlags2CreateInfo>(chassis_state.modified_create_info.pNext);
const VkBufferUsageFlags2 in_usage = flags2 ? flags2->usage : chassis_state.modified_create_info.usage;

// Ray tracing acceleration structure instance buffers also need the storage buffer usage as
// acceleration structure build validation will find and replace invalid acceleration structure
// handles inside of a compute shader.
if (in_usage & VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR) {
if (flags2) {
const_cast<VkBufferUsageFlags2CreateInfo *>(flags2)->usage |= VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT;
} else {
chassis_state.modified_create_info.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
if (gpuav_settings.IsBufferValidationEnabled()) {
if (in_usage & (VK_BUFFER_USAGE_2_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_2_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_2_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_2_TRANSFER_DST_BIT)) {
if (flags2) {
const_cast<VkBufferUsageFlags2CreateInfo *>(flags2)->usage |= VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT;
} else {
chassis_state.modified_create_info.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
}
}
}

// Indirect buffers will require validation shader to bind the indirect buffers as a storage buffer.
if (gpuav_settings.IsBufferValidationEnabled() &&
(in_usage & (VK_BUFFER_USAGE_2_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_2_INDEX_BUFFER_BIT))) {
if (flags2) {
const_cast<VkBufferUsageFlags2CreateInfo *>(flags2)->usage |= VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT;
} else {
chassis_state.modified_create_info.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
}
// Align index buffer size to 4: validation shader reads DWORDS
chassis_state.modified_create_info.size = Align<VkDeviceSize>(chassis_state.modified_create_info.size, 4);
}
}

// Align index buffer size to 4: validation shader reads DWORDS
void Validator::PreCallRecordAllocateMemory(VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo,
const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMemory,
const RecordObject &record_obj, chassis::AllocateMemory &chassis_state) {
if (gpuav_settings.IsBufferValidationEnabled()) {
chassis_state.modified_create_info.size = Align<VkDeviceSize>(chassis_state.modified_create_info.size, 4);
if (chassis_state.allocate_flags_info.sType == 0) {
chassis_state.allocate_flags_info = vku::InitStructHelper();
}
chassis_state.allocate_flags_info.flags |= VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
chassis_state.allocate_info.pNext = &chassis_state.allocate_flags_info;
}
}

Expand Down
24 changes: 4 additions & 20 deletions layers/gpuav/core/gpuav_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,24 +211,8 @@ void Validator::FinishDeviceSetup(const VkDeviceCreateInfo *pCreateInfo, const L
}

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},
// Vertex attribute fetch limits
{glsl::kBindingInstVertexAttributeFetchLimits, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr},
// Root Node Address
{glsl::kBindingInstRootNode, 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 FinishDeviceSetup if possible (or at least
Expand Down Expand Up @@ -264,7 +248,7 @@ void Validator::FinishDeviceSetup(const VkDeviceCreateInfo *pCreateInfo, const L
{
VkBufferCreateInfo error_buffer_ci = vku::InitStructHelper();
error_buffer_ci.size = glsl::kErrorBufferByteSize;
error_buffer_ci.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
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;
Expand All @@ -280,7 +264,7 @@ void Validator::FinishDeviceSetup(const VkDeviceCreateInfo *pCreateInfo, const L
{
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.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
buffer_info.size = cst::indices_count * indices_buffer_alignment_;
VmaAllocationCreateInfo alloc_info = {};
alloc_info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
Expand Down
39 changes: 8 additions & 31 deletions layers/gpuav/core/gpuav_validation_pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,18 @@ namespace valpipe {
namespace internal {

bool CreateComputePipelineHelper(Validator &gpuav, const Location &loc,
const std::vector<VkDescriptorSetLayoutBinding> specific_bindings,

VkDescriptorSetLayout additional_desc_set_layout, uint32_t push_constants_byte_size,
uint32_t spirv_size, const uint32_t *spirv, VkDevice &out_device,
VkDescriptorSetLayout &out_specific_descriptor_set_layout, VkPipelineLayout &out_pipeline_layout,
VkShaderModule &out_shader_module, VkPipeline &out_pipeline) {
VkPipelineLayout &out_pipeline_layout, VkShaderModule &out_shader_module,
VkPipeline &out_pipeline) {
out_device = gpuav.device;
VkPushConstantRange push_constant_range = {};
push_constant_range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
push_constant_range.offset = 0;
push_constant_range.size = push_constants_byte_size;

VkDescriptorSetLayoutCreateInfo ds_layout_ci = vku::InitStructHelper();

ds_layout_ci.bindingCount = static_cast<uint32_t>(specific_bindings.size());
ds_layout_ci.pBindings = specific_bindings.data();
VkResult result = DispatchCreateDescriptorSetLayout(gpuav.device, &ds_layout_ci, nullptr, &out_specific_descriptor_set_layout);
if (result != VK_SUCCESS) {
gpuav.InternalError(gpuav.device, loc, "Failed to create descriptor set layout.");
return false;
}

std::vector<VkDescriptorSetLayout> set_layouts = {out_specific_descriptor_set_layout};
std::vector<VkDescriptorSetLayout> set_layouts;
if (additional_desc_set_layout != VK_NULL_HANDLE) {
set_layouts.emplace_back(additional_desc_set_layout);
}
Expand All @@ -61,8 +51,8 @@ bool CreateComputePipelineHelper(Validator &gpuav, const Location &loc,
pipeline_layout_ci.pPushConstantRanges = &push_constant_range;
}
pipeline_layout_ci.setLayoutCount = static_cast<uint32_t>(set_layouts.size());
pipeline_layout_ci.pSetLayouts = set_layouts.data();
result = DispatchCreatePipelineLayout(gpuav.device, &pipeline_layout_ci, nullptr, &out_pipeline_layout);
pipeline_layout_ci.pSetLayouts = !set_layouts.empty() ? set_layouts.data() : nullptr;
VkResult result = DispatchCreatePipelineLayout(gpuav.device, &pipeline_layout_ci, nullptr, &out_pipeline_layout);
if (result != VK_SUCCESS) {
gpuav.InternalError(gpuav.device, loc, "Failed to create pipeline layout.");
return false;
Expand Down Expand Up @@ -93,12 +83,8 @@ bool CreateComputePipelineHelper(Validator &gpuav, const Location &loc,
return true;
}

void DestroyComputePipelineHelper(VkDevice device, VkDescriptorSetLayout specific_descriptor_set_layout,
VkPipelineLayout pipeline_layout, VkShaderModule shader_module, VkPipeline pipeline) {
if (specific_descriptor_set_layout != VK_NULL_HANDLE) {
DispatchDestroyDescriptorSetLayout(device, specific_descriptor_set_layout, nullptr);
}

void DestroyComputePipelineHelper(VkDevice device, VkPipelineLayout pipeline_layout, VkShaderModule shader_module,
VkPipeline pipeline) {
if (pipeline_layout != VK_NULL_HANDLE) {
DispatchDestroyPipelineLayout(device, pipeline_layout, nullptr);
}
Expand All @@ -117,21 +103,12 @@ VkDescriptorSet GetDescriptorSetHelper(CommandBufferSubState &cb_state, VkDescri
}

void BindShaderResourcesHelper(Validator &gpuav, CommandBufferSubState &cb_state, VkPipelineLayout pipeline_layout,
VkDescriptorSet desc_set, const std::vector<VkWriteDescriptorSet> &descriptor_writes,
const uint32_t push_constants_byte_size, const void *push_constants) {
// Any push constants byte size below 4 is illegal. Can come from empty push constant struct
if (push_constants_byte_size >= 4) {
DispatchCmdPushConstants(cb_state.VkHandle(), pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, push_constants_byte_size,
push_constants);
}

if (!descriptor_writes.empty()) {
// Specific resources
DispatchUpdateDescriptorSets(gpuav.device, uint32_t(descriptor_writes.size()), descriptor_writes.data(), 0, nullptr);

DispatchCmdBindDescriptorSets(cb_state.VkHandle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, glsl::kValPipeDescSet,
1, &desc_set, 0, nullptr);
}
}

} // namespace internal
Expand Down
Loading
Loading