| /* |
| * 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]); |
| } |
| } |