blob: 54047ed1a75bfc1bd0e2308020da7f29df7791e6 [file] [log] [blame]
/*
* Copyright (c) 2020-2025 The Khronos Group Inc.
* Copyright (c) 2020-2025 Valve Corporation
* Copyright (c) 2020-2025 LunarG, Inc.
* Copyright (c) 2020-2025 Google, 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 "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
#include "../framework/buffer_helper.h"
class PositiveGpuAVIndexBuffer : public GpuAVTest {};
TEST_F(PositiveGpuAVIndexBuffer, BadVertexIndex) {
TEST_DESCRIPTION("If no vertex buffer is used, all index values are legal");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
CreatePipelineHelper pipe(*this);
pipe.CreateGraphicsPipeline();
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, vvl::kU32Max, 42});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVIndexBuffer, VertexIndex) {
TEST_DESCRIPTION("Validate index buffer values");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
CreatePipelineHelper pipe(*this);
pipe.CreateGraphicsPipeline();
constexpr uint32_t num_vertices = 12;
std::vector<uint32_t> indicies(num_vertices);
for (uint32_t i = 0; i < num_vertices; i++) {
indicies[i] = num_vertices - 1 - i;
}
vkt::Buffer index_buffer = vkt::IndexBuffer(*m_device, std::move(indicies));
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);
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);
}
TEST_F(PositiveGpuAVIndexBuffer, DrawIndexedDynamicStates) {
TEST_DESCRIPTION("vkCmdDrawIndexed - Set dynamic states");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
RETURN_IF_SKIP(InitGpuAvFramework());
AddRequiredFeature(vkt::Feature::extendedDynamicState);
AddRequiredFeature(vkt::Feature::extendedDynamicState2);
AddRequiredFeature(vkt::Feature::extendedDynamicState3PolygonMode);
// AddRequiredFeature(vkt::Feature::depthBiasClamp);
RETURN_IF_SKIP(InitState());
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);
CreatePipelineHelper pipe(*this);
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.AddDynamicState(VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE);
pipe.AddDynamicState(VK_DYNAMIC_STATE_CULL_MODE);
pipe.AddDynamicState(VK_DYNAMIC_STATE_FRONT_FACE);
pipe.AddDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE);
pipe.AddDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS);
pipe.AddDynamicState(VK_DYNAMIC_STATE_LINE_WIDTH);
pipe.AddDynamicState(VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE);
pipe.AddDynamicState(VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY);
pipe.CreateGraphicsPipeline();
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, 1, 2});
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;
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);
vk::CmdSetRasterizerDiscardEnableEXT(m_command_buffer, VK_FALSE);
vk::CmdSetCullModeEXT(m_command_buffer, VK_CULL_MODE_NONE);
vk::CmdSetFrontFaceEXT(m_command_buffer, VK_FRONT_FACE_CLOCKWISE);
vk::CmdSetDepthBiasEnableEXT(m_command_buffer, VK_TRUE);
vk::CmdSetDepthBias(m_command_buffer, 0.0f, 0.0f, 1.0f);
vk::CmdSetLineWidth(m_command_buffer, 1.0f);
vk::CmdSetPrimitiveRestartEnableEXT(m_command_buffer, VK_FALSE);
vk::CmdSetPrimitiveTopologyEXT(m_command_buffer, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
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);
vk::CmdDrawIndexed(m_command_buffer, 3, 1, 0, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(PositiveGpuAVIndexBuffer, IndexedIndirectRobustness) {
AddRequiredExtensions(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
RETURN_IF_SKIP(InitGpuAvFramework());
AddRequiredFeature(vkt::Feature::robustBufferAccess);
AddRequiredFeature(vkt::Feature::robustBufferAccess2);
RETURN_IF_SKIP(InitState());
InitRenderTarget();
CreatePipelineHelper pipe(*this);
pipe.CreateGraphicsPipeline();
VkDrawIndexedIndirectCommand draw_params{};
draw_params.indexCount = 4;
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, vvl::kU32Max, 42});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVIndexBuffer, NoShaderInputsVertexIndex16) {
TEST_DESCRIPTION("Vertex shader defines no vertex attributes - no OOB vertex fetch should be detected");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
struct Vertex {
std::array<float, 3> position;
std::array<float, 2> uv;
std::array<float, 3> normal;
};
const char *vsSource = R"glsl(
#version 450
void main() {
gl_Position = vec4(1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
CreatePipelineHelper pipe(*this);
// "Array of structs" style vertices
VkVertexInputBindingDescription input_binding = {0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX};
std::array<VkVertexInputAttributeDescription, 3> vertex_attributes = {};
// Position
vertex_attributes[0] = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
// UV
vertex_attributes[1] = {1, 0, VK_FORMAT_R32G32_SFLOAT, 3 * sizeof(float)};
// Normal
vertex_attributes[2] = {2, 0, VK_FORMAT_R32G32B32_SFLOAT, (3 + 2) * sizeof(float)};
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
pipe.vi_ci_.pVertexAttributeDescriptions = vertex_attributes.data();
pipe.vi_ci_.vertexAttributeDescriptionCount = size32(vertex_attributes);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
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);
std::vector<Vertex> vertices;
for (int i = 0; i < 3; ++i) {
const Vertex vertex = {{0.0f, 1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f, 7.0f}};
vertices.emplace_back(vertex);
}
vkt::Buffer vertex_buffer = vkt::VertexBuffer<Vertex>(*m_device, vertices);
// Offset vertex buffer so that only first Vertex can correctly be fetched
VkDeviceSize vertex_buffer_offset = 2 * sizeof(Vertex);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint16_t>(*m_device, {0, 1, 0});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16);
vk::CmdDrawIndexed(m_command_buffer, 3, 1, 0, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(PositiveGpuAVIndexBuffer, VertexShaderUnusedLocations) {
TEST_DESCRIPTION(
"Vertex shader defines only a position vertex attribute - no OOB vertex fetch should be detected for uv and normal");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
const char *vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
// Uncommenting will cause OOB vertex attribute fetch
// layout(location=1) in vec2 uv;
void main() {
gl_Position = vec4(pos, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
CreatePipelineHelper pipe(*this);
// "Struct of arrays" style vertices
VkVertexInputBindingDescription position_input_binding_desc = {0, 3 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
VkVertexInputBindingDescription uv_input_binding_desc = {1, 2 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
VkVertexInputBindingDescription normal_input_binding_desc = {2, 3 * sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX};
std::array<VkVertexInputBindingDescription, 3> input_binding_descs = {
{position_input_binding_desc, uv_input_binding_desc, normal_input_binding_desc}};
std::array<VkVertexInputAttributeDescription, 3> vertex_attributes = {};
// Position
vertex_attributes[0] = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
// UV
vertex_attributes[1] = {1, 1, VK_FORMAT_R32G32_SFLOAT, 0};
// Normal
vertex_attributes[2] = {2, 2, VK_FORMAT_R32G32B32_SFLOAT, 0};
pipe.vi_ci_.pVertexBindingDescriptions = input_binding_descs.data();
pipe.vi_ci_.vertexBindingDescriptionCount = size32(input_binding_descs);
pipe.vi_ci_.pVertexAttributeDescriptions = vertex_attributes.data();
pipe.vi_ci_.vertexAttributeDescriptionCount = size32(vertex_attributes);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
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);
std::vector<float> positions;
for (int i = 0; i < 3 * 3; ++i) {
positions.emplace_back(float(i));
}
std::vector<float> uvs;
for (int i = 0; i < 2; ++i) {
uvs.emplace_back(float(i));
}
std::vector<float> normals;
for (int i = 0; i < 3; ++i) {
normals.emplace_back(float(i));
}
vkt::Buffer positions_buffer = vkt::VertexBuffer<float>(*m_device, positions);
vkt::Buffer uvs_buffer = vkt::VertexBuffer<float>(*m_device, uvs);
vkt::Buffer normals_buffer = vkt::VertexBuffer<float>(*m_device, normals);
// Only position buffer will not cause OOB vertex attribute fetching, uv/normal would - should be fine, vertex shader does not
// use those two.
std::array<VkBuffer, 3> vertex_buffers_handles = {{positions_buffer, uvs_buffer, normals_buffer}};
std::array<VkDeviceSize, 3> vertex_buffer_offsets = {{0, 0, 0}};
vk::CmdBindVertexBuffers(m_command_buffer, 0, size32(vertex_buffers_handles), vertex_buffers_handles.data(),
vertex_buffer_offsets.data());
vkt::Buffer index_buffer = vkt::IndexBuffer<uint16_t>(*m_device, {0, 1, 2});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16);
vk::CmdDrawIndexed(m_command_buffer, 3, 1, 0, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(PositiveGpuAVIndexBuffer, InstanceIndex) {
TEST_DESCRIPTION("No false positive for OOB instance index validation");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
struct Vertex {
std::array<float, 3> position;
std::array<float, 2> uv;
std::array<float, 3> normal;
};
const char *vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
layout(location=1) in vec2 uv;
layout(location=2) in vec3 normal;
layout(location=3) in float instance_float;
void main() {
gl_Position = vec4(pos + uv.xyx + normal + instance_float, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
CreatePipelineHelper pipe(*this);
// "Array of structs" style vertices
std::array<VkVertexInputBindingDescription, 2> input_bindings = {
{{0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX}, {1, sizeof(float), VK_VERTEX_INPUT_RATE_INSTANCE}}};
std::array<VkVertexInputAttributeDescription, 4> vertex_attributes = {};
// Position
vertex_attributes[0] = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
// UV
vertex_attributes[1] = {1, 0, VK_FORMAT_R32G32_SFLOAT, 3 * sizeof(float)};
// Normal
vertex_attributes[2] = {2, 0, VK_FORMAT_R32G32B32_SFLOAT, (3 + 2) * sizeof(float)};
// Instance float
vertex_attributes[3] = {3, 1, VK_FORMAT_R32_SFLOAT, 0};
pipe.vi_ci_.vertexBindingDescriptionCount = size32(input_bindings);
pipe.vi_ci_.pVertexBindingDescriptions = input_bindings.data();
pipe.vi_ci_.vertexAttributeDescriptionCount = size32(vertex_attributes);
pipe.vi_ci_.pVertexAttributeDescriptions = vertex_attributes.data();
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
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);
std::vector<Vertex> vertices;
for (int i = 0; i < 3; ++i) {
const Vertex vertex = {{0.0f, 1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f, 7.0f}};
vertices.emplace_back(vertex);
}
vkt::Buffer vertex_buffer = vkt::VertexBuffer<Vertex>(*m_device, vertices);
// Offset vertex buffer so that only first Vertex can correctly be fetched
const VkDeviceSize vertex_buffer_offset = 2 * sizeof(Vertex);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
std::vector<float> instance_data = {42.0f, 39.5f, 1233.0f};
vkt::Buffer instance_buffer = vkt::VertexBuffer<float>(*m_device, instance_data);
const VkDeviceSize instance_data_offset = 0;
vk::CmdBindVertexBuffers(m_command_buffer, 1, 1, &instance_buffer.handle(), &instance_data_offset);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint16_t>(*m_device, {0, 0, 0});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16);
vk::CmdDrawIndexed(m_command_buffer, 3, 3, 0, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVIndexBuffer, CmdSetVertexInputEXT) {
TEST_DESCRIPTION("Simple graphics pipeline, use vkCmdSetVertexInputEXT");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::vertexInputDynamicState);
AddRequiredFeature(vkt::Feature::extendedDynamicState);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
struct Vertex {
std::array<float, 3> position;
std::array<float, 2> uv;
std::array<float, 3> normal;
};
const char *vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
layout(location=1) in vec2 uv;
layout(location=2) in vec3 normal;
void main() {
gl_Position = vec4(pos + uv.xyx + normal, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
CreatePipelineHelper pipe(*this);
pipe.AddDynamicState(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
pipe.gp_ci_.pVertexInputState = nullptr;
// "Array of structs" style vertices
VkVertexInputBindingDescription2EXT input_binding = vku::InitStructHelper();
input_binding.binding = 0;
input_binding.stride = sizeof(Vertex);
input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
input_binding.divisor = 1;
std::array<VkVertexInputAttributeDescription2EXT, 3> vertex_attributes = {};
// Position
vertex_attributes[0] = vku::InitStructHelper();
vertex_attributes[0].location = 0;
vertex_attributes[0].binding = 0;
vertex_attributes[0].format = VK_FORMAT_R32G32B32_SFLOAT;
vertex_attributes[0].offset = 0;
// UV
vertex_attributes[1] = vku::InitStructHelper();
vertex_attributes[1].location = 1;
vertex_attributes[1].binding = 0;
vertex_attributes[1].format = VK_FORMAT_R32G32_SFLOAT;
vertex_attributes[1].offset = 3 * sizeof(float);
// Normal
vertex_attributes[2] = vku::InitStructHelper();
vertex_attributes[2].location = 2;
vertex_attributes[2].binding = 0;
vertex_attributes[2].format = VK_FORMAT_R32G32B32_SFLOAT;
vertex_attributes[2].offset = (3 + 2) * sizeof(float);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
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);
vk::CmdSetVertexInputEXT(m_command_buffer, 1, &input_binding, size32(vertex_attributes), vertex_attributes.data());
std::vector<Vertex> vertices;
for (int i = 0; i < 3; ++i) {
const Vertex vertex = {{0.0f, 1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f, 7.0f}};
vertices.emplace_back(vertex);
}
vkt::Buffer vertex_buffer = vkt::VertexBuffer<Vertex>(*m_device, vertices);
// Offset vertex buffer so that only first and second vertices can correctly be fetched
VkDeviceSize vertex_buffer_offset = 1 * sizeof(Vertex);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint16_t>(*m_device, {0, 1, 0});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16);
vk::CmdDrawIndexed(m_command_buffer, 3, 1, 0, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVIndexBuffer, CmdSetVertexInputEXT_CmdBindVertexBuffers2EXT) {
TEST_DESCRIPTION("Simple graphics pipeline, use vkCmdSetVertexInputEXT and vkCmdBindVertexBuffers2EXT");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::vertexInputDynamicState);
AddRequiredFeature(vkt::Feature::extendedDynamicState);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
struct Vertex {
std::array<float, 3> position;
std::array<float, 2> uv;
std::array<float, 3> normal;
};
const char *vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
layout(location=1) in vec2 uv;
layout(location=2) in vec3 normal;
void main() {
gl_Position = vec4(pos + uv.xyx + normal, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
CreatePipelineHelper pipe(*this);
pipe.AddDynamicState(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
pipe.AddDynamicState(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE);
pipe.gp_ci_.pVertexInputState = nullptr;
// "Array of structs" style vertices
VkVertexInputBindingDescription2EXT input_binding = vku::InitStructHelper();
input_binding.binding = 0;
input_binding.stride = sizeof(Vertex);
input_binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
input_binding.divisor = 1;
std::array<VkVertexInputAttributeDescription2EXT, 3> vertex_attributes = {};
// Position
vertex_attributes[0] = vku::InitStructHelper();
vertex_attributes[0].location = 0;
vertex_attributes[0].binding = 0;
vertex_attributes[0].format = VK_FORMAT_R32G32B32_SFLOAT;
vertex_attributes[0].offset = 0;
// UV
vertex_attributes[1] = vku::InitStructHelper();
vertex_attributes[1].location = 1;
vertex_attributes[1].binding = 0;
vertex_attributes[1].format = VK_FORMAT_R32G32_SFLOAT;
vertex_attributes[1].offset = 3 * sizeof(float);
// Normal
vertex_attributes[2] = vku::InitStructHelper();
vertex_attributes[2].location = 2;
vertex_attributes[2].binding = 0;
vertex_attributes[2].format = VK_FORMAT_R32G32B32_SFLOAT;
vertex_attributes[2].offset = (3 + 2) * sizeof(float);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
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);
vk::CmdSetVertexInputEXT(m_command_buffer, 1, &input_binding, size32(vertex_attributes), vertex_attributes.data());
std::vector<Vertex> vertices;
for (int i = 0; i < 3; ++i) {
const Vertex vertex = {{0.0f, 1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f, 7.0f}};
vertices.emplace_back(vertex);
}
vkt::Buffer vertex_buffer = vkt::VertexBuffer<Vertex>(*m_device, vertices);
// Offset vertex buffer so that only first and second vertices can correctly be fetched
const VkDeviceSize vertex_buffer_offset = 1 * sizeof(Vertex);
const VkDeviceSize vertex_buffer_size = 2 * sizeof(Vertex);
const VkDeviceSize vertex_stride = sizeof(Vertex);
vk::CmdBindVertexBuffers2EXT(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset, &vertex_buffer_size,
&vertex_stride);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint16_t>(*m_device, {0, 1, 0});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16);
vk::CmdDrawIndexed(m_command_buffer, 3, 1, 0, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVIndexBuffer, IndirectDrawBadVertexIndex32) {
TEST_DESCRIPTION("Do no Validate illegal index buffer values when robustness is on");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::robustBufferAccess);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
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);
CreatePipelineHelper pipe(*this);
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.CreateGraphicsPipeline();
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);
// The last 2 indices cause an OOB access in the vertex buffer, but if robustness is on,
// no validation is performed
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);
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);
}
TEST_F(PositiveGpuAVIndexBuffer, VertexIndex32MultiDraw) {
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_MULTI_DRAW_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::multiDraw);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
const char *vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
void main() {
gl_Position = vec4(pos, gl_VertexIndex);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
CreatePipelineHelper pipe(*this);
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.CreateGraphicsPipeline();
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, 1, 2});
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, 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);
VkMultiDrawIndexedInfoEXT multi_draw_info[2] = {};
multi_draw_info[0].firstIndex = 0;
multi_draw_info[0].indexCount = 3;
multi_draw_info[0].vertexOffset = 0;
multi_draw_info[1].firstIndex = 0;
multi_draw_info[1].indexCount = 3;
multi_draw_info[1].vertexOffset = 1;
vk::CmdDrawMultiIndexedEXT(m_command_buffer, 2, multi_draw_info, 1, 0, sizeof(VkMultiDrawIndexedInfoEXT), nullptr);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
m_errorMonitor->VerifyFound();
}
TEST_F(PositiveGpuAVIndexBuffer, InstanceIndexVertexAttributeDivisor) {
TEST_DESCRIPTION("Validate illegal instance index values, when using vertex attribute divisor");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::vertexAttributeInstanceRateDivisor);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
struct Vertex {
std::array<float, 3> position;
std::array<float, 2> uv;
std::array<float, 3> normal;
};
const char *vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
layout(location=1) in vec2 uv;
layout(location=2) in vec3 normal;
layout(location=3) in float instance_float;
void main() {
gl_Position = vec4(pos + uv.xyx + normal + instance_float, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
CreatePipelineHelper pipe(*this);
// "Array of structs" style vertices
std::array<VkVertexInputBindingDescription, 2> input_bindings = {
{{0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX}, {1, sizeof(float), VK_VERTEX_INPUT_RATE_INSTANCE}}};
std::array<VkVertexInputAttributeDescription, 4> vertex_attributes = {};
// Position
vertex_attributes[0] = {0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0};
// UV
vertex_attributes[1] = {1, 0, VK_FORMAT_R32G32_SFLOAT, 3 * sizeof(float)};
// Normal
vertex_attributes[2] = {2, 0, VK_FORMAT_R32G32B32_SFLOAT, (3 + 2) * sizeof(float)};
// Instance float
vertex_attributes[3] = {3, 1, VK_FORMAT_R32_SFLOAT, 0};
VkVertexInputBindingDivisorDescription vertex_binding_divisor;
vertex_binding_divisor.binding = 1u;
vertex_binding_divisor.divisor = 3u;
VkPipelineVertexInputDivisorStateCreateInfo vertex_input_divisor_state = vku::InitStructHelper();
vertex_input_divisor_state.vertexBindingDivisorCount = 1u;
vertex_input_divisor_state.pVertexBindingDivisors = &vertex_binding_divisor;
pipe.vi_ci_.pNext = &vertex_input_divisor_state;
pipe.vi_ci_.vertexBindingDescriptionCount = size32(input_bindings);
pipe.vi_ci_.pVertexBindingDescriptions = input_bindings.data();
pipe.vi_ci_.vertexAttributeDescriptionCount = size32(vertex_attributes);
pipe.vi_ci_.pVertexAttributeDescriptions = vertex_attributes.data();
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
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);
std::vector<Vertex> vertices;
for (int i = 0; i < 3; ++i) {
const Vertex vertex = {{0.0f, 1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f, 7.0f}};
vertices.emplace_back(vertex);
}
vkt::Buffer vertex_buffer = vkt::VertexBuffer<Vertex>(*m_device, vertices);
// Offset vertex buffer so that only first Vertex can correctly be fetched
const VkDeviceSize vertex_buffer_offset = 2 * sizeof(Vertex);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
std::vector<float> instance_data = {42.0f};
vkt::Buffer instance_buffer = vkt::VertexBuffer<float>(*m_device, instance_data);
const VkDeviceSize instance_data_offset = 0;
vk::CmdBindVertexBuffers(m_command_buffer, 1, 1, &instance_buffer.handle(), &instance_data_offset);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint16_t>(*m_device, {0, 0, 0});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16);
// gl_InstanceIndex is divided by 3 => effective instance index of 0, so no OOB
vk::CmdDrawIndexed(m_command_buffer, 3, 3, 0, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVIndexBuffer, InstanceIndexVertexAttributeDivisorDynamic) {
TEST_DESCRIPTION("Validate illegal instance index values, when using vertex attribute divisor. Vertex input state is dynamic");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::vertexAttributeInstanceRateDivisor);
AddRequiredExtensions(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::vertexInputDynamicState);
AddRequiredFeature(vkt::Feature::extendedDynamicState);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
struct Vertex {
std::array<float, 3> position;
std::array<float, 2> uv;
std::array<float, 3> normal;
};
const char *vsSource = R"glsl(
#version 450
layout(location=0) in vec3 pos;
layout(location=1) in vec2 uv;
layout(location=2) in vec3 normal;
layout(location=3) in float instance_float;
void main() {
gl_Position = vec4(pos + uv.xyx + normal + instance_float, 1.0);
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
CreatePipelineHelper pipe(*this);
// "Array of structs" style vertices
std::array<VkVertexInputBindingDescription2EXT, 2> input_bindings = {};
input_bindings[0] = vku::InitStructHelper();
input_bindings[0].binding = 0;
input_bindings[0].stride = sizeof(Vertex);
input_bindings[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
input_bindings[0].divisor = 1;
input_bindings[1] = vku::InitStructHelper();
input_bindings[1].binding = 1;
input_bindings[1].stride = sizeof(float);
input_bindings[1].inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
input_bindings[1].divisor = 3;
std::array<VkVertexInputAttributeDescription2EXT, 4> vertex_attributes = {};
// Position
vertex_attributes[0] = vku::InitStructHelper();
vertex_attributes[0].location = 0;
vertex_attributes[0].binding = 0;
vertex_attributes[0].format = VK_FORMAT_R32G32B32_SFLOAT;
vertex_attributes[0].offset = 0;
// UV
vertex_attributes[1] = vku::InitStructHelper();
vertex_attributes[1].location = 1;
vertex_attributes[1].binding = 0;
vertex_attributes[1].format = VK_FORMAT_R32G32_SFLOAT;
vertex_attributes[1].offset = 3 * sizeof(float);
// Normal
vertex_attributes[2] = vku::InitStructHelper();
vertex_attributes[2].location = 2;
vertex_attributes[2].binding = 0;
vertex_attributes[2].format = VK_FORMAT_R32G32B32_SFLOAT;
vertex_attributes[2].offset = (3 + 2) * sizeof(float);
// Instance float
vertex_attributes[3] = vku::InitStructHelper();
vertex_attributes[3].location = 3;
vertex_attributes[3].binding = 1;
vertex_attributes[3].format = VK_FORMAT_R32_SFLOAT;
vertex_attributes[3].offset = 0;
pipe.AddDynamicState(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
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);
vk::CmdSetVertexInputEXT(m_command_buffer, size32(input_bindings), input_bindings.data(), size32(vertex_attributes),
vertex_attributes.data());
std::vector<Vertex> vertices;
for (int i = 0; i < 3; ++i) {
const Vertex vertex = {{0.0f, 1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f, 7.0f}};
vertices.emplace_back(vertex);
}
vkt::Buffer vertex_buffer = vkt::VertexBuffer<Vertex>(*m_device, vertices);
// Offset vertex buffer so that only first Vertex can correctly be fetched
const VkDeviceSize vertex_buffer_offset = 2 * sizeof(Vertex);
vk::CmdBindVertexBuffers(m_command_buffer, 0, 1, &vertex_buffer.handle(), &vertex_buffer_offset);
std::vector<float> instance_data = {42.0f};
vkt::Buffer instance_buffer = vkt::VertexBuffer<float>(*m_device, instance_data);
const VkDeviceSize instance_data_offset = 0;
vk::CmdBindVertexBuffers(m_command_buffer, 1, 1, &instance_buffer.handle(), &instance_data_offset);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint16_t>(*m_device, {0, 0, 0});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT16);
// gl_InstanceIndex is divided by 3 => effective instance index of 0, so no OOB
vk::CmdDrawIndexed(m_command_buffer, 3, 3, 0, 0, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVIndexBuffer, DrawIndexedIndirectWithOffset) {
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
CreatePipelineHelper pipe(*this);
pipe.CreateGraphicsPipeline();
const uint32_t offset = sizeof(VkDrawIndexedIndirectCommand);
const uint32_t size = sizeof(VkDrawIndexedIndirectCommand) + offset;
vkt::Buffer draw_params_buffer(*m_device, size, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, kHostVisibleMemProps);
uint8_t *data = (uint8_t *)draw_params_buffer.Memory().Map();
memset(data, 255, size);
auto indirect_command = reinterpret_cast<VkDrawIndexedIndirectCommand *>(data + offset);
indirect_command->indexCount = 3u;
indirect_command->instanceCount = 1u;
indirect_command->firstIndex = 0u;
indirect_command->vertexOffset = 0;
indirect_command->firstInstance = 0u;
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, vvl::kU32Max, 42});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, offset, 1u, 0u);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVIndexBuffer, Ssbo) {
AddRequiredFeature(vkt::Feature::vertexPipelineStoresAndAtomics);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
const char *vsSource = R"glsl(
#version 450
layout(set=0, binding=0) buffer InData {
vec4 pos;
} in_data;
layout(set=1, binding=0) buffer OutData {
vec4 pos;
} out_data;
void main() {
gl_Position = vec4(in_data.pos);
out_data.pos = in_data.pos;
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
OneOffDescriptorIndexingSet descriptor_set_1(
m_device, {
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr, 0},
});
OneOffDescriptorIndexingSet descriptor_set_2(
m_device, {
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr, 0},
});
const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set_1.layout_, &descriptor_set_2.layout_});
vkt::Buffer in_buffer(*m_device, sizeof(float) * 4, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);
float *in_buffer_ptr = (float *)in_buffer.Memory().Map();
in_buffer_ptr[0] = 1.0f;
in_buffer_ptr[1] = 2.0f;
in_buffer_ptr[2] = 3.0f;
in_buffer_ptr[3] = 4.0f;
vkt::Buffer out_buffer(*m_device, sizeof(float) * 4, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);
descriptor_set_1.WriteDescriptorBufferInfo(0, in_buffer, 0u, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
descriptor_set_1.UpdateDescriptorSets();
descriptor_set_2.WriteDescriptorBufferInfo(0, out_buffer, 0u, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
descriptor_set_2.UpdateDescriptorSets();
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.layout = pipeline_layout;
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
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);
VkDescriptorSet descriptor_sets[] = {descriptor_set_1.set_, descriptor_set_2.set_};
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 2u, descriptor_sets,
0, nullptr);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, vvl::kU32Max, 42});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
float *out_buffer_ptr = (float *)out_buffer.Memory().Map();
for (uint32_t i = 0; i < 4; ++i) {
ASSERT_EQ(in_buffer_ptr[i], out_buffer_ptr[i]);
}
}
TEST_F(PositiveGpuAVIndexBuffer, SsboDescriptorBuffer) {
AddRequiredExtensions(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::vertexPipelineStoresAndAtomics);
AddRequiredFeature(vkt::Feature::descriptorBindingPartiallyBound);
AddRequiredFeature(vkt::Feature::descriptorBuffer);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer_properties = vku::InitStructHelper();
GetPhysicalDeviceProperties2(descriptor_buffer_properties);
if (descriptor_buffer_properties.maxResourceDescriptorBufferBindings < 2) {
GTEST_SKIP() << "maxResourceDescriptorBufferBindings is not 2";
}
const char *vsSource = R"glsl(
#version 450
layout(set=0, binding=0) buffer InData {
vec4 pos;
} in_data;
layout(set=1, binding=0) buffer OutData {
vec4 pos;
} out_data;
void main() {
gl_Position = vec4(in_data.pos);
out_data.pos = in_data.pos;
}
)glsl";
VkShaderObj vs(*m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT);
const VkDescriptorSetLayoutBinding binding = {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr};
const VkDescriptorBindingFlags ds_binding_flags = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT;
VkDescriptorSetLayoutBindingFlagsCreateInfo flags_create_info = vku::InitStructHelper();
flags_create_info.bindingCount = 1u;
flags_create_info.pBindingFlags = &ds_binding_flags;
VkDescriptorSetLayoutCreateInfo ds_layout_ci = vku::InitStructHelper(&flags_create_info);
ds_layout_ci.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
ds_layout_ci.bindingCount = 1u;
ds_layout_ci.pBindings = &binding;
vkt::DescriptorSetLayout ds_layout(*m_device, ds_layout_ci);
const vkt::PipelineLayout pipeline_layout(*m_device, {&ds_layout, &ds_layout});
vkt::Buffer in_buffer(*m_device, sizeof(float) * 4, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vkt::device_address);
float *in_buffer_ptr = (float *)in_buffer.Memory().Map();
in_buffer_ptr[0] = 1.0f;
in_buffer_ptr[1] = 2.0f;
in_buffer_ptr[2] = 3.0f;
in_buffer_ptr[3] = 4.0f;
vkt::Buffer out_buffer(*m_device, sizeof(float) * 4, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vkt::device_address);
CreatePipelineHelper pipe(*this);
pipe.gp_ci_.flags |= VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
pipe.gp_ci_.layout = pipeline_layout;
pipe.shader_stages_ = {vs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
pipe.CreateGraphicsPipeline();
vkt::Buffer in_descriptor_buffer(*m_device, 4096, VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT, vkt::device_address);
vkt::Buffer out_descriptor_buffer(*m_device, 4096, VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT, vkt::device_address);
uint8_t *in_descriptor_data = reinterpret_cast<uint8_t *>(in_descriptor_buffer.Memory().Map());
uint8_t *out_descriptor_data = reinterpret_cast<uint8_t *>(out_descriptor_buffer.Memory().Map());
VkDeviceSize in_buffer_offset = ds_layout.GetDescriptorBufferBindingOffset(0);
VkDeviceSize out_buffer_offset = ds_layout.GetDescriptorBufferBindingOffset(0);
vkt::DescriptorGetInfo in_buffer_get_info(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, in_buffer, in_buffer.CreateInfo().size);
vk::GetDescriptorEXT(*m_device, in_buffer_get_info, descriptor_buffer_properties.storageBufferDescriptorSize,
in_descriptor_data + in_buffer_offset);
vkt::DescriptorGetInfo out_buffer_get_info(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, out_buffer, out_buffer.CreateInfo().size);
vk::GetDescriptorEXT(*m_device, out_buffer_get_info, descriptor_buffer_properties.storageBufferDescriptorSize,
out_descriptor_data + out_buffer_offset);
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});
VkDescriptorBufferBindingInfoEXT buffer_binding_infos[2];
buffer_binding_infos[0] = vku::InitStructHelper();
buffer_binding_infos[0].address = in_descriptor_buffer.Address();
buffer_binding_infos[0].usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;
buffer_binding_infos[1] = vku::InitStructHelper();
buffer_binding_infos[1].address = out_descriptor_buffer.Address();
buffer_binding_infos[1].usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;
VkCommandBufferBeginInfo begin_info = vku::InitStructHelper();
m_command_buffer.Begin(&begin_info);
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindDescriptorBuffersEXT(m_command_buffer, 2u, buffer_binding_infos);
uint32_t buffer_indices[2] = {0u, 1u};
VkDeviceSize offsets[2] = {0u, 0u};
vk::CmdSetDescriptorBufferOffsetsEXT(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0u, 2u, buffer_indices,
offsets);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vkt::Buffer index_buffer = vkt::IndexBuffer<uint32_t>(*m_device, {0, vvl::kU32Max, 42});
vk::CmdBindIndexBuffer(m_command_buffer, index_buffer, 0, VK_INDEX_TYPE_UINT32);
vk::CmdDrawIndexedIndirect(m_command_buffer, draw_params_buffer, 0, 1, 0);
m_command_buffer.EndRenderPass();
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
float *out_buffer_ptr = (float *)out_buffer.Memory().Map();
for (uint32_t i = 0; i < 4; ++i) {
ASSERT_EQ(in_buffer_ptr[i], out_buffer_ptr[i]);
}
}