blob: 6bae16f32d1c890dfe06b80d03a1fbc8f0c9640d [file]
/*
* Copyright (c) 2026 The Khronos Group Inc.
* Copyright (c) 2026 Valve Corporation
* Copyright (c) 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
*/
#include <vulkan/vulkan_core.h>
#include "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
#include "../framework/buffer_helper.h"
#include "../utils/math_utils.h"
void GpuAVDescriptorHeap::InitGpuAVDescriptorHeap(bool safe_mode) {
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::descriptorHeap);
RETURN_IF_SKIP(InitGpuAvFramework({}, safe_mode));
RETURN_IF_SKIP(InitState());
GetPhysicalDeviceProperties2(heap_props);
}
void GpuAVDescriptorHeap::CreateResourceHeap(VkDeviceSize app_size) {
const VkDeviceSize heap_size = AlignResource(app_size) + heap_props.minResourceHeapReservedRange;
VkBufferUsageFlags2CreateInfo buffer_usage = vku::InitStructHelper();
buffer_usage.usage = VK_BUFFER_USAGE_2_DESCRIPTOR_HEAP_BIT_EXT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT;
VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper();
allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
resource_heap_.Init(*m_device, vkt::Buffer::CreateInfo(heap_size, 0, {}, &buffer_usage),
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info);
resource_heap_data_ = static_cast<uint8_t*>(resource_heap_.Memory().Map());
}
void GpuAVDescriptorHeap::CreateSamplerHeap(VkDeviceSize app_size, bool use_embedded_samplers) {
embedded_samplers = use_embedded_samplers;
const VkDeviceSize reserved_range =
(embedded_samplers ? heap_props.minSamplerHeapReservedRangeWithEmbedded : heap_props.minSamplerHeapReservedRange);
const VkDeviceSize heap_size = AlignSampler(app_size + reserved_range);
VkBufferUsageFlags2CreateInfo buffer_usage = vku::InitStructHelper();
buffer_usage.usage = VK_BUFFER_USAGE_2_DESCRIPTOR_HEAP_BIT_EXT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT;
VkMemoryAllocateFlagsInfo allocate_flag_info = vku::InitStructHelper();
allocate_flag_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
sampler_heap_.Init(*m_device, vkt::Buffer::CreateInfo(heap_size, 0, {}, &buffer_usage),
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &allocate_flag_info);
sampler_heap_data_ = static_cast<uint8_t*>(sampler_heap_.Memory().Map());
}
void GpuAVDescriptorHeap::BindResourceHeap() {
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange.address = resource_heap_.Address();
bind_resource_info.heapRange.size = resource_heap_.CreateInfo().size;
bind_resource_info.reservedRangeOffset = resource_heap_.CreateInfo().size - heap_props.minResourceHeapReservedRange;
bind_resource_info.reservedRangeSize = heap_props.minResourceHeapReservedRange;
vk::CmdBindResourceHeapEXT(m_command_buffer, &bind_resource_info);
}
void GpuAVDescriptorHeap::BindSamplerHeap() {
const VkDeviceSize min_reserved_range =
embedded_samplers ? heap_props.minSamplerHeapReservedRangeWithEmbedded : heap_props.minSamplerHeapReservedRange;
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange.address = sampler_heap_.Address();
bind_resource_info.heapRange.size = sampler_heap_.CreateInfo().size;
bind_resource_info.reservedRangeOffset = sampler_heap_.CreateInfo().size - min_reserved_range;
bind_resource_info.reservedRangeSize = min_reserved_range;
vk::CmdBindSamplerHeapEXT(m_command_buffer, &bind_resource_info);
}
VkDeviceSize GpuAVDescriptorHeap::AlignResource(VkDeviceSize offset) {
return Align(Align(offset, heap_props.bufferDescriptorAlignment), heap_props.imageDescriptorAlignment);
}
VkDeviceSize GpuAVDescriptorHeap::AlignSampler(VkDeviceSize offset) { return Align(offset, heap_props.samplerDescriptorAlignment); }
class NegativeGpuAVDescriptorHeap : public GpuAVDescriptorHeap {};
TEST_F(NegativeGpuAVDescriptorHeap, IndexBufferOOB) {
TEST_DESCRIPTION("Validate overruning the index buffer");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
InitRenderTarget();
VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
CreatePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
pipe.gp_ci_.layout = VK_NULL_HANDLE;
pipe.gp_ci_.stageCount = pipe.shader_stages_.size();
pipe.gp_ci_.pStages = pipe.shader_stages_.data();
pipe.CreateGraphicsPipeline(false);
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 3;
draw_params.instanceCount = 1;
draw_params.firstIndex = 1;
draw_params.vertexOffset = 0;
draw_params.firstInstance = 0;
vkt::Buffer draw_params_buffer = vkt::IndirectBuffer<VkDrawIndexedIndirectCommand>(*m_device, {draw_params});
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {1, 2, 3});
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
m_errorMonitor->SetDesiredErrorRegex("VUID-VkDrawIndexedIndirectCommand-robustBufferAccess2-08798",
"Index 4 is not within the bound index buffer.");
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, sizeof(VkDrawIndexedIndirectCommand));
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeGpuAVDescriptorHeap, NoMappings) {
TEST_DESCRIPTION("Validate illegal index buffer values with no VkShaderDescriptorSetAndBindingMappingInfoEXT provided");
RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
InitRenderTarget();
const char* vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
void main() {
gl_Position = vec4(pos, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
CreatePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
pipe.gp_ci_.layout = VK_NULL_HANDLE;
VkVertexInputBindingDescription input_binding = {0, 3 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
VkVertexInputAttributeDescription input_attrib = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
pipe.vi_ci_.pVertexAttributeDescriptions = &input_attrib;
pipe.vi_ci_.vertexAttributeDescriptionCount = 1;
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.gp_ci_.stageCount = pipe.shader_stages_.size();
pipe.gp_ci_.pStages = pipe.shader_stages_.data();
pipe.CreateGraphicsPipeline(false);
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 3;
draw_params.instanceCount = 1;
draw_params.firstIndex = 0;
draw_params.vertexOffset = 0;
draw_params.firstInstance = 0;
vkt::Buffer draw_params_buffer = vkt::IndirectBuffer<VkDrawIndexedIndirectCommand>(*m_device, {draw_params});
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, 666, 42});
vkt::Buffer vertex_buffer = vkt::VertexBuffer<float>(*m_device, {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
VkDeviceSize vertex_buffer_offset = 0;
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 666");
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 42");
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, sizeof(VkDrawIndexedIndirectCommand));
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeGpuAVDescriptorHeap, NoHeapBound) {
TEST_DESCRIPTION("Validate illegal index buffer values when no descriptor heap is bound");
RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
InitRenderTarget();
const char* vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
void main() {
gl_Position = vec4(pos, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
CreatePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
pipe.gp_ci_.layout = VK_NULL_HANDLE;
VkVertexInputBindingDescription input_binding = {0, 3 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
VkVertexInputAttributeDescription input_attrib = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
pipe.vi_ci_.pVertexAttributeDescriptions = &input_attrib;
pipe.vi_ci_.vertexAttributeDescriptionCount = 1;
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.gp_ci_.stageCount = pipe.shader_stages_.size();
pipe.gp_ci_.pStages = pipe.shader_stages_.data();
pipe.CreateGraphicsPipeline(false);
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 3;
draw_params.instanceCount = 1;
draw_params.firstIndex = 0;
draw_params.vertexOffset = 0;
draw_params.firstInstance = 0;
vkt::Buffer draw_params_buffer = vkt::IndirectBuffer<VkDrawIndexedIndirectCommand>(*m_device, {draw_params});
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, 666, 42});
vkt::Buffer vertex_buffer = vkt::VertexBuffer<float>(*m_device, {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
VkDeviceSize vertex_buffer_offset = 0;
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 666");
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 42");
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, sizeof(VkDrawIndexedIndirectCommand));
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeGpuAVDescriptorHeap, HeapBoundBeforePipeline) {
TEST_DESCRIPTION("Validate illegal index buffer values when descriptor heap is bound before the pipeline");
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
InitRenderTarget();
const VkDeviceSize descriptor_size = AlignResource(heap_props.bufferDescriptorAlignment * 4u);
const VkDeviceSize heap_size = descriptor_size + heap_props.minResourceHeapReservedRange;
vkt::Buffer heap(*m_device, heap_size, VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, vkt::device_address);
const char* vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
void main() {
gl_Position = vec4(pos, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
CreatePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
pipe.gp_ci_.layout = VK_NULL_HANDLE;
VkVertexInputBindingDescription input_binding = {0, 3 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
VkVertexInputAttributeDescription input_attrib = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
pipe.vi_ci_.pVertexAttributeDescriptions = &input_attrib;
pipe.vi_ci_.vertexAttributeDescriptionCount = 1;
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.gp_ci_.stageCount = pipe.shader_stages_.size();
pipe.gp_ci_.pStages = pipe.shader_stages_.data();
pipe.CreateGraphicsPipeline(false);
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 3;
draw_params.instanceCount = 1;
draw_params.firstIndex = 0;
draw_params.vertexOffset = 0;
draw_params.firstInstance = 0;
vkt::Buffer draw_params_buffer = vkt::IndirectBuffer<VkDrawIndexedIndirectCommand>(*m_device, {draw_params});
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange.address = heap.Address();
bind_resource_info.heapRange.size = heap_size;
bind_resource_info.reservedRangeOffset = descriptor_size;
bind_resource_info.reservedRangeSize = heap_props.minResourceHeapReservedRange;
vk::CmdBindResourceHeapEXT(m_command_buffer, &bind_resource_info);
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, 666, 42});
vkt::Buffer vertex_buffer = vkt::VertexBuffer<float>(*m_device, {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
VkDeviceSize vertex_buffer_offset = 0;
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 666");
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 42");
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, sizeof(VkDrawIndexedIndirectCommand));
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeGpuAVDescriptorHeap, HeapBoundAfterPipeline) {
TEST_DESCRIPTION("Validate illegal index buffer values when descriptor heap is bound after pipeline");
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
InitRenderTarget();
const VkDeviceSize descriptor_size = AlignResource(heap_props.bufferDescriptorAlignment * 4u);
const VkDeviceSize heap_size = descriptor_size + heap_props.minResourceHeapReservedRange;
vkt::Buffer heap(*m_device, heap_size, VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, vkt::device_address);
const char* vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
void main() {
gl_Position = vec4(pos, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
CreatePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
pipe.gp_ci_.layout = VK_NULL_HANDLE;
VkVertexInputBindingDescription input_binding = {0, 3 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
VkVertexInputAttributeDescription input_attrib = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
pipe.vi_ci_.pVertexAttributeDescriptions = &input_attrib;
pipe.vi_ci_.vertexAttributeDescriptionCount = 1;
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.gp_ci_.stageCount = pipe.shader_stages_.size();
pipe.gp_ci_.pStages = pipe.shader_stages_.data();
pipe.CreateGraphicsPipeline(false);
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 3;
draw_params.instanceCount = 1;
draw_params.firstIndex = 0;
draw_params.vertexOffset = 0;
draw_params.firstInstance = 0;
vkt::Buffer draw_params_buffer = vkt::IndirectBuffer<VkDrawIndexedIndirectCommand>(*m_device, {draw_params});
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, 666, 42});
vkt::Buffer vertex_buffer = vkt::VertexBuffer<float>(*m_device, {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
VkDeviceSize vertex_buffer_offset = 0;
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange.address = heap.Address();
bind_resource_info.heapRange.size = heap_size;
bind_resource_info.reservedRangeOffset = descriptor_size;
bind_resource_info.reservedRangeSize = heap_props.minResourceHeapReservedRange;
vk::CmdBindResourceHeapEXT(m_command_buffer, &bind_resource_info);
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 666");
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 42");
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, sizeof(VkDrawIndexedIndirectCommand));
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeGpuAVDescriptorHeap, SamplerHeapBound) {
TEST_DESCRIPTION("Validate illegal index buffer values when sampler heap is bound");
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
InitRenderTarget();
const VkDeviceSize descriptor_size = AlignSampler(heap_props.samplerDescriptorAlignment * 4u);
const VkDeviceSize heap_size = descriptor_size + heap_props.minSamplerHeapReservedRange;
vkt::Buffer heap(*m_device, heap_size, VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, vkt::device_address);
const char* vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
void main() {
gl_Position = vec4(pos, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
CreatePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
pipe.gp_ci_.layout = VK_NULL_HANDLE;
VkVertexInputBindingDescription input_binding = {0, 3 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
VkVertexInputAttributeDescription input_attrib = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
pipe.vi_ci_.pVertexAttributeDescriptions = &input_attrib;
pipe.vi_ci_.vertexAttributeDescriptionCount = 1;
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.gp_ci_.stageCount = pipe.shader_stages_.size();
pipe.gp_ci_.pStages = pipe.shader_stages_.data();
pipe.CreateGraphicsPipeline(false);
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 3;
draw_params.instanceCount = 1;
draw_params.firstIndex = 0;
draw_params.vertexOffset = 0;
draw_params.firstInstance = 0;
vkt::Buffer draw_params_buffer = vkt::IndirectBuffer<VkDrawIndexedIndirectCommand>(*m_device, {draw_params});
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, 666, 42});
vkt::Buffer vertex_buffer = vkt::VertexBuffer<float>(*m_device, {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
VkDeviceSize vertex_buffer_offset = 0;
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange = heap.AddressRange();
bind_resource_info.reservedRangeOffset = descriptor_size;
bind_resource_info.reservedRangeSize = heap_props.minSamplerHeapReservedRange;
vk::CmdBindSamplerHeapEXT(m_command_buffer, &bind_resource_info);
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 666");
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 42");
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, sizeof(VkDrawIndexedIndirectCommand));
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeGpuAVDescriptorHeap, MappingsUsed) {
TEST_DESCRIPTION("Validate illegal index buffer values when custom mappings are used");
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::vertexPipelineStoresAndAtomics);
RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
InitRenderTarget();
const VkDeviceSize descriptor_size = AlignResource(heap_props.bufferDescriptorAlignment * 4u);
const VkDeviceSize heap_size = descriptor_size + heap_props.minResourceHeapReservedRange;
vkt::Buffer heap(*m_device, heap_size, VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, vkt::device_address);
uint8_t* heap_data = static_cast<uint8_t*>(heap.Memory().Map());
vkt::Buffer buffer1(*m_device, 256u, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vkt::device_address);
vkt::Buffer buffer2(*m_device, 256u, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vkt::device_address);
vkt::Buffer buffer3(*m_device, 256u, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vkt::device_address);
VkDeviceAddressRangeEXT address_ranges[3];
address_ranges[0] = buffer1.AddressRange();
address_ranges[1] = buffer2.AddressRange();
address_ranges[2] = buffer3.AddressRange();
VkResourceDescriptorInfoEXT resource_infos[3];
resource_infos[0] = vku::InitStructHelper();
resource_infos[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
resource_infos[0].data.pAddressRange = &address_ranges[0];
resource_infos[1] = vku::InitStructHelper();
resource_infos[1].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
resource_infos[1].data.pAddressRange = &address_ranges[1];
resource_infos[2] = vku::InitStructHelper();
resource_infos[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
resource_infos[2].data.pAddressRange = &address_ranges[2];
VkHostAddressRangeEXT host_addresses[3];
host_addresses[0].address = heap_data;
host_addresses[0].size = static_cast<size_t>(heap_props.bufferDescriptorSize);
host_addresses[1].address = heap_data + heap_props.bufferDescriptorAlignment;
host_addresses[1].size = static_cast<size_t>(heap_props.bufferDescriptorSize);
host_addresses[2].address = heap_data + heap_props.bufferDescriptorAlignment * 2u;
host_addresses[2].size = static_cast<size_t>(heap_props.bufferDescriptorSize);
vk::WriteResourceDescriptorsEXT(*m_device, 3u, resource_infos, host_addresses);
const char* vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
layout(set=0, binding=0) buffer Buf1 { uint data1[]; } buf1;
layout(set=0, binding=1) readonly buffer Buf2 { uint data2[]; } buf2;
layout(set=1, binding=2) buffer Buf3 { uint data3[]; } buf3;
void main() {
buf3.data3[gl_VertexIndex] = buf1.data1[gl_VertexIndex] + buf2.data2[gl_VertexIndex];
gl_Position = vec4(pos, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
VkDescriptorSetAndBindingMappingEXT mappings[3];
mappings[0] = vku::InitStructHelper();
mappings[0].descriptorSet = 0u;
mappings[0].firstBinding = 0u;
mappings[0].bindingCount = 1u;
mappings[0].resourceMask = VK_SPIRV_RESOURCE_TYPE_READ_WRITE_STORAGE_BUFFER_BIT_EXT;
mappings[0].source = VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT;
mappings[0].sourceData.constantOffset.heapOffset = 0;
mappings[1] = vku::InitStructHelper();
mappings[1].descriptorSet = 0u;
mappings[1].firstBinding = 1u;
mappings[1].bindingCount = 1u;
mappings[1].resourceMask = VK_SPIRV_RESOURCE_TYPE_READ_ONLY_STORAGE_BUFFER_BIT_EXT;
mappings[1].source = VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT;
mappings[1].sourceData.constantOffset.heapOffset = static_cast<uint32_t>(heap_props.bufferDescriptorAlignment);
mappings[2] = vku::InitStructHelper();
mappings[2].descriptorSet = 1u;
mappings[2].firstBinding = 2u;
mappings[2].bindingCount = 1u;
mappings[2].resourceMask = VK_SPIRV_RESOURCE_TYPE_READ_WRITE_STORAGE_BUFFER_BIT_EXT;
mappings[2].source = VK_DESCRIPTOR_MAPPING_SOURCE_HEAP_WITH_CONSTANT_OFFSET_EXT;
mappings[2].sourceData.constantOffset.heapOffset = static_cast<uint32_t>(heap_props.bufferDescriptorAlignment * 2u);
VkShaderDescriptorSetAndBindingMappingInfoEXT mapping_info = vku::InitStructHelper();
mapping_info.mappingCount = 3u;
mapping_info.pMappings = mappings;
CreatePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
pipe.gp_ci_.layout = VK_NULL_HANDLE;
VkVertexInputBindingDescription input_binding = {0, 3 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
VkVertexInputAttributeDescription input_attrib = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
pipe.vi_ci_.pVertexAttributeDescriptions = &input_attrib;
pipe.vi_ci_.vertexAttributeDescriptionCount = 1;
pipe.shader_stages_ = {vs.GetStageCreateInfo(&mapping_info), pipe.fs_->GetStageCreateInfo(&mapping_info)};
pipe.gp_ci_.stageCount = pipe.shader_stages_.size();
pipe.gp_ci_.pStages = pipe.shader_stages_.data();
pipe.CreateGraphicsPipeline(false);
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 3;
draw_params.instanceCount = 1;
draw_params.firstIndex = 0;
draw_params.vertexOffset = 0;
draw_params.firstInstance = 0;
vkt::Buffer draw_params_buffer = vkt::IndirectBuffer<VkDrawIndexedIndirectCommand>(*m_device, {draw_params});
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange.address = heap.Address();
bind_resource_info.heapRange.size = heap_size;
bind_resource_info.reservedRangeOffset = descriptor_size;
bind_resource_info.reservedRangeSize = heap_props.minResourceHeapReservedRange;
vk::CmdBindResourceHeapEXT(m_command_buffer, &bind_resource_info);
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, 666, 42});
vkt::Buffer vertex_buffer = vkt::VertexBuffer<float>(*m_device, {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
VkDeviceSize vertex_buffer_offset = 0;
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 666");
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 42");
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, sizeof(VkDrawIndexedIndirectCommand));
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeGpuAVDescriptorHeap, DispatchWorkgroupSize) {
TEST_DESCRIPTION("GPU validation: Validate VkDispatchIndirectCommand with descriptor heap");
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::descriptorHeap);
RETURN_IF_SKIP(InitGpuAvFramework());
PFN_vkSetPhysicalDeviceLimitsEXT fpvkSetPhysicalDeviceLimitsEXT = nullptr;
PFN_vkGetOriginalPhysicalDeviceLimitsEXT fpvkGetOriginalPhysicalDeviceLimitsEXT = nullptr;
if (!LoadDeviceProfileLayer(fpvkSetPhysicalDeviceLimitsEXT, fpvkGetOriginalPhysicalDeviceLimitsEXT)) {
GTEST_SKIP() << "Failed to load device profile layer.";
}
VkPhysicalDeviceProperties props;
fpvkGetOriginalPhysicalDeviceLimitsEXT(Gpu(), &props.limits);
props.limits.maxComputeWorkGroupCount[0] = 2;
props.limits.maxComputeWorkGroupCount[1] = 2;
props.limits.maxComputeWorkGroupCount[2] = 2;
fpvkSetPhysicalDeviceLimitsEXT(Gpu(), &props.limits);
RETURN_IF_SKIP(InitState());
GetPhysicalDeviceProperties2(heap_props);
vkt::Buffer indirect_buffer(*m_device, 5 * sizeof(VkDispatchIndirectCommand), VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
kHostVisibleMemProps);
VkDispatchIndirectCommand* ptr = static_cast<VkDispatchIndirectCommand*>(indirect_buffer.Memory().Map());
// VkDispatchIndirectCommand[0]
ptr->x = 4; // over
ptr->y = 2;
ptr->z = 1;
// VkDispatchIndirectCommand[1]
ptr++;
ptr->x = 2;
ptr->y = 3; // over
ptr->z = 1;
// VkDispatchIndirectCommand[2] - valid in between
ptr++;
ptr->x = 1;
ptr->y = 1;
ptr->z = 1;
// VkDispatchIndirectCommand[3]
ptr++;
ptr->x = 0; // allowed
ptr->y = 2;
ptr->z = 3; // over
// VkDispatchIndirectCommand[4]
ptr++;
ptr->x = 3; // over
ptr->y = 2;
ptr->z = 3; // over
VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
CreateComputePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
pipe.cp_ci_.layout = VK_NULL_HANDLE;
pipe.cp_ci_.stage = pipe.cs_.GetStageCreateInfo();
pipe.CreateComputePipeline(false);
m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
m_errorMonitor->SetDesiredError("VUID-VkDispatchIndirectCommand-x-00417");
vk::CmdDispatchIndirect(m_command_buffer, indirect_buffer, 0);
m_errorMonitor->SetDesiredError("VUID-VkDispatchIndirectCommand-y-00418");
vk::CmdDispatchIndirect(m_command_buffer, indirect_buffer, sizeof(VkDispatchIndirectCommand));
// valid
vk::CmdDispatchIndirect(m_command_buffer, indirect_buffer, 2 * sizeof(VkDispatchIndirectCommand));
m_errorMonitor->SetDesiredError("VUID-VkDispatchIndirectCommand-z-00419");
vk::CmdDispatchIndirect(m_command_buffer, indirect_buffer, 3 * sizeof(VkDispatchIndirectCommand));
// Only expect to have the first error return
m_errorMonitor->SetDesiredError("VUID-VkDispatchIndirectCommand-x-00417");
vk::CmdDispatchIndirect(m_command_buffer, indirect_buffer, 4 * sizeof(VkDispatchIndirectCommand));
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
// Check again in a 2nd submitted command buffer
m_command_buffer.Reset();
m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
m_errorMonitor->SetDesiredError("VUID-VkDispatchIndirectCommand-x-00417");
vk::CmdDispatchIndirect(m_command_buffer, indirect_buffer, 0);
vk::CmdDispatchIndirect(m_command_buffer, indirect_buffer, 2 * sizeof(VkDispatchIndirectCommand));
m_errorMonitor->SetDesiredError("VUID-VkDispatchIndirectCommand-x-00417");
vk::CmdDispatchIndirect(m_command_buffer, indirect_buffer, 0);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeGpuAVDescriptorHeap, HeapRebound) {
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
InitRenderTarget();
const VkDeviceSize descriptor_size = AlignResource(heap_props.bufferDescriptorAlignment * 4u);
const VkDeviceSize heap_size = descriptor_size + heap_props.minResourceHeapReservedRange;
vkt::Buffer heap1(*m_device, heap_size, VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, vkt::device_address);
vkt::Buffer heap2(*m_device, heap_size, VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, vkt::device_address);
const char* vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
void main() {
gl_Position = vec4(pos, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
VkPipelineCreateFlags2CreateInfoKHR pipeline_create_flags_2_create_info = vku::InitStructHelper();
pipeline_create_flags_2_create_info.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
CreatePipelineHelper pipe(*this, &pipeline_create_flags_2_create_info);
pipe.gp_ci_.layout = VK_NULL_HANDLE;
VkVertexInputBindingDescription input_binding = {0, 3 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
VkVertexInputAttributeDescription input_attrib = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
pipe.vi_ci_.pVertexAttributeDescriptions = &input_attrib;
pipe.vi_ci_.vertexAttributeDescriptionCount = 1;
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.gp_ci_.stageCount = pipe.shader_stages_.size();
pipe.gp_ci_.pStages = pipe.shader_stages_.data();
pipe.CreateGraphicsPipeline(false);
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 3;
draw_params.instanceCount = 1;
draw_params.firstIndex = 0;
draw_params.vertexOffset = 0;
draw_params.firstInstance = 0;
vkt::Buffer draw_params_buffer = vkt::IndirectBuffer<VkDrawIndexedIndirectCommand>(*m_device, {draw_params});
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange.address = heap1.Address();
bind_resource_info.heapRange.size = heap_size;
bind_resource_info.reservedRangeOffset = descriptor_size;
bind_resource_info.reservedRangeSize = heap_props.minResourceHeapReservedRange;
vk::CmdBindResourceHeapEXT(m_command_buffer, &bind_resource_info);
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, 666, 42});
vkt::Buffer vertex_buffer = vkt::VertexBuffer<float>(*m_device, {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
VkDeviceSize vertex_buffer_offset = 0;
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 666");
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 42");
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, sizeof(VkDrawIndexedIndirectCommand));
bind_resource_info.heapRange.address = heap2.Address();
vk::CmdBindResourceHeapEXT(m_command_buffer, &bind_resource_info);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(NegativeGpuAVDescriptorHeap, ShaderObjects) {
TEST_DESCRIPTION("Validate illegal index buffer values with shader objects");
AddRequiredExtensions(VK_EXT_SHADER_OBJECT_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::shaderObject);
AddRequiredFeature(vkt::Feature::dynamicRendering);
RETURN_IF_SKIP(InitGpuAVDescriptorHeap());
InitDynamicRenderTarget();
const VkDeviceSize descriptor_size = AlignResource(heap_props.bufferDescriptorAlignment * 4u);
const VkDeviceSize heap_size = descriptor_size + heap_props.minResourceHeapReservedRange;
vkt::Buffer heap(*m_device, heap_size, VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, vkt::device_address);
const char* vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
void main() {
gl_Position = vec4(pos, 1.0);
}
)glsl";
const auto vspv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, vsSource);
VkShaderCreateInfoEXT vert_ci = vku::InitStructHelper();
vert_ci.flags = VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT;
vert_ci.stage = VK_SHADER_STAGE_VERTEX_BIT;
vert_ci.nextStage = VK_SHADER_STAGE_FRAGMENT_BIT;
vert_ci.codeType = VK_SHADER_CODE_TYPE_SPIRV_EXT;
vert_ci.codeSize = vspv.size() * sizeof(vspv[0]);
vert_ci.pCode = vspv.data();
vert_ci.pName = "main";
const auto fspv = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, kFragmentMinimalGlsl);
VkShaderCreateInfoEXT frag_ci = vku::InitStructHelper();
frag_ci.flags = VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT;
frag_ci.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
frag_ci.codeType = VK_SHADER_CODE_TYPE_SPIRV_EXT;
frag_ci.codeSize = fspv.size() * sizeof(fspv[0]);
frag_ci.pCode = fspv.data();
frag_ci.pName = "main";
const vkt::Shader vert_shader(*m_device, vert_ci);
const vkt::Shader frag_shader(*m_device, frag_ci);
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 3;
draw_params.instanceCount = 1;
draw_params.firstIndex = 0;
draw_params.vertexOffset = 0;
draw_params.firstInstance = 0;
vkt::Buffer draw_params_buffer = vkt::IndirectBuffer<VkDrawIndexedIndirectCommand>(*m_device, {draw_params});
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
VkBindHeapInfoEXT bind_resource_info = vku::InitStructHelper();
bind_resource_info.heapRange.address = heap.Address();
bind_resource_info.heapRange.size = heap_size;
bind_resource_info.reservedRangeOffset = descriptor_size;
bind_resource_info.reservedRangeSize = heap_props.minResourceHeapReservedRange;
vk::CmdBindResourceHeapEXT(m_command_buffer, &bind_resource_info);
m_command_buffer.BeginRenderingColor(GetDynamicRenderTarget(), GetRenderTargetArea());
VkShaderStageFlagBits stages[2] = {VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT};
VkShaderEXT shaders[2] = {vert_shader.handle(), frag_shader.handle()};
vk::CmdBindShadersEXT(m_command_buffer, 2, stages, shaders);
vk::CmdSetRasterizerDiscardEnableEXT(m_command_buffer, VK_FALSE);
vk::CmdSetCullModeEXT(m_command_buffer, VK_CULL_MODE_NONE);
vk::CmdSetDepthTestEnableEXT(m_command_buffer, VK_FALSE);
vk::CmdSetStencilTestEnableEXT(m_command_buffer, VK_FALSE);
vk::CmdSetPolygonModeEXT(m_command_buffer, VK_POLYGON_MODE_FILL);
vk::CmdSetPrimitiveTopology(m_command_buffer, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
vk::CmdSetPrimitiveRestartEnableEXT(m_command_buffer, VK_FALSE);
VkColorBlendEquationEXT colorBlendEquation = {
VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ONE, VK_BLEND_OP_ADD, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ONE, VK_BLEND_OP_ADD,
};
vk::CmdSetColorBlendEquationEXT(m_command_buffer, 0, 1u, &colorBlendEquation);
VkViewport viewport = {0.0f, 0.0f, 32.0f, 32.0f, 0.0f, 1.0f};
vk::CmdSetViewportWithCountEXT(m_command_buffer, 1, &viewport);
VkRect2D scissor = {{0, 0}, {32, 32}};
vk::CmdSetScissorWithCountEXT(m_command_buffer, 1, &scissor);
vk::CmdSetDepthBiasEnableEXT(m_command_buffer, VK_FALSE);
vk::CmdSetRasterizationSamplesEXT(m_command_buffer, VK_SAMPLE_COUNT_1_BIT);
VkSampleMask sampleMask = 0xFFFFFFFF;
vk::CmdSetSampleMaskEXT(m_command_buffer, VK_SAMPLE_COUNT_1_BIT, &sampleMask);
vk::CmdSetAlphaToCoverageEnableEXT(m_command_buffer, VK_FALSE);
VkBool32 colorBlendEnable = VK_FALSE;
vk::CmdSetColorBlendEnableEXT(m_command_buffer, 0u, 1u, &colorBlendEnable);
VkColorComponentFlags colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
vk::CmdSetColorWriteMaskEXT(m_command_buffer, 0u, 1u, &colorWriteMask);
VkVertexInputBindingDescription2EXT binding = vku::InitStructHelper();
binding.binding = 0u;
binding.stride = 3 * sizeof(float);
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
binding.divisor = 1u;
VkVertexInputAttributeDescription2EXT attribute = vku::InitStructHelper();
attribute.location = 0u;
attribute.binding = 0u;
attribute.format = VK_FORMAT_R32G32B32_SFLOAT;
attribute.offset = 0u;
vk::CmdSetVertexInputEXT(m_command_buffer, 1u, &binding, 1u, &attribute);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, 666, 42});
vkt::Buffer vertex_buffer = vkt::VertexBuffer<float>(*m_device, {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
VkDeviceSize vertex_buffer_offset = 0;
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 666");
m_errorMonitor->SetDesiredErrorRegex("VUID-vkCmdDrawIndexedIndirect-None-02721", "Vertex index 42");
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, sizeof(VkDrawIndexedIndirectCommand));
vk::CmdBindResourceHeapEXT(m_command_buffer, &bind_resource_info);
m_command_buffer.EndRendering();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}