| /* |
| * Copyright (c) 2015-2019 The Khronos Group Inc. |
| * Copyright (c) 2015-2019 Valve Corporation |
| * Copyright (c) 2015-2019 LunarG, Inc. |
| * Copyright (c) 2015-2019 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 |
| * |
| * Author: Chia-I Wu <olvaffe@gmail.com> |
| * Author: Chris Forbes <chrisf@ijw.co.nz> |
| * Author: Courtney Goeltzenleuchter <courtney@LunarG.com> |
| * Author: Mark Lobodzinski <mark@lunarg.com> |
| * Author: Mike Stroyan <mike@LunarG.com> |
| * Author: Tobin Ehlis <tobine@google.com> |
| * Author: Tony Barbour <tony@LunarG.com> |
| * Author: Cody Northrop <cnorthrop@google.com> |
| * Author: Dave Houlton <daveh@lunarg.com> |
| * Author: Jeremy Kniager <jeremyk@lunarg.com> |
| * Author: Shannon McPherson <shannon@lunarg.com> |
| * Author: John Zulauf <jzulauf@lunarg.com> |
| */ |
| |
| #include "cast_utils.h" |
| #include "layer_validation_tests.h" |
| |
| TEST_F(VkLayerTest, GpuValidationArrayOOBGraphicsShaders) { |
| TEST_DESCRIPTION( |
| "GPU validation: Verify detection of out-of-bounds descriptor array indexing and use of uninitialized descriptors."); |
| |
| VkValidationFeatureEnableEXT enables[] = {VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT}; |
| VkValidationFeaturesEXT features = {}; |
| features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; |
| features.enabledValidationFeatureCount = 1; |
| features.pEnabledValidationFeatures = enables; |
| bool descriptor_indexing = CheckDescriptorIndexingSupportAndInitFramework(this, m_instance_extension_names, |
| m_device_extension_names, &features, m_errorMonitor); |
| if (DeviceIsMockICD() || DeviceSimulation()) { |
| printf("%s GPU-Assisted validation test requires a driver that can draw.\n", kSkipPrefix); |
| return; |
| } |
| |
| VkPhysicalDeviceFeatures2KHR features2 = {}; |
| auto indexing_features = lvl_init_struct<VkPhysicalDeviceDescriptorIndexingFeaturesEXT>(); |
| if (descriptor_indexing) { |
| PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR = |
| (PFN_vkGetPhysicalDeviceFeatures2KHR)vk::GetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR"); |
| ASSERT_TRUE(vkGetPhysicalDeviceFeatures2KHR != nullptr); |
| |
| features2 = lvl_init_struct<VkPhysicalDeviceFeatures2KHR>(&indexing_features); |
| vkGetPhysicalDeviceFeatures2KHR(gpu(), &features2); |
| |
| if (!indexing_features.runtimeDescriptorArray || !indexing_features.descriptorBindingSampledImageUpdateAfterBind || |
| !indexing_features.descriptorBindingPartiallyBound || !indexing_features.descriptorBindingVariableDescriptorCount || |
| !indexing_features.shaderSampledImageArrayNonUniformIndexing || |
| !indexing_features.shaderStorageBufferArrayNonUniformIndexing) { |
| printf("Not all descriptor indexing features supported, skipping descriptor indexing tests\n"); |
| descriptor_indexing = false; |
| } |
| } |
| |
| VkCommandPoolCreateFlags pool_flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2, pool_flags)); |
| if (m_device->props.apiVersion < VK_API_VERSION_1_1) { |
| printf("%s GPU-Assisted validation test requires Vulkan 1.1+.\n", kSkipPrefix); |
| return; |
| } |
| ASSERT_NO_FATAL_FAILURE(InitViewport()); |
| ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); |
| |
| // Make a uniform buffer to be passed to the shader that contains the invalid array index. |
| uint32_t qfi = 0; |
| VkBufferCreateInfo bci = {}; |
| bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
| bci.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; |
| bci.size = 1024; |
| bci.queueFamilyIndexCount = 1; |
| bci.pQueueFamilyIndices = &qfi; |
| VkBufferObj buffer0; |
| VkMemoryPropertyFlags mem_props = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| buffer0.init(*m_device, bci, mem_props); |
| |
| bci.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; |
| // Make another buffer to populate the buffer array to be indexed |
| VkBufferObj buffer1; |
| buffer1.init(*m_device, bci, mem_props); |
| |
| void *layout_pnext = nullptr; |
| void *allocate_pnext = nullptr; |
| auto pool_create_flags = 0; |
| auto layout_create_flags = 0; |
| VkDescriptorBindingFlagsEXT ds_binding_flags[2] = {}; |
| VkDescriptorSetLayoutBindingFlagsCreateInfoEXT layout_createinfo_binding_flags[1] = {}; |
| if (descriptor_indexing) { |
| ds_binding_flags[0] = 0; |
| ds_binding_flags[1] = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT; |
| |
| layout_createinfo_binding_flags[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT; |
| layout_createinfo_binding_flags[0].pNext = NULL; |
| layout_createinfo_binding_flags[0].bindingCount = 2; |
| layout_createinfo_binding_flags[0].pBindingFlags = ds_binding_flags; |
| layout_create_flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT; |
| pool_create_flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT; |
| layout_pnext = layout_createinfo_binding_flags; |
| } |
| |
| // Prepare descriptors |
| OneOffDescriptorSet descriptor_set(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6, VK_SHADER_STAGE_ALL, nullptr}, |
| }, |
| layout_create_flags, layout_pnext, pool_create_flags); |
| |
| VkDescriptorSetVariableDescriptorCountAllocateInfoEXT variable_count = {}; |
| uint32_t desc_counts; |
| if (descriptor_indexing) { |
| layout_create_flags = 0; |
| pool_create_flags = 0; |
| ds_binding_flags[1] = |
| VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT; |
| desc_counts = 6; // We'll reserve 8 spaces in the layout, but the descriptor will only use 6 |
| variable_count.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT; |
| variable_count.descriptorSetCount = 1; |
| variable_count.pDescriptorCounts = &desc_counts; |
| allocate_pnext = &variable_count; |
| } |
| |
| OneOffDescriptorSet descriptor_set_variable(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 8, VK_SHADER_STAGE_ALL, nullptr}, |
| }, |
| layout_create_flags, layout_pnext, pool_create_flags, allocate_pnext); |
| |
| const VkPipelineLayoutObj pipeline_layout(m_device, {&descriptor_set.layout_}); |
| const VkPipelineLayoutObj pipeline_layout_variable(m_device, {&descriptor_set_variable.layout_}); |
| VkTextureObj texture(m_device, nullptr); |
| VkSamplerObj sampler(m_device); |
| |
| VkDescriptorBufferInfo buffer_info[1] = {}; |
| buffer_info[0].buffer = buffer0.handle(); |
| buffer_info[0].offset = 0; |
| buffer_info[0].range = sizeof(uint32_t); |
| |
| VkDescriptorImageInfo image_info[6] = {}; |
| for (int i = 0; i < 6; i++) { |
| image_info[i] = texture.DescriptorImageInfo(); |
| image_info[i].sampler = sampler.handle(); |
| image_info[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| } |
| |
| VkWriteDescriptorSet descriptor_writes[2] = {}; |
| descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| descriptor_writes[0].dstSet = descriptor_set.set_; // descriptor_set; |
| descriptor_writes[0].dstBinding = 0; |
| descriptor_writes[0].descriptorCount = 1; |
| descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| descriptor_writes[0].pBufferInfo = buffer_info; |
| descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| descriptor_writes[1].dstSet = descriptor_set.set_; // descriptor_set; |
| descriptor_writes[1].dstBinding = 1; |
| if (descriptor_indexing) |
| descriptor_writes[1].descriptorCount = 5; // Intentionally don't write index 5 |
| else |
| descriptor_writes[1].descriptorCount = 6; |
| descriptor_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; |
| descriptor_writes[1].pImageInfo = image_info; |
| vk::UpdateDescriptorSets(m_device->device(), 2, descriptor_writes, 0, NULL); |
| if (descriptor_indexing) { |
| descriptor_writes[0].dstSet = descriptor_set_variable.set_; |
| descriptor_writes[1].dstSet = descriptor_set_variable.set_; |
| vk::UpdateDescriptorSets(m_device->device(), 2, descriptor_writes, 0, NULL); |
| } |
| |
| ds_binding_flags[0] = 0; |
| ds_binding_flags[1] = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT; |
| |
| // Resources for buffer tests |
| OneOffDescriptorSet descriptor_set_buffer(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 6, VK_SHADER_STAGE_ALL, nullptr}, |
| }, |
| 0, layout_pnext, 0); |
| |
| const VkPipelineLayoutObj pipeline_layout_buffer(m_device, {&descriptor_set_buffer.layout_}); |
| |
| VkDescriptorBufferInfo buffer_test_buffer_info[7] = {}; |
| buffer_test_buffer_info[0].buffer = buffer0.handle(); |
| buffer_test_buffer_info[0].offset = 0; |
| buffer_test_buffer_info[0].range = sizeof(uint32_t); |
| |
| for (int i = 1; i < 7; i++) { |
| buffer_test_buffer_info[i].buffer = buffer1.handle(); |
| buffer_test_buffer_info[i].offset = 0; |
| buffer_test_buffer_info[i].range = 4 * sizeof(float); |
| } |
| |
| if (descriptor_indexing) { |
| VkWriteDescriptorSet buffer_descriptor_writes[2] = {}; |
| buffer_descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| buffer_descriptor_writes[0].dstSet = descriptor_set_buffer.set_; // descriptor_set; |
| buffer_descriptor_writes[0].dstBinding = 0; |
| buffer_descriptor_writes[0].descriptorCount = 1; |
| buffer_descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| buffer_descriptor_writes[0].pBufferInfo = buffer_test_buffer_info; |
| buffer_descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| buffer_descriptor_writes[1].dstSet = descriptor_set_buffer.set_; // descriptor_set; |
| buffer_descriptor_writes[1].dstBinding = 1; |
| buffer_descriptor_writes[1].descriptorCount = 5; // Intentionally don't write index 5 |
| buffer_descriptor_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; |
| buffer_descriptor_writes[1].pBufferInfo = &buffer_test_buffer_info[1]; |
| vk::UpdateDescriptorSets(m_device->device(), 2, buffer_descriptor_writes, 0, NULL); |
| } |
| |
| // Shader programs for array OOB test in vertex stage: |
| // - The vertex shader fetches the invalid index from the uniform buffer and uses it to make an invalid index into another |
| // array. |
| char const *vsSource_vert = |
| "#version 450\n" |
| "\n" |
| "layout(std140, set = 0, binding = 0) uniform foo { uint tex_index[1]; } uniform_index_buffer;\n" |
| "layout(set = 0, binding = 1) uniform sampler2D tex[6];\n" |
| "vec2 vertices[3];\n" |
| "void main(){\n" |
| " vertices[0] = vec2(-1.0, -1.0);\n" |
| " vertices[1] = vec2( 1.0, -1.0);\n" |
| " vertices[2] = vec2( 0.0, 1.0);\n" |
| " gl_Position = vec4(vertices[gl_VertexIndex % 3], 0.0, 1.0);\n" |
| " gl_Position += 1e-30 * texture(tex[uniform_index_buffer.tex_index[0]], vec2(0, 0));\n" |
| "}\n"; |
| char const *fsSource_vert = |
| "#version 450\n" |
| "\n" |
| "layout(set = 0, binding = 1) uniform sampler2D tex[6];\n" |
| "layout(location = 0) out vec4 uFragColor;\n" |
| "void main(){\n" |
| " uFragColor = texture(tex[0], vec2(0, 0));\n" |
| "}\n"; |
| |
| // Shader programs for array OOB test in fragment stage: |
| // - The vertex shader fetches the invalid index from the uniform buffer and passes it to the fragment shader. |
| // - The fragment shader makes the invalid array access. |
| char const *vsSource_frag = |
| "#version 450\n" |
| "\n" |
| "layout(std140, binding = 0) uniform foo { uint tex_index[1]; } uniform_index_buffer;\n" |
| "layout(location = 0) out flat uint index;\n" |
| "vec2 vertices[3];\n" |
| "void main(){\n" |
| " vertices[0] = vec2(-1.0, -1.0);\n" |
| " vertices[1] = vec2( 1.0, -1.0);\n" |
| " vertices[2] = vec2( 0.0, 1.0);\n" |
| " gl_Position = vec4(vertices[gl_VertexIndex % 3], 0.0, 1.0);\n" |
| " index = uniform_index_buffer.tex_index[0];\n" |
| "}\n"; |
| char const *fsSource_frag = |
| "#version 450\n" |
| "\n" |
| "layout(set = 0, binding = 1) uniform sampler2D tex[6];\n" |
| "layout(location = 0) out vec4 uFragColor;\n" |
| "layout(location = 0) in flat uint index;\n" |
| "void main(){\n" |
| " uFragColor = texture(tex[index], vec2(0, 0));\n" |
| "}\n"; |
| char const *fsSource_frag_runtime = |
| "#version 450\n" |
| "#extension GL_EXT_nonuniform_qualifier : enable\n" |
| "\n" |
| "layout(set = 0, binding = 1) uniform sampler2D tex[];\n" |
| "layout(location = 0) out vec4 uFragColor;\n" |
| "layout(location = 0) in flat uint index;\n" |
| "void main(){\n" |
| " uFragColor = texture(tex[index], vec2(0, 0));\n" |
| "}\n"; |
| char const *fsSource_buffer = |
| "#version 450\n" |
| "#extension GL_EXT_nonuniform_qualifier : enable\n " |
| "\n" |
| "layout(set = 0, binding = 1) buffer foo { vec4 val; } colors[];\n" |
| "layout(location = 0) out vec4 uFragColor;\n" |
| "layout(location = 0) in flat uint index;\n" |
| "void main(){\n" |
| " uFragColor = colors[index].val;\n" |
| "}\n"; |
| char const *gsSource = |
| "#version 450\n" |
| "#extension GL_EXT_nonuniform_qualifier : enable\n " |
| "layout(triangles) in;\n" |
| "layout(triangle_strip, max_vertices=3) out;\n" |
| "layout(location=0) in VertexData { vec4 x; } gs_in[];\n" |
| "layout(std140, set = 0, binding = 0) uniform ufoo { uint index; } uniform_index_buffer;\n" |
| "layout(set = 0, binding = 1) buffer bfoo { vec4 val; } adds[];\n" |
| "void main() {\n" |
| " gl_Position = gs_in[0].x + adds[uniform_index_buffer.index].val.x;\n" |
| " EmitVertex();\n" |
| "}\n"; |
| static const char *tesSource = |
| "#version 450\n" |
| "#extension GL_EXT_nonuniform_qualifier : enable\n " |
| "layout(std140, set = 0, binding = 0) uniform ufoo { uint index; } uniform_index_buffer;\n" |
| "layout(set = 0, binding = 1) buffer bfoo { vec4 val; } adds[];\n" |
| "layout(triangles, equal_spacing, cw) in;\n" |
| "void main() {\n" |
| " gl_Position = adds[uniform_index_buffer.index].val;\n" |
| "}\n"; |
| |
| struct TestCase { |
| char const *vertex_source; |
| char const *fragment_source; |
| char const *geometry_source; |
| char const *tess_ctrl_source; |
| char const *tess_eval_source; |
| bool debug; |
| const VkPipelineLayoutObj *pipeline_layout; |
| const OneOffDescriptorSet *descriptor_set; |
| uint32_t index; |
| char const *expected_error; |
| }; |
| |
| std::vector<TestCase> tests; |
| tests.push_back({vsSource_vert, fsSource_vert, nullptr, nullptr, nullptr, false, &pipeline_layout, &descriptor_set, 25, |
| "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({vsSource_frag, fsSource_frag, nullptr, nullptr, nullptr, false, &pipeline_layout, &descriptor_set, 25, |
| "Index of 25 used to index descriptor array of length 6."}); |
| #if !defined(ANDROID) |
| // The Android test framework uses shaderc for online compilations. Even when configured to compile with debug info, |
| // shaderc seems to drop the OpLine instructions from the shader binary. This causes the following two tests to fail |
| // on Android platforms. Skip these tests until the shaderc issue is understood/resolved. |
| tests.push_back({vsSource_vert, fsSource_vert, nullptr, nullptr, nullptr, true, &pipeline_layout, &descriptor_set, 25, |
| "gl_Position += 1e-30 * texture(tex[uniform_index_buffer.tex_index[0]], vec2(0, 0));"}); |
| tests.push_back({vsSource_frag, fsSource_frag, nullptr, nullptr, nullptr, true, &pipeline_layout, &descriptor_set, 25, |
| "uFragColor = texture(tex[index], vec2(0, 0));"}); |
| #endif |
| if (descriptor_indexing) { |
| tests.push_back({vsSource_frag, fsSource_frag_runtime, nullptr, nullptr, nullptr, false, &pipeline_layout, &descriptor_set, |
| 25, "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({vsSource_frag, fsSource_frag_runtime, nullptr, nullptr, nullptr, false, &pipeline_layout, &descriptor_set, |
| 5, "Descriptor index 5 is uninitialized"}); |
| // Pick 6 below because it is less than the maximum specified, but more than the actual specified |
| tests.push_back({vsSource_frag, fsSource_frag_runtime, nullptr, nullptr, nullptr, false, &pipeline_layout_variable, |
| &descriptor_set_variable, 6, "Index of 6 used to index descriptor array of length 6."}); |
| tests.push_back({vsSource_frag, fsSource_frag_runtime, nullptr, nullptr, nullptr, false, &pipeline_layout_variable, |
| &descriptor_set_variable, 5, "Descriptor index 5 is uninitialized"}); |
| tests.push_back({vsSource_frag, fsSource_buffer, nullptr, nullptr, nullptr, false, &pipeline_layout_buffer, |
| &descriptor_set_buffer, 25, "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({vsSource_frag, fsSource_buffer, nullptr, nullptr, nullptr, false, &pipeline_layout_buffer, |
| &descriptor_set_buffer, 5, "Descriptor index 5 is uninitialized"}); |
| if (m_device->phy().features().geometryShader) { |
| // OOB Geometry |
| tests.push_back({bindStateVertShaderText, bindStateFragShaderText, gsSource, nullptr, nullptr, false, |
| &pipeline_layout_buffer, &descriptor_set_buffer, 25, "Stage = Geometry"}); |
| // Uninitialized Geometry |
| tests.push_back({bindStateVertShaderText, bindStateFragShaderText, gsSource, nullptr, nullptr, false, |
| &pipeline_layout_buffer, &descriptor_set_buffer, 5, "Stage = Geometry"}); |
| } |
| if (m_device->phy().features().tessellationShader) { |
| tests.push_back({bindStateVertShaderText, bindStateFragShaderText, nullptr, bindStateTscShaderText, tesSource, false, |
| &pipeline_layout_buffer, &descriptor_set_buffer, 25, "Stage = Tessellation Eval"}); |
| tests.push_back({bindStateVertShaderText, bindStateFragShaderText, nullptr, bindStateTscShaderText, tesSource, false, |
| &pipeline_layout_buffer, &descriptor_set_buffer, 5, "Stage = Tessellation Eval"}); |
| } |
| } |
| |
| VkViewport viewport = m_viewports[0]; |
| VkRect2D scissors = m_scissors[0]; |
| |
| VkSubmitInfo submit_info = {}; |
| submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
| submit_info.commandBufferCount = 1; |
| submit_info.pCommandBuffers = &m_commandBuffer->handle(); |
| |
| for (const auto &iter : tests) { |
| VkResult err; |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, iter.expected_error); |
| VkShaderObj vs(m_device, iter.vertex_source, VK_SHADER_STAGE_VERTEX_BIT, this, "main", iter.debug); |
| VkShaderObj fs(m_device, iter.fragment_source, VK_SHADER_STAGE_FRAGMENT_BIT, this, "main", iter.debug); |
| VkShaderObj *gs = nullptr; |
| VkShaderObj *tcs = nullptr; |
| VkShaderObj *tes = nullptr; |
| VkPipelineObj pipe(m_device); |
| pipe.AddShader(&vs); |
| pipe.AddShader(&fs); |
| if (iter.geometry_source) { |
| gs = new VkShaderObj(m_device, iter.geometry_source, VK_SHADER_STAGE_GEOMETRY_BIT, this, "main", iter.debug); |
| pipe.AddShader(gs); |
| } |
| if (iter.tess_ctrl_source && iter.tess_eval_source) { |
| tcs = new VkShaderObj(m_device, iter.tess_ctrl_source, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, this, "main", |
| iter.debug); |
| tes = new VkShaderObj(m_device, iter.tess_eval_source, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, this, "main", |
| iter.debug); |
| pipe.AddShader(tcs); |
| pipe.AddShader(tes); |
| VkPipelineInputAssemblyStateCreateInfo iasci{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, nullptr, 0, |
| VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, VK_FALSE}; |
| VkPipelineTessellationDomainOriginStateCreateInfo tessellationDomainOriginStateInfo = { |
| VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, VK_NULL_HANDLE, |
| VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT}; |
| |
| VkPipelineTessellationStateCreateInfo tsci{VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, |
| &tessellationDomainOriginStateInfo, 0, 3}; |
| pipe.SetTessellation(&tsci); |
| pipe.SetInputAssembly(&iasci); |
| } |
| pipe.AddDefaultColorAttachment(); |
| err = pipe.CreateVKPipeline(iter.pipeline_layout->handle(), renderPass()); |
| ASSERT_VK_SUCCESS(err); |
| m_commandBuffer->begin(); |
| m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo); |
| vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle()); |
| vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, iter.pipeline_layout->handle(), 0, 1, |
| &iter.descriptor_set->set_, 0, nullptr); |
| vk::CmdSetViewport(m_commandBuffer->handle(), 0, 1, &viewport); |
| vk::CmdSetScissor(m_commandBuffer->handle(), 0, 1, &scissors); |
| vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0); |
| vk::CmdEndRenderPass(m_commandBuffer->handle()); |
| m_commandBuffer->end(); |
| uint32_t *data = (uint32_t *)buffer0.memory().map(); |
| data[0] = iter.index; |
| buffer0.memory().unmap(); |
| |
| m_errorMonitor->SetUnexpectedError("UNASSIGNED-CoreValidation-DrawState-DescriptorSetNotUpdated"); |
| vk::QueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE); |
| vk::QueueWaitIdle(m_device->m_queue); |
| m_errorMonitor->VerifyFound(); |
| if (gs) { |
| delete gs; |
| } |
| if (tcs && tes) { |
| delete tcs; |
| delete tes; |
| } |
| } |
| auto c_queue = m_device->GetDefaultComputeQueue(); |
| if (c_queue && descriptor_indexing) { |
| char const *csSource = |
| "#version 450\n" |
| "#extension GL_EXT_nonuniform_qualifier : enable\n " |
| "layout(set = 0, binding = 0) uniform ufoo { uint index; } u_index;" |
| "layout(set = 0, binding = 1) buffer StorageBuffer {\n" |
| " uint data;\n" |
| "} Data[];\n" |
| "void main() {\n" |
| " Data[(u_index.index - 1)].data = Data[u_index.index].data;\n" |
| "}\n"; |
| |
| auto shader_module = new VkShaderObj(m_device, csSource, VK_SHADER_STAGE_COMPUTE_BIT, this); |
| |
| VkPipelineShaderStageCreateInfo stage; |
| stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| stage.pNext = nullptr; |
| stage.flags = 0; |
| stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; |
| stage.module = shader_module->handle(); |
| stage.pName = "main"; |
| stage.pSpecializationInfo = nullptr; |
| |
| // CreateComputePipelines |
| VkComputePipelineCreateInfo pipeline_info = {}; |
| pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; |
| pipeline_info.pNext = nullptr; |
| pipeline_info.flags = 0; |
| pipeline_info.layout = pipeline_layout_buffer.handle(); |
| pipeline_info.basePipelineHandle = VK_NULL_HANDLE; |
| pipeline_info.basePipelineIndex = -1; |
| pipeline_info.stage = stage; |
| |
| VkPipeline c_pipeline; |
| vk::CreateComputePipelines(device(), VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &c_pipeline); |
| VkCommandBufferBeginInfo begin_info = {}; |
| VkCommandBufferInheritanceInfo hinfo = {}; |
| hinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; |
| begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
| begin_info.pInheritanceInfo = &hinfo; |
| |
| m_commandBuffer->begin(&begin_info); |
| vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_COMPUTE, c_pipeline); |
| vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout_buffer.handle(), 0, 1, |
| &descriptor_set_buffer.set_, 0, nullptr); |
| vk::CmdDispatch(m_commandBuffer->handle(), 1, 1, 1); |
| m_commandBuffer->end(); |
| |
| // Uninitialized |
| uint32_t *data = (uint32_t *)buffer0.memory().map(); |
| data[0] = 5; |
| buffer0.memory().unmap(); |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "Stage = Compute"); |
| m_errorMonitor->SetUnexpectedError("UNASSIGNED-CoreValidation-DrawState-DescriptorSetNotUpdated"); |
| vk::QueueSubmit(c_queue->handle(), 1, &submit_info, VK_NULL_HANDLE); |
| vk::QueueWaitIdle(m_device->m_queue); |
| m_errorMonitor->VerifyFound(); |
| // Out of Bounds |
| data = (uint32_t *)buffer0.memory().map(); |
| data[0] = 25; |
| buffer0.memory().unmap(); |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "Stage = Compute"); |
| m_errorMonitor->SetUnexpectedError("UNASSIGNED-CoreValidation-DrawState-DescriptorSetNotUpdated"); |
| vk::QueueSubmit(c_queue->handle(), 1, &submit_info, VK_NULL_HANDLE); |
| vk::QueueWaitIdle(m_device->m_queue); |
| m_errorMonitor->VerifyFound(); |
| vk::DestroyPipeline(m_device->handle(), c_pipeline, NULL); |
| vk::DestroyShaderModule(m_device->handle(), shader_module->handle(), NULL); |
| } |
| return; |
| } |
| |
| TEST_F(VkLayerTest, GpuBufferDeviceAddressOOB) { |
| bool supported = InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| |
| VkValidationFeatureEnableEXT enables[] = {VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT}; |
| VkValidationFeaturesEXT features = {}; |
| features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; |
| features.enabledValidationFeatureCount = 1; |
| features.pEnabledValidationFeatures = enables; |
| InitFramework(myDbgFunc, m_errorMonitor, &features); |
| |
| if (DeviceIsMockICD() || DeviceSimulation()) { |
| printf("%s GPU-Assisted validation test requires a driver that can draw.\n", kSkipPrefix); |
| return; |
| } |
| |
| supported = supported && DeviceExtensionSupported(gpu(), nullptr, VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); |
| m_device_extension_names.push_back(VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); |
| |
| VkPhysicalDeviceFeatures2KHR features2 = {}; |
| auto bda_features = lvl_init_struct<VkPhysicalDeviceBufferDeviceAddressFeaturesEXT>(); |
| PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR = |
| (PFN_vkGetPhysicalDeviceFeatures2KHR)vk::GetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR"); |
| ASSERT_TRUE(vkGetPhysicalDeviceFeatures2KHR != nullptr); |
| |
| features2 = lvl_init_struct<VkPhysicalDeviceFeatures2KHR>(&bda_features); |
| vkGetPhysicalDeviceFeatures2KHR(gpu(), &features2); |
| supported = supported && bda_features.bufferDeviceAddress; |
| |
| if (!supported) { |
| printf("Buffer Device Address feature not supported, skipping test\n"); |
| return; |
| } |
| VkCommandPoolCreateFlags pool_flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2, pool_flags)); |
| if (m_device->props.apiVersion < VK_API_VERSION_1_1) { |
| printf("%s GPU-Assisted validation test requires Vulkan 1.1+.\n", kSkipPrefix); |
| return; |
| } |
| ASSERT_NO_FATAL_FAILURE(InitViewport()); |
| ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); |
| |
| // Make a uniform buffer to be passed to the shader that contains the pointer and write count |
| uint32_t qfi = 0; |
| VkBufferCreateInfo bci = {}; |
| bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
| bci.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; |
| bci.size = 8; |
| bci.queueFamilyIndexCount = 1; |
| bci.pQueueFamilyIndices = &qfi; |
| VkBufferObj buffer0; |
| VkMemoryPropertyFlags mem_props = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| buffer0.init(*m_device, bci, mem_props); |
| |
| // Make another buffer to write to |
| bci.usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT; |
| bci.size = 64; // Buffer should be 16*4 = 64 bytes |
| VkBufferObj buffer1; |
| buffer1.init(*m_device, bci, mem_props); |
| |
| // Get device address of buffer to write to |
| VkBufferDeviceAddressInfoEXT bda_info = {}; |
| bda_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT; |
| bda_info.buffer = buffer1.handle(); |
| auto vkGetBufferDeviceAddressEXT = |
| (PFN_vkGetBufferDeviceAddressEXT)vk::GetDeviceProcAddr(m_device->device(), "vkGetBufferDeviceAddressEXT"); |
| ASSERT_TRUE(vkGetBufferDeviceAddressEXT != nullptr); |
| auto pBuffer = vkGetBufferDeviceAddressEXT(m_device->device(), &bda_info); |
| |
| OneOffDescriptorSet descriptor_set(m_device, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr}}); |
| |
| const VkPipelineLayoutObj pipeline_layout(m_device, {&descriptor_set.layout_}); |
| VkDescriptorBufferInfo buffer_test_buffer_info[2] = {}; |
| buffer_test_buffer_info[0].buffer = buffer0.handle(); |
| buffer_test_buffer_info[0].offset = 0; |
| buffer_test_buffer_info[0].range = sizeof(uint32_t); |
| |
| VkWriteDescriptorSet descriptor_writes[1] = {}; |
| descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| descriptor_writes[0].dstSet = descriptor_set.set_; |
| descriptor_writes[0].dstBinding = 0; |
| descriptor_writes[0].descriptorCount = 1; |
| descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| descriptor_writes[0].pBufferInfo = buffer_test_buffer_info; |
| vk::UpdateDescriptorSets(m_device->device(), 1, descriptor_writes, 0, NULL); |
| |
| char const *shader_source = |
| "#version 450\n" |
| "#extension GL_EXT_buffer_reference : enable\n " |
| "layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct;\n" |
| "layout(set = 0, binding = 0) uniform ufoo {\n" |
| " bufStruct data;\n" |
| " int nWrites;\n" |
| "} u_info;\n" |
| "layout(buffer_reference, std140) buffer bufStruct {\n" |
| " int a[4];\n" |
| "};\n" |
| "void main() {\n" |
| " for (int i=0; i < u_info.nWrites; ++i) {\n" |
| " u_info.data.a[i] = 0xdeadca71;\n" |
| " }\n" |
| "}\n"; |
| VkShaderObj vs(m_device, shader_source, VK_SHADER_STAGE_VERTEX_BIT, this, "main", true); |
| |
| VkViewport viewport = m_viewports[0]; |
| VkRect2D scissors = m_scissors[0]; |
| |
| VkSubmitInfo submit_info = {}; |
| submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
| submit_info.commandBufferCount = 1; |
| submit_info.pCommandBuffers = &m_commandBuffer->handle(); |
| |
| VkPipelineObj pipe(m_device); |
| pipe.AddShader(&vs); |
| pipe.AddDefaultColorAttachment(); |
| VkResult err = pipe.CreateVKPipeline(pipeline_layout.handle(), renderPass()); |
| ASSERT_VK_SUCCESS(err); |
| |
| VkCommandBufferBeginInfo begin_info = {}; |
| VkCommandBufferInheritanceInfo hinfo = {}; |
| hinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; |
| begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
| begin_info.pInheritanceInfo = &hinfo; |
| |
| m_commandBuffer->begin(&begin_info); |
| m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo); |
| vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle()); |
| vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1, |
| &descriptor_set.set_, 0, nullptr); |
| vk::CmdSetViewport(m_commandBuffer->handle(), 0, 1, &viewport); |
| vk::CmdSetScissor(m_commandBuffer->handle(), 0, 1, &scissors); |
| vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0); |
| vk::CmdEndRenderPass(m_commandBuffer->handle()); |
| m_commandBuffer->end(); |
| |
| // Starting address too low |
| VkDeviceAddress *data = (VkDeviceAddress *)buffer0.memory().map(); |
| data[0] = pBuffer - 16; |
| data[1] = 4; |
| buffer0.memory().unmap(); |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "access out of bounds"); |
| err = vk::QueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE); |
| ASSERT_VK_SUCCESS(err); |
| err = vk::QueueWaitIdle(m_device->m_queue); |
| ASSERT_VK_SUCCESS(err); |
| m_errorMonitor->VerifyFound(); |
| |
| // Run past the end |
| data = (VkDeviceAddress *)buffer0.memory().map(); |
| data[0] = pBuffer; |
| data[1] = 5; |
| buffer0.memory().unmap(); |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "access out of bounds"); |
| err = vk::QueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE); |
| ASSERT_VK_SUCCESS(err); |
| err = vk::QueueWaitIdle(m_device->m_queue); |
| ASSERT_VK_SUCCESS(err); |
| m_errorMonitor->VerifyFound(); |
| |
| // Positive test - stay inside buffer |
| m_errorMonitor->ExpectSuccess(); |
| data = (VkDeviceAddress *)buffer0.memory().map(); |
| data[0] = pBuffer; |
| data[1] = 4; |
| buffer0.memory().unmap(); |
| err = vk::QueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE); |
| ASSERT_VK_SUCCESS(err); |
| err = vk::QueueWaitIdle(m_device->m_queue); |
| ASSERT_VK_SUCCESS(err); |
| m_errorMonitor->VerifyNotFound(); |
| } |
| |
| TEST_F(VkLayerTest, GpuValidationArrayOOBRayTracingShaders) { |
| TEST_DESCRIPTION( |
| "GPU validation: Verify detection of out-of-bounds descriptor array indexing and use of uninitialized descriptors for " |
| "ray tracing shaders."); |
| |
| std::array<const char *, 1> required_instance_extensions = {VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME}; |
| for (auto instance_extension : required_instance_extensions) { |
| if (InstanceExtensionSupported(instance_extension)) { |
| m_instance_extension_names.push_back(instance_extension); |
| } else { |
| printf("%s Did not find required instance extension %s; skipped.\n", kSkipPrefix, instance_extension); |
| return; |
| } |
| } |
| |
| VkValidationFeatureEnableEXT validation_feature_enables[] = {VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT}; |
| VkValidationFeaturesEXT validation_features = {}; |
| validation_features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; |
| validation_features.enabledValidationFeatureCount = 1; |
| validation_features.pEnabledValidationFeatures = validation_feature_enables; |
| bool descriptor_indexing = CheckDescriptorIndexingSupportAndInitFramework( |
| this, m_instance_extension_names, m_device_extension_names, &validation_features, m_errorMonitor); |
| |
| if (DeviceIsMockICD() || DeviceSimulation()) { |
| printf("%s Test not supported by MockICD, skipping tests\n", kSkipPrefix); |
| return; |
| } |
| |
| std::array<const char *, 2> required_device_extensions = {VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, |
| VK_NV_RAY_TRACING_EXTENSION_NAME}; |
| for (auto device_extension : required_device_extensions) { |
| if (DeviceExtensionSupported(gpu(), nullptr, device_extension)) { |
| m_device_extension_names.push_back(device_extension); |
| } else { |
| printf("%s %s Extension not supported, skipping tests\n", kSkipPrefix, device_extension); |
| return; |
| } |
| } |
| |
| VkPhysicalDeviceFeatures2KHR features2 = {}; |
| auto indexing_features = lvl_init_struct<VkPhysicalDeviceDescriptorIndexingFeaturesEXT>(); |
| if (descriptor_indexing) { |
| PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR = |
| (PFN_vkGetPhysicalDeviceFeatures2KHR)vk::GetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR"); |
| ASSERT_TRUE(vkGetPhysicalDeviceFeatures2KHR != nullptr); |
| |
| features2 = lvl_init_struct<VkPhysicalDeviceFeatures2KHR>(&indexing_features); |
| vkGetPhysicalDeviceFeatures2KHR(gpu(), &features2); |
| |
| if (!indexing_features.runtimeDescriptorArray || !indexing_features.descriptorBindingPartiallyBound || |
| !indexing_features.descriptorBindingSampledImageUpdateAfterBind || |
| !indexing_features.descriptorBindingVariableDescriptorCount) { |
| printf("Not all descriptor indexing features supported, skipping descriptor indexing tests\n"); |
| descriptor_indexing = false; |
| } |
| } |
| VkCommandPoolCreateFlags pool_flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2, pool_flags)); |
| |
| PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR = |
| (PFN_vkGetPhysicalDeviceProperties2KHR)vk::GetInstanceProcAddr(instance(), "vkGetPhysicalDeviceProperties2KHR"); |
| ASSERT_TRUE(vkGetPhysicalDeviceProperties2KHR != nullptr); |
| |
| auto ray_tracing_properties = lvl_init_struct<VkPhysicalDeviceRayTracingPropertiesNV>(); |
| auto properties2 = lvl_init_struct<VkPhysicalDeviceProperties2KHR>(&ray_tracing_properties); |
| vkGetPhysicalDeviceProperties2KHR(gpu(), &properties2); |
| if (ray_tracing_properties.maxTriangleCount == 0) { |
| printf("%s Did not find required ray tracing properties; skipped.\n", kSkipPrefix); |
| return; |
| } |
| |
| VkQueue ray_tracing_queue = m_device->m_queue; |
| uint32_t ray_tracing_queue_family_index = 0; |
| |
| // If supported, run on the compute only queue. |
| uint32_t compute_only_queue_family_index = m_device->QueueFamilyMatching(VK_QUEUE_COMPUTE_BIT, VK_QUEUE_GRAPHICS_BIT); |
| if (compute_only_queue_family_index != UINT32_MAX) { |
| const auto &compute_only_queues = m_device->queue_family_queues(compute_only_queue_family_index); |
| if (!compute_only_queues.empty()) { |
| ray_tracing_queue = compute_only_queues[0]->handle(); |
| ray_tracing_queue_family_index = compute_only_queue_family_index; |
| } |
| } |
| |
| VkCommandPoolObj ray_tracing_command_pool(m_device, ray_tracing_queue_family_index, |
| VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); |
| VkCommandBufferObj ray_tracing_command_buffer(m_device, &ray_tracing_command_pool); |
| |
| struct AABB { |
| float min_x; |
| float min_y; |
| float min_z; |
| float max_x; |
| float max_y; |
| float max_z; |
| }; |
| |
| const std::vector<AABB> aabbs = {{-1.0f, -1.0f, -1.0f, +1.0f, +1.0f, +1.0f}}; |
| |
| struct VkGeometryInstanceNV { |
| float transform[12]; |
| uint32_t instanceCustomIndex : 24; |
| uint32_t mask : 8; |
| uint32_t instanceOffset : 24; |
| uint32_t flags : 8; |
| uint64_t accelerationStructureHandle; |
| }; |
| |
| VkDeviceSize aabb_buffer_size = sizeof(AABB) * aabbs.size(); |
| VkBufferObj aabb_buffer; |
| aabb_buffer.init(*m_device, aabb_buffer_size, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, |
| VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, {ray_tracing_queue_family_index}); |
| |
| uint8_t *mapped_aabb_buffer_data = (uint8_t *)aabb_buffer.memory().map(); |
| std::memcpy(mapped_aabb_buffer_data, (uint8_t *)aabbs.data(), static_cast<std::size_t>(aabb_buffer_size)); |
| aabb_buffer.memory().unmap(); |
| |
| VkGeometryNV geometry = {}; |
| geometry.sType = VK_STRUCTURE_TYPE_GEOMETRY_NV; |
| geometry.geometryType = VK_GEOMETRY_TYPE_AABBS_NV; |
| geometry.geometry.triangles = {}; |
| geometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_GEOMETRY_TRIANGLES_NV; |
| geometry.geometry.aabbs = {}; |
| geometry.geometry.aabbs.sType = VK_STRUCTURE_TYPE_GEOMETRY_AABB_NV; |
| geometry.geometry.aabbs.aabbData = aabb_buffer.handle(); |
| geometry.geometry.aabbs.numAABBs = static_cast<uint32_t>(aabbs.size()); |
| geometry.geometry.aabbs.offset = 0; |
| geometry.geometry.aabbs.stride = static_cast<VkDeviceSize>(sizeof(AABB)); |
| geometry.flags = 0; |
| |
| VkAccelerationStructureInfoNV bot_level_as_info = {}; |
| bot_level_as_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV; |
| bot_level_as_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV; |
| bot_level_as_info.instanceCount = 0; |
| bot_level_as_info.geometryCount = 1; |
| bot_level_as_info.pGeometries = &geometry; |
| |
| VkAccelerationStructureCreateInfoNV bot_level_as_create_info = {}; |
| bot_level_as_create_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV; |
| bot_level_as_create_info.info = bot_level_as_info; |
| |
| VkAccelerationStructureObj bot_level_as(*m_device, bot_level_as_create_info); |
| |
| const std::vector<VkGeometryInstanceNV> instances = { |
| VkGeometryInstanceNV{ |
| { |
| // clang-format off |
| 1.0f, 0.0f, 0.0f, 0.0f, |
| 0.0f, 1.0f, 0.0f, 0.0f, |
| 0.0f, 0.0f, 1.0f, 0.0f, |
| // clang-format on |
| }, |
| 0, |
| 0xFF, |
| 0, |
| VK_GEOMETRY_INSTANCE_TRIANGLE_CULL_DISABLE_BIT_NV, |
| bot_level_as.opaque_handle(), |
| }, |
| }; |
| |
| VkDeviceSize instance_buffer_size = sizeof(VkGeometryInstanceNV) * instances.size(); |
| VkBufferObj instance_buffer; |
| instance_buffer.init(*m_device, instance_buffer_size, |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, |
| VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, {ray_tracing_queue_family_index}); |
| |
| uint8_t *mapped_instance_buffer_data = (uint8_t *)instance_buffer.memory().map(); |
| std::memcpy(mapped_instance_buffer_data, (uint8_t *)instances.data(), static_cast<std::size_t>(instance_buffer_size)); |
| instance_buffer.memory().unmap(); |
| |
| VkAccelerationStructureInfoNV top_level_as_info = {}; |
| top_level_as_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV; |
| top_level_as_info.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV; |
| top_level_as_info.instanceCount = 1; |
| top_level_as_info.geometryCount = 0; |
| |
| VkAccelerationStructureCreateInfoNV top_level_as_create_info = {}; |
| top_level_as_create_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV; |
| top_level_as_create_info.info = top_level_as_info; |
| |
| VkAccelerationStructureObj top_level_as(*m_device, top_level_as_create_info); |
| |
| VkDeviceSize scratch_buffer_size = std::max(bot_level_as.build_scratch_memory_requirements().memoryRequirements.size, |
| top_level_as.build_scratch_memory_requirements().memoryRequirements.size); |
| VkBufferObj scratch_buffer; |
| scratch_buffer.init(*m_device, scratch_buffer_size, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_BUFFER_USAGE_RAY_TRACING_BIT_NV); |
| |
| ray_tracing_command_buffer.begin(); |
| |
| // Build bot level acceleration structure |
| ray_tracing_command_buffer.BuildAccelerationStructure(&bot_level_as, scratch_buffer.handle()); |
| |
| // Barrier to prevent using scratch buffer for top level build before bottom level build finishes |
| VkMemoryBarrier memory_barrier = {}; |
| memory_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; |
| memory_barrier.srcAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV; |
| memory_barrier.dstAccessMask = VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV; |
| ray_tracing_command_buffer.PipelineBarrier(VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, |
| VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV, 0, 1, &memory_barrier, 0, |
| nullptr, 0, nullptr); |
| |
| // Build top level acceleration structure |
| ray_tracing_command_buffer.BuildAccelerationStructure(&top_level_as, scratch_buffer.handle(), instance_buffer.handle()); |
| |
| ray_tracing_command_buffer.end(); |
| |
| VkSubmitInfo submit_info = {}; |
| submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
| submit_info.commandBufferCount = 1; |
| submit_info.pCommandBuffers = &ray_tracing_command_buffer.handle(); |
| vk::QueueSubmit(ray_tracing_queue, 1, &submit_info, VK_NULL_HANDLE); |
| vk::QueueWaitIdle(ray_tracing_queue); |
| m_errorMonitor->VerifyNotFound(); |
| |
| VkTextureObj texture(m_device, nullptr); |
| VkSamplerObj sampler(m_device); |
| |
| VkDeviceSize storage_buffer_size = 1024; |
| VkBufferObj storage_buffer; |
| storage_buffer.init(*m_device, storage_buffer_size, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, |
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, {ray_tracing_queue_family_index}); |
| |
| VkDeviceSize shader_binding_table_buffer_size = ray_tracing_properties.shaderGroupHandleSize * 4ull; |
| VkBufferObj shader_binding_table_buffer; |
| shader_binding_table_buffer.init(*m_device, shader_binding_table_buffer_size, |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, |
| VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, {ray_tracing_queue_family_index}); |
| |
| // Setup descriptors! |
| const VkShaderStageFlags kAllRayTracingStages = VK_SHADER_STAGE_RAYGEN_BIT_NV | VK_SHADER_STAGE_ANY_HIT_BIT_NV | |
| VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV | VK_SHADER_STAGE_MISS_BIT_NV | |
| VK_SHADER_STAGE_INTERSECTION_BIT_NV | VK_SHADER_STAGE_CALLABLE_BIT_NV; |
| |
| void *layout_pnext = nullptr; |
| void *allocate_pnext = nullptr; |
| VkDescriptorPoolCreateFlags pool_create_flags = 0; |
| VkDescriptorSetLayoutCreateFlags layout_create_flags = 0; |
| VkDescriptorBindingFlagsEXT ds_binding_flags[3] = {}; |
| VkDescriptorSetLayoutBindingFlagsCreateInfoEXT layout_createinfo_binding_flags[1] = {}; |
| if (descriptor_indexing) { |
| ds_binding_flags[0] = 0; |
| ds_binding_flags[1] = 0; |
| ds_binding_flags[2] = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT; |
| |
| layout_createinfo_binding_flags[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT; |
| layout_createinfo_binding_flags[0].pNext = NULL; |
| layout_createinfo_binding_flags[0].bindingCount = 3; |
| layout_createinfo_binding_flags[0].pBindingFlags = ds_binding_flags; |
| layout_create_flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT; |
| pool_create_flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT; |
| layout_pnext = layout_createinfo_binding_flags; |
| } |
| |
| // Prepare descriptors |
| OneOffDescriptorSet ds(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV, 1, kAllRayTracingStages, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, kAllRayTracingStages, nullptr}, |
| {2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6, kAllRayTracingStages, nullptr}, |
| }, |
| layout_create_flags, layout_pnext, pool_create_flags); |
| |
| VkDescriptorSetVariableDescriptorCountAllocateInfoEXT variable_count = {}; |
| uint32_t desc_counts; |
| if (descriptor_indexing) { |
| layout_create_flags = 0; |
| pool_create_flags = 0; |
| ds_binding_flags[2] = |
| VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT; |
| desc_counts = 6; // We'll reserve 8 spaces in the layout, but the descriptor will only use 6 |
| variable_count.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT; |
| variable_count.descriptorSetCount = 1; |
| variable_count.pDescriptorCounts = &desc_counts; |
| allocate_pnext = &variable_count; |
| } |
| |
| OneOffDescriptorSet ds_variable(m_device, |
| { |
| {0, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV, 1, kAllRayTracingStages, nullptr}, |
| {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, kAllRayTracingStages, nullptr}, |
| {2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 8, kAllRayTracingStages, nullptr}, |
| }, |
| layout_create_flags, layout_pnext, pool_create_flags, allocate_pnext); |
| |
| VkAccelerationStructureNV top_level_as_handle = top_level_as.handle(); |
| VkWriteDescriptorSetAccelerationStructureNV write_descript_set_as = {}; |
| write_descript_set_as.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV; |
| write_descript_set_as.accelerationStructureCount = 1; |
| write_descript_set_as.pAccelerationStructures = &top_level_as_handle; |
| |
| VkDescriptorBufferInfo descriptor_buffer_info = {}; |
| descriptor_buffer_info.buffer = storage_buffer.handle(); |
| descriptor_buffer_info.offset = 0; |
| descriptor_buffer_info.range = storage_buffer_size; |
| |
| VkDescriptorImageInfo descriptor_image_infos[6] = {}; |
| for (int i = 0; i < 6; i++) { |
| descriptor_image_infos[i] = texture.DescriptorImageInfo(); |
| descriptor_image_infos[i].sampler = sampler.handle(); |
| descriptor_image_infos[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| } |
| |
| VkWriteDescriptorSet descriptor_writes[3] = {}; |
| descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| descriptor_writes[0].dstSet = ds.set_; |
| descriptor_writes[0].dstBinding = 0; |
| descriptor_writes[0].descriptorCount = 1; |
| descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV; |
| descriptor_writes[0].pNext = &write_descript_set_as; |
| |
| descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| descriptor_writes[1].dstSet = ds.set_; |
| descriptor_writes[1].dstBinding = 1; |
| descriptor_writes[1].descriptorCount = 1; |
| descriptor_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; |
| descriptor_writes[1].pBufferInfo = &descriptor_buffer_info; |
| |
| descriptor_writes[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| descriptor_writes[2].dstSet = ds.set_; |
| descriptor_writes[2].dstBinding = 2; |
| if (descriptor_indexing) { |
| descriptor_writes[2].descriptorCount = 5; // Intentionally don't write index 5 |
| } else { |
| descriptor_writes[2].descriptorCount = 6; |
| } |
| descriptor_writes[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; |
| descriptor_writes[2].pImageInfo = descriptor_image_infos; |
| vk::UpdateDescriptorSets(m_device->device(), 3, descriptor_writes, 0, NULL); |
| if (descriptor_indexing) { |
| descriptor_writes[0].dstSet = ds_variable.set_; |
| descriptor_writes[1].dstSet = ds_variable.set_; |
| descriptor_writes[2].dstSet = ds_variable.set_; |
| vk::UpdateDescriptorSets(m_device->device(), 3, descriptor_writes, 0, NULL); |
| } |
| |
| const VkPipelineLayoutObj pipeline_layout(m_device, {&ds.layout_}); |
| const VkPipelineLayoutObj pipeline_layout_variable(m_device, {&ds_variable.layout_}); |
| |
| const auto SetImagesArrayLength = [](const std::string &shader_template, const std::string &length_str) { |
| const std::string to_replace = "IMAGES_ARRAY_LENGTH"; |
| |
| std::string result = shader_template; |
| auto position = result.find(to_replace); |
| assert(position != std::string::npos); |
| result.replace(position, to_replace.length(), length_str); |
| return result; |
| }; |
| |
| const std::string rgen_source_template = R"(#version 460 |
| #extension GL_EXT_nonuniform_qualifier : require |
| #extension GL_EXT_samplerless_texture_functions : require |
| #extension GL_NV_ray_tracing : require |
| |
| layout(set = 0, binding = 0) uniform accelerationStructureNV topLevelAS; |
| layout(set = 0, binding = 1, std430) buffer RayTracingSbo { |
| uint rgen_index; |
| uint ahit_index; |
| uint chit_index; |
| uint miss_index; |
| uint intr_index; |
| uint call_index; |
| |
| uint rgen_ran; |
| uint ahit_ran; |
| uint chit_ran; |
| uint miss_ran; |
| uint intr_ran; |
| uint call_ran; |
| |
| float result1; |
| float result2; |
| float result3; |
| } sbo; |
| layout(set = 0, binding = 2) uniform texture2D textures[IMAGES_ARRAY_LENGTH]; |
| |
| layout(location = 0) rayPayloadNV vec3 payload; |
| layout(location = 3) callableDataNV vec3 callableData; |
| |
| void main() { |
| sbo.rgen_ran = 1; |
| |
| executeCallableNV(0, 3); |
| sbo.result1 = callableData.x; |
| |
| vec3 origin = vec3(0.0f, 0.0f, -2.0f); |
| vec3 direction = vec3(0.0f, 0.0f, 1.0f); |
| |
| traceNV(topLevelAS, gl_RayFlagsNoneNV, 0xFF, 0, 1, 0, origin, 0.001, direction, 10000.0, 0); |
| sbo.result2 = payload.x; |
| |
| traceNV(topLevelAS, gl_RayFlagsNoneNV, 0xFF, 0, 1, 0, origin, 0.001, -direction, 10000.0, 0); |
| sbo.result3 = payload.x; |
| |
| if (sbo.rgen_index > 0) { |
| // OOB here: |
| sbo.result3 = texelFetch(textures[sbo.rgen_index], ivec2(0, 0), 0).x; |
| } |
| } |
| )"; |
| |
| const std::string rgen_source = SetImagesArrayLength(rgen_source_template, "6"); |
| const std::string rgen_source_runtime = SetImagesArrayLength(rgen_source_template, ""); |
| |
| const std::string ahit_source_template = R"(#version 460 |
| #extension GL_EXT_nonuniform_qualifier : require |
| #extension GL_EXT_samplerless_texture_functions : require |
| #extension GL_NV_ray_tracing : require |
| |
| layout(set = 0, binding = 1, std430) buffer StorageBuffer { |
| uint rgen_index; |
| uint ahit_index; |
| uint chit_index; |
| uint miss_index; |
| uint intr_index; |
| uint call_index; |
| |
| uint rgen_ran; |
| uint ahit_ran; |
| uint chit_ran; |
| uint miss_ran; |
| uint intr_ran; |
| uint call_ran; |
| |
| float result1; |
| float result2; |
| float result3; |
| } sbo; |
| layout(set = 0, binding = 2) uniform texture2D textures[IMAGES_ARRAY_LENGTH]; |
| |
| hitAttributeNV vec3 hitValue; |
| |
| layout(location = 0) rayPayloadInNV vec3 payload; |
| |
| void main() { |
| sbo.ahit_ran = 2; |
| |
| payload = vec3(0.1234f); |
| |
| if (sbo.ahit_index > 0) { |
| // OOB here: |
| payload.x = texelFetch(textures[sbo.ahit_index], ivec2(0, 0), 0).x; |
| } |
| } |
| )"; |
| const std::string ahit_source = SetImagesArrayLength(ahit_source_template, "6"); |
| const std::string ahit_source_runtime = SetImagesArrayLength(ahit_source_template, ""); |
| |
| const std::string chit_source_template = R"(#version 460 |
| #extension GL_EXT_nonuniform_qualifier : require |
| #extension GL_EXT_samplerless_texture_functions : require |
| #extension GL_NV_ray_tracing : require |
| |
| layout(set = 0, binding = 1, std430) buffer RayTracingSbo { |
| uint rgen_index; |
| uint ahit_index; |
| uint chit_index; |
| uint miss_index; |
| uint intr_index; |
| uint call_index; |
| |
| uint rgen_ran; |
| uint ahit_ran; |
| uint chit_ran; |
| uint miss_ran; |
| uint intr_ran; |
| uint call_ran; |
| |
| float result1; |
| float result2; |
| float result3; |
| } sbo; |
| layout(set = 0, binding = 2) uniform texture2D textures[IMAGES_ARRAY_LENGTH]; |
| |
| layout(location = 0) rayPayloadInNV vec3 payload; |
| |
| hitAttributeNV vec3 attribs; |
| |
| void main() { |
| sbo.chit_ran = 3; |
| |
| payload = attribs; |
| if (sbo.chit_index > 0) { |
| // OOB here: |
| payload.x = texelFetch(textures[sbo.chit_index], ivec2(0, 0), 0).x; |
| } |
| } |
| )"; |
| const std::string chit_source = SetImagesArrayLength(chit_source_template, "6"); |
| const std::string chit_source_runtime = SetImagesArrayLength(chit_source_template, ""); |
| |
| const std::string miss_source_template = R"(#version 460 |
| #extension GL_EXT_nonuniform_qualifier : enable |
| #extension GL_EXT_samplerless_texture_functions : require |
| #extension GL_NV_ray_tracing : require |
| |
| layout(set = 0, binding = 1, std430) buffer RayTracingSbo { |
| uint rgen_index; |
| uint ahit_index; |
| uint chit_index; |
| uint miss_index; |
| uint intr_index; |
| uint call_index; |
| |
| uint rgen_ran; |
| uint ahit_ran; |
| uint chit_ran; |
| uint miss_ran; |
| uint intr_ran; |
| uint call_ran; |
| |
| float result1; |
| float result2; |
| float result3; |
| } sbo; |
| layout(set = 0, binding = 2) uniform texture2D textures[IMAGES_ARRAY_LENGTH]; |
| |
| layout(location = 0) rayPayloadInNV vec3 payload; |
| |
| void main() { |
| sbo.miss_ran = 4; |
| |
| payload = vec3(1.0, 0.0, 0.0); |
| |
| if (sbo.miss_index > 0) { |
| // OOB here: |
| payload.x = texelFetch(textures[sbo.miss_index], ivec2(0, 0), 0).x; |
| } |
| } |
| )"; |
| const std::string miss_source = SetImagesArrayLength(miss_source_template, "6"); |
| const std::string miss_source_runtime = SetImagesArrayLength(miss_source_template, ""); |
| |
| const std::string intr_source_template = R"(#version 460 |
| #extension GL_EXT_nonuniform_qualifier : require |
| #extension GL_EXT_samplerless_texture_functions : require |
| #extension GL_NV_ray_tracing : require |
| |
| layout(set = 0, binding = 1, std430) buffer StorageBuffer { |
| uint rgen_index; |
| uint ahit_index; |
| uint chit_index; |
| uint miss_index; |
| uint intr_index; |
| uint call_index; |
| |
| uint rgen_ran; |
| uint ahit_ran; |
| uint chit_ran; |
| uint miss_ran; |
| uint intr_ran; |
| uint call_ran; |
| |
| float result1; |
| float result2; |
| float result3; |
| } sbo; |
| layout(set = 0, binding = 2) uniform texture2D textures[IMAGES_ARRAY_LENGTH]; |
| |
| hitAttributeNV vec3 hitValue; |
| |
| void main() { |
| sbo.intr_ran = 5; |
| |
| hitValue = vec3(0.0f, 0.5f, 0.0f); |
| |
| reportIntersectionNV(1.0f, 0); |
| |
| if (sbo.intr_index > 0) { |
| // OOB here: |
| hitValue.x = texelFetch(textures[sbo.intr_index], ivec2(0, 0), 0).x; |
| } |
| } |
| )"; |
| const std::string intr_source = SetImagesArrayLength(intr_source_template, "6"); |
| const std::string intr_source_runtime = SetImagesArrayLength(intr_source_template, ""); |
| |
| const std::string call_source_template = R"(#version 460 |
| #extension GL_EXT_nonuniform_qualifier : require |
| #extension GL_EXT_samplerless_texture_functions : require |
| #extension GL_NV_ray_tracing : require |
| |
| layout(set = 0, binding = 1, std430) buffer StorageBuffer { |
| uint rgen_index; |
| uint ahit_index; |
| uint chit_index; |
| uint miss_index; |
| uint intr_index; |
| uint call_index; |
| |
| uint rgen_ran; |
| uint ahit_ran; |
| uint chit_ran; |
| uint miss_ran; |
| uint intr_ran; |
| uint call_ran; |
| |
| float result1; |
| float result2; |
| float result3; |
| } sbo; |
| layout(set = 0, binding = 2) uniform texture2D textures[IMAGES_ARRAY_LENGTH]; |
| |
| layout(location = 3) callableDataInNV vec3 callableData; |
| |
| void main() { |
| sbo.call_ran = 6; |
| |
| callableData = vec3(0.1234f); |
| |
| if (sbo.call_index > 0) { |
| // OOB here: |
| callableData.x = texelFetch(textures[sbo.call_index], ivec2(0, 0), 0).x; |
| } |
| } |
| )"; |
| const std::string call_source = SetImagesArrayLength(call_source_template, "6"); |
| const std::string call_source_runtime = SetImagesArrayLength(call_source_template, ""); |
| |
| struct TestCase { |
| const std::string &rgen_shader_source; |
| const std::string &ahit_shader_source; |
| const std::string &chit_shader_source; |
| const std::string &miss_shader_source; |
| const std::string &intr_shader_source; |
| const std::string &call_shader_source; |
| bool variable_length; |
| uint32_t rgen_index; |
| uint32_t ahit_index; |
| uint32_t chit_index; |
| uint32_t miss_index; |
| uint32_t intr_index; |
| uint32_t call_index; |
| const char *expected_error; |
| }; |
| |
| std::vector<TestCase> tests; |
| tests.push_back({rgen_source, ahit_source, chit_source, miss_source, intr_source, call_source, false, 25, 0, 0, 0, 0, 0, |
| "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source, ahit_source, chit_source, miss_source, intr_source, call_source, false, 0, 25, 0, 0, 0, 0, |
| "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source, ahit_source, chit_source, miss_source, intr_source, call_source, false, 0, 0, 25, 0, 0, 0, |
| "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source, ahit_source, chit_source, miss_source, intr_source, call_source, false, 0, 0, 0, 25, 0, 0, |
| "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source, ahit_source, chit_source, miss_source, intr_source, call_source, false, 0, 0, 0, 0, 25, 0, |
| "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source, ahit_source, chit_source, miss_source, intr_source, call_source, false, 0, 0, 0, 0, 0, 25, |
| "Index of 25 used to index descriptor array of length 6."}); |
| |
| if (descriptor_indexing) { |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 25, 0, 0, 0, 0, 0, "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 25, 0, 0, 0, 0, "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 25, 0, 0, 0, "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 0, 25, 0, 0, "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 0, 0, 25, 0, "Index of 25 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 0, 0, 0, 25, "Index of 25 used to index descriptor array of length 6."}); |
| |
| // For this group, 6 is less than max specified (max specified is 8) but more than actual specified (actual specified is 5) |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 6, 0, 0, 0, 0, 0, "Index of 6 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 6, 0, 0, 0, 0, "Index of 6 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 6, 0, 0, 0, "Index of 6 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 0, 6, 0, 0, "Index of 6 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 0, 0, 6, 0, "Index of 6 used to index descriptor array of length 6."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 0, 0, 0, 6, "Index of 6 used to index descriptor array of length 6."}); |
| |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 5, 0, 0, 0, 0, 0, "Descriptor index 5 is uninitialized."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 5, 0, 0, 0, 0, "Descriptor index 5 is uninitialized."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 5, 0, 0, 0, "Descriptor index 5 is uninitialized."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 0, 5, 0, 0, "Descriptor index 5 is uninitialized."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 0, 0, 5, 0, "Descriptor index 5 is uninitialized."}); |
| tests.push_back({rgen_source_runtime, ahit_source_runtime, chit_source_runtime, miss_source_runtime, intr_source_runtime, |
| call_source_runtime, true, 0, 0, 0, 0, 0, 5, "Descriptor index 5 is uninitialized."}); |
| } |
| |
| PFN_vkCreateRayTracingPipelinesNV vkCreateRayTracingPipelinesNV = reinterpret_cast<PFN_vkCreateRayTracingPipelinesNV>( |
| vk::GetDeviceProcAddr(m_device->handle(), "vkCreateRayTracingPipelinesNV")); |
| ASSERT_TRUE(vkCreateRayTracingPipelinesNV != nullptr); |
| |
| PFN_vkGetRayTracingShaderGroupHandlesNV vkGetRayTracingShaderGroupHandlesNV = |
| reinterpret_cast<PFN_vkGetRayTracingShaderGroupHandlesNV>( |
| vk::GetDeviceProcAddr(m_device->handle(), "vkGetRayTracingShaderGroupHandlesNV")); |
| ASSERT_TRUE(vkGetRayTracingShaderGroupHandlesNV != nullptr); |
| |
| PFN_vkCmdTraceRaysNV vkCmdTraceRaysNV = |
| reinterpret_cast<PFN_vkCmdTraceRaysNV>(vk::GetDeviceProcAddr(m_device->handle(), "vkCmdTraceRaysNV")); |
| ASSERT_TRUE(vkCmdTraceRaysNV != nullptr); |
| |
| // Iteration 0 tests with no descriptor set bound (to sanity test "draw" validation). Iteration 1 |
| // tests what's in the test case vector. |
| for (int i = 0; i < 2; ++i) { |
| for (const auto &test : tests) { |
| if (i == 1) { |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, test.expected_error); |
| } |
| |
| VkShaderObj rgen_shader(m_device, test.rgen_shader_source.c_str(), VK_SHADER_STAGE_RAYGEN_BIT_NV, this, "main"); |
| VkShaderObj ahit_shader(m_device, test.ahit_shader_source.c_str(), VK_SHADER_STAGE_ANY_HIT_BIT_NV, this, "main"); |
| VkShaderObj chit_shader(m_device, test.chit_shader_source.c_str(), VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV, this, "main"); |
| VkShaderObj miss_shader(m_device, test.miss_shader_source.c_str(), VK_SHADER_STAGE_MISS_BIT_NV, this, "main"); |
| VkShaderObj intr_shader(m_device, test.intr_shader_source.c_str(), VK_SHADER_STAGE_INTERSECTION_BIT_NV, this, "main"); |
| VkShaderObj call_shader(m_device, test.call_shader_source.c_str(), VK_SHADER_STAGE_CALLABLE_BIT_NV, this, "main"); |
| |
| VkPipelineShaderStageCreateInfo stage_create_infos[6] = {}; |
| stage_create_infos[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| stage_create_infos[0].stage = VK_SHADER_STAGE_RAYGEN_BIT_NV; |
| stage_create_infos[0].module = rgen_shader.handle(); |
| stage_create_infos[0].pName = "main"; |
| |
| stage_create_infos[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| stage_create_infos[1].stage = VK_SHADER_STAGE_ANY_HIT_BIT_NV; |
| stage_create_infos[1].module = ahit_shader.handle(); |
| stage_create_infos[1].pName = "main"; |
| |
| stage_create_infos[2].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| stage_create_infos[2].stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV; |
| stage_create_infos[2].module = chit_shader.handle(); |
| stage_create_infos[2].pName = "main"; |
| |
| stage_create_infos[3].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| stage_create_infos[3].stage = VK_SHADER_STAGE_MISS_BIT_NV; |
| stage_create_infos[3].module = miss_shader.handle(); |
| stage_create_infos[3].pName = "main"; |
| |
| stage_create_infos[4].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| stage_create_infos[4].stage = VK_SHADER_STAGE_INTERSECTION_BIT_NV; |
| stage_create_infos[4].module = intr_shader.handle(); |
| stage_create_infos[4].pName = "main"; |
| |
| stage_create_infos[5].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| stage_create_infos[5].stage = VK_SHADER_STAGE_CALLABLE_BIT_NV; |
| stage_create_infos[5].module = call_shader.handle(); |
| stage_create_infos[5].pName = "main"; |
| |
| VkRayTracingShaderGroupCreateInfoNV group_create_infos[4] = {}; |
| group_create_infos[0].sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; |
| group_create_infos[0].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV; |
| group_create_infos[0].generalShader = 0; // rgen |
| group_create_infos[0].closestHitShader = VK_SHADER_UNUSED_NV; |
| group_create_infos[0].anyHitShader = VK_SHADER_UNUSED_NV; |
| group_create_infos[0].intersectionShader = VK_SHADER_UNUSED_NV; |
| |
| group_create_infos[1].sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; |
| group_create_infos[1].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV; |
| group_create_infos[1].generalShader = 3; // miss |
| group_create_infos[1].closestHitShader = VK_SHADER_UNUSED_NV; |
| group_create_infos[1].anyHitShader = VK_SHADER_UNUSED_NV; |
| group_create_infos[1].intersectionShader = VK_SHADER_UNUSED_NV; |
| |
| group_create_infos[2].sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; |
| group_create_infos[2].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV; |
| group_create_infos[2].generalShader = VK_SHADER_UNUSED_NV; |
| group_create_infos[2].closestHitShader = 2; |
| group_create_infos[2].anyHitShader = 1; |
| group_create_infos[2].intersectionShader = 4; |
| |
| group_create_infos[3].sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV; |
| group_create_infos[3].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV; |
| group_create_infos[3].generalShader = 5; // call |
| group_create_infos[3].closestHitShader = VK_SHADER_UNUSED_NV; |
| group_create_infos[3].anyHitShader = VK_SHADER_UNUSED_NV; |
| group_create_infos[3].intersectionShader = VK_SHADER_UNUSED_NV; |
| |
| VkRayTracingPipelineCreateInfoNV pipeline_ci = {}; |
| pipeline_ci.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV; |
| pipeline_ci.stageCount = 6; |
| pipeline_ci.pStages = stage_create_infos; |
| pipeline_ci.groupCount = 4; |
| pipeline_ci.pGroups = group_create_infos; |
| pipeline_ci.maxRecursionDepth = 2; |
| pipeline_ci.layout = test.variable_length ? pipeline_layout_variable.handle() : pipeline_layout.handle(); |
| |
| VkPipeline pipeline = VK_NULL_HANDLE; |
| ASSERT_VK_SUCCESS( |
| vkCreateRayTracingPipelinesNV(m_device->handle(), VK_NULL_HANDLE, 1, &pipeline_ci, nullptr, &pipeline)); |
| |
| std::vector<uint8_t> shader_binding_table_data; |
| shader_binding_table_data.resize(static_cast<std::size_t>(shader_binding_table_buffer_size), 0); |
| ASSERT_VK_SUCCESS(vkGetRayTracingShaderGroupHandlesNV(m_device->handle(), pipeline, 0, 4, |
| static_cast<std::size_t>(shader_binding_table_buffer_size), |
| shader_binding_table_data.data())); |
| |
| uint8_t *mapped_shader_binding_table_data = (uint8_t *)shader_binding_table_buffer.memory().map(); |
| std::memcpy(mapped_shader_binding_table_data, shader_binding_table_data.data(), shader_binding_table_data.size()); |
| shader_binding_table_buffer.memory().unmap(); |
| |
| ray_tracing_command_buffer.begin(); |
| |
| vk::CmdBindPipeline(ray_tracing_command_buffer.handle(), VK_PIPELINE_BIND_POINT_RAY_TRACING_NV, pipeline); |
| |
| if (i == 1) { |
| vk::CmdBindDescriptorSets(ray_tracing_command_buffer.handle(), VK_PIPELINE_BIND_POINT_RAY_TRACING_NV, |
| test.variable_length ? pipeline_layout_variable.handle() : pipeline_layout.handle(), 0, 1, |
| test.variable_length ? &ds_variable.set_ : &ds.set_, 0, nullptr); |
| } else { |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdTraceRaysNV-None-02697"); |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, |
| "UNASSIGNED-CoreValidation-DrawState-DescriptorSetNotBound"); |
| } |
| |
| vkCmdTraceRaysNV(ray_tracing_command_buffer.handle(), shader_binding_table_buffer.handle(), |
| ray_tracing_properties.shaderGroupHandleSize * 0ull, shader_binding_table_buffer.handle(), |
| ray_tracing_properties.shaderGroupHandleSize * 1ull, ray_tracing_properties.shaderGroupHandleSize, |
| shader_binding_table_buffer.handle(), ray_tracing_properties.shaderGroupHandleSize * 2ull, |
| ray_tracing_properties.shaderGroupHandleSize, shader_binding_table_buffer.handle(), |
| ray_tracing_properties.shaderGroupHandleSize * 3ull, ray_tracing_properties.shaderGroupHandleSize, |
| /*width=*/1, /*height=*/1, /*depth=*/1); |
| |
| ray_tracing_command_buffer.end(); |
| |
| // Update the index of the texture that the shaders should read |
| uint32_t *mapped_storage_buffer_data = (uint32_t *)storage_buffer.memory().map(); |
| mapped_storage_buffer_data[0] = test.rgen_index; |
| mapped_storage_buffer_data[1] = test.ahit_index; |
| mapped_storage_buffer_data[2] = test.chit_index; |
| mapped_storage_buffer_data[3] = test.miss_index; |
| mapped_storage_buffer_data[4] = test.intr_index; |
| mapped_storage_buffer_data[5] = test.call_index; |
| mapped_storage_buffer_data[6] = 0; |
| mapped_storage_buffer_data[7] = 0; |
| mapped_storage_buffer_data[8] = 0; |
| mapped_storage_buffer_data[9] = 0; |
| mapped_storage_buffer_data[10] = 0; |
| mapped_storage_buffer_data[11] = 0; |
| storage_buffer.memory().unmap(); |
| |
| m_errorMonitor->SetUnexpectedError("UNASSIGNED-CoreValidation-DrawState-DescriptorSetNotUpdated"); |
| vk::QueueSubmit(ray_tracing_queue, 1, &submit_info, VK_NULL_HANDLE); |
| vk::QueueWaitIdle(ray_tracing_queue); |
| m_errorMonitor->VerifyFound(); |
| |
| mapped_storage_buffer_data = (uint32_t *)storage_buffer.memory().map(); |
| if (i == 1) { |
| ASSERT_TRUE(mapped_storage_buffer_data[6] == 1); |
| ASSERT_TRUE(mapped_storage_buffer_data[7] == 2); |
| ASSERT_TRUE(mapped_storage_buffer_data[8] == 3); |
| ASSERT_TRUE(mapped_storage_buffer_data[9] == 4); |
| ASSERT_TRUE(mapped_storage_buffer_data[10] == 5); |
| ASSERT_TRUE(mapped_storage_buffer_data[11] == 6); |
| } |
| storage_buffer.memory().unmap(); |
| |
| vk::DestroyPipeline(m_device->handle(), pipeline, nullptr); |
| } |
| } |
| } |
| |
| TEST_F(VkLayerTest, InvalidDescriptorPoolConsistency) { |
| VkResult err; |
| |
| TEST_DESCRIPTION("Allocate descriptor sets from one DS pool and attempt to delete them from another."); |
| |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkFreeDescriptorSets-pDescriptorSets-parent"); |
| |
| ASSERT_NO_FATAL_FAILURE(Init()); |
| ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); |
| |
| VkDescriptorPoolSize ds_type_count = {}; |
| ds_type_count.type = VK_DESCRIPTOR_TYPE_SAMPLER; |
| ds_type_count.descriptorCount = 1; |
| |
| VkDescriptorPoolCreateInfo ds_pool_ci = {}; |
| ds_pool_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; |
| ds_pool_ci.pNext = NULL; |
| ds_pool_ci.flags = 0; |
| ds_pool_ci.maxSets = 1; |
| ds_pool_ci.poolSizeCount = 1; |
| ds_pool_ci.pPoolSizes = &ds_type_count; |
| |
| VkDescriptorPool bad_pool; |
| err = vk::CreateDescriptorPool(m_device->device(), &ds_pool_ci, NULL, &bad_pool); |
| ASSERT_VK_SUCCESS(err); |
| |
| OneOffDescriptorSet descriptor_set(m_device, { |
| {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr}, |
| }); |
| |
| err = vk::FreeDescriptorSets(m_device->device(), bad_pool, 1, &descriptor_set.set_); |
| |
| m_errorMonitor->VerifyFound(); |
| |
| vk::DestroyDescriptorPool(m_device->device(), bad_pool, NULL); |
| } |
| |
| TEST_F(VkLayerTest, DrawWithPipelineIncompatibleWithSubpass) { |
| TEST_DESCRIPTION("Use a pipeline for the wrong subpass in a render pass instance"); |
| |
| ASSERT_NO_FATAL_FAILURE(Init()); |
| |
| // A renderpass with two subpasses, both writing the same attachment. |
| VkAttachmentDescription attach[] = { |
| {0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| }; |
| VkAttachmentReference ref = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; |
| VkSubpassDescription subpasses[] = { |
| {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &ref, nullptr, nullptr, 0, nullptr}, |
| {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &ref, nullptr, nullptr, 0, nullptr}, |
| }; |
| VkSubpassDependency dep = {0, |
| 1, |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, |
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, |
| VK_DEPENDENCY_BY_REGION_BIT}; |
| VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, attach, 2, subpasses, 1, &dep}; |
| VkRenderPass rp; |
| VkResult err = vk::CreateRenderPass(m_device->device(), &rpci, nullptr, &rp); |
| ASSERT_VK_SUCCESS(err); |
| |
| VkImageObj image(m_device); |
| image.InitNoLayout(32, 32, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0); |
| VkImageView imageView = image.targetView(VK_FORMAT_R8G8B8A8_UNORM); |
| |
| VkFramebufferCreateInfo fbci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, 1, &imageView, 32, 32, 1}; |
| VkFramebuffer fb; |
| err = vk::CreateFramebuffer(m_device->device(), &fbci, nullptr, &fb); |
| ASSERT_VK_SUCCESS(err); |
| |
| VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this); |
| VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this); |
| VkPipelineObj pipe(m_device); |
| pipe.AddDefaultColorAttachment(); |
| pipe.AddShader(&vs); |
| pipe.AddShader(&fs); |
| VkViewport viewport = {0.0f, 0.0f, 64.0f, 64.0f, 0.0f, 1.0f}; |
| m_viewports.push_back(viewport); |
| pipe.SetViewport(m_viewports); |
| VkRect2D rect = {}; |
| m_scissors.push_back(rect); |
| pipe.SetScissor(m_scissors); |
| |
| const VkPipelineLayoutObj pl(m_device); |
| pipe.CreateVKPipeline(pl.handle(), rp); |
| |
| m_commandBuffer->begin(); |
| |
| VkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, |
| nullptr, |
| rp, |
| fb, |
| {{ |
| 0, |
| 0, |
| }, |
| {32, 32}}, |
| 0, |
| nullptr}; |
| |
| // subtest 1: bind in the wrong subpass |
| vk::CmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE); |
| vk::CmdNextSubpass(m_commandBuffer->handle(), VK_SUBPASS_CONTENTS_INLINE); |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "built for subpass 0 but used in subpass 1"); |
| vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle()); |
| vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0); |
| m_errorMonitor->VerifyFound(); |
| |
| vk::CmdEndRenderPass(m_commandBuffer->handle()); |
| |
| // subtest 2: bind in correct subpass, then transition to next subpass |
| vk::CmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE); |
| vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle()); |
| vk::CmdNextSubpass(m_commandBuffer->handle(), VK_SUBPASS_CONTENTS_INLINE); |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "built for subpass 0 but used in subpass 1"); |
| vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0); |
| m_errorMonitor->VerifyFound(); |
| |
| vk::CmdEndRenderPass(m_commandBuffer->handle()); |
| |
| m_commandBuffer->end(); |
| |
| vk::DestroyFramebuffer(m_device->device(), fb, nullptr); |
| vk::DestroyRenderPass(m_device->device(), rp, nullptr); |
| } |
| |
| TEST_F(VkLayerTest, ImageBarrierSubpassConflict) { |
| TEST_DESCRIPTION("Check case where subpass index references different image from image barrier"); |
| ASSERT_NO_FATAL_FAILURE(Init()); |
| |
| // Create RP/FB combo where subpass has incorrect index attachment, this is 2nd half of "VUID-vkCmdPipelineBarrier-image-02635" |
| VkAttachmentDescription attach[] = { |
| {0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| {0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| }; |
| // ref attachment points to wrong attachment index compared to img_barrier below |
| VkAttachmentReference ref = {1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; |
| VkSubpassDescription subpasses[] = { |
| {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &ref, nullptr, nullptr, 0, nullptr}, |
| }; |
| VkSubpassDependency dep = {0, |
| 0, |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, |
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, |
| VK_DEPENDENCY_BY_REGION_BIT}; |
| |
| VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 2, attach, 1, subpasses, 1, &dep}; |
| VkRenderPass rp; |
| |
| VkResult err = vk::CreateRenderPass(m_device->device(), &rpci, nullptr, &rp); |
| ASSERT_VK_SUCCESS(err); |
| |
| VkImageObj image(m_device); |
| image.InitNoLayout(32, 32, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0); |
| VkImageView imageView = image.targetView(VK_FORMAT_R8G8B8A8_UNORM); |
| VkImageObj image2(m_device); |
| image2.InitNoLayout(32, 32, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0); |
| VkImageView imageView2 = image2.targetView(VK_FORMAT_R8G8B8A8_UNORM); |
| // re-use imageView from start of test |
| VkImageView iv_array[2] = {imageView, imageView2}; |
| |
| VkFramebufferCreateInfo fbci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, 2, iv_array, 32, 32, 1}; |
| VkFramebuffer fb; |
| err = vk::CreateFramebuffer(m_device->device(), &fbci, nullptr, &fb); |
| ASSERT_VK_SUCCESS(err); |
| |
| VkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, |
| nullptr, |
| rp, |
| fb, |
| {{ |
| 0, |
| 0, |
| }, |
| {32, 32}}, |
| 0, |
| nullptr}; |
| |
| VkImageMemoryBarrier img_barrier = {}; |
| img_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; |
| img_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| img_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| img_barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| img_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| img_barrier.image = image.handle(); /* barrier references image from attachment index 0 */ |
| img_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| img_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| img_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| img_barrier.subresourceRange.baseArrayLayer = 0; |
| img_barrier.subresourceRange.baseMipLevel = 0; |
| img_barrier.subresourceRange.layerCount = 1; |
| img_barrier.subresourceRange.levelCount = 1; |
| m_commandBuffer->begin(); |
| vk::CmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE); |
| m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "VUID-vkCmdPipelineBarrier-image-02635"); |
| vk::CmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, |
| &img_barrier); |
| m_errorMonitor->VerifyFound(); |
| |
| vk::DestroyFramebuffer(m_device->device(), fb, nullptr); |
| vk::DestroyRenderPass(m_device->device(), rp, nullptr); |
| } |
| |
| TEST_F(VkLayerTest, RenderPassCreateAttachmentIndexOutOfRange) { |
| // Check for VK_KHR_get_physical_device_properties2 |
| if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor)); |
| bool rp2Supported = CheckCreateRenderPass2Support(this, m_device_extension_names); |
| ASSERT_NO_FATAL_FAILURE(InitState()); |
| |
| // There are no attachments, but refer to attachment 0. |
| VkAttachmentReference ref = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; |
| VkSubpassDescription subpasses[] = { |
| {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &ref, nullptr, nullptr, 0, nullptr}, |
| }; |
| |
| VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 0, nullptr, 1, subpasses, 0, nullptr}; |
| |
| // "... must be less than the total number of attachments ..." |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkRenderPassCreateInfo-attachment-00834", |
| "VUID-VkRenderPassCreateInfo2KHR-attachment-03051"); |
| } |
| |
| TEST_F(VkLayerTest, RenderPassCreateAttachmentReadOnlyButCleared) { |
| // Check for VK_KHR_get_physical_device_properties2 |
| if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor)); |
| |
| bool rp2Supported = CheckCreateRenderPass2Support(this, m_device_extension_names); |
| bool maintenance2Supported = rp2Supported; |
| |
| // Check for VK_KHR_maintenance2 |
| if (!rp2Supported && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_MAINTENANCE2_EXTENSION_NAME)) { |
| m_device_extension_names.push_back(VK_KHR_MAINTENANCE2_EXTENSION_NAME); |
| maintenance2Supported = true; |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitState()); |
| |
| if (m_device->props.apiVersion < VK_API_VERSION_1_1) { |
| maintenance2Supported = true; |
| } |
| |
| VkAttachmentDescription description = {0, |
| VK_FORMAT_D32_SFLOAT_S8_UINT, |
| VK_SAMPLE_COUNT_1_BIT, |
| VK_ATTACHMENT_LOAD_OP_CLEAR, |
| VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_CLEAR, |
| VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_IMAGE_LAYOUT_GENERAL, |
| VK_IMAGE_LAYOUT_GENERAL}; |
| |
| VkAttachmentReference depth_stencil_ref = {0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL}; |
| |
| VkSubpassDescription subpass = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr, &depth_stencil_ref, 0, |
| nullptr}; |
| |
| VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, &description, 1, &subpass, 0, nullptr}; |
| |
| // VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL but depth cleared |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkRenderPassCreateInfo-pAttachments-00836", |
| "VUID-VkRenderPassCreateInfo2KHR-pAttachments-02522"); |
| |
| if (maintenance2Supported) { |
| // VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL but depth cleared |
| depth_stencil_ref.layout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkRenderPassCreateInfo-pAttachments-01566", nullptr); |
| |
| // VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL but depth cleared |
| depth_stencil_ref.layout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkRenderPassCreateInfo-pAttachments-01567", nullptr); |
| } |
| } |
| |
| TEST_F(VkLayerTest, RenderPassCreateAttachmentMismatchingLayoutsColor) { |
| TEST_DESCRIPTION("Attachment is used simultaneously as two color attachments with different layouts."); |
| |
| // Check for VK_KHR_get_physical_device_properties2 |
| if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor)); |
| bool rp2Supported = CheckCreateRenderPass2Support(this, m_device_extension_names); |
| ASSERT_NO_FATAL_FAILURE(InitState()); |
| |
| VkAttachmentDescription attach[] = { |
| {0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| }; |
| VkAttachmentReference refs[] = { |
| {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| {0, VK_IMAGE_LAYOUT_GENERAL}, |
| }; |
| VkSubpassDescription subpasses[] = { |
| {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 2, refs, nullptr, nullptr, 0, nullptr}, |
| }; |
| |
| VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, attach, 1, subpasses, 0, nullptr}; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "subpass 0 already uses attachment 0 with a different image layout", |
| "subpass 0 already uses attachment 0 with a different image layout"); |
| } |
| |
| TEST_F(VkLayerTest, RenderPassCreateAttachmentDescriptionInvalidFinalLayout) { |
| TEST_DESCRIPTION("VkAttachmentDescription's finalLayout must not be UNDEFINED or PREINITIALIZED"); |
| |
| // Check for VK_KHR_get_physical_device_properties2 |
| if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor)); |
| bool rp2Supported = CheckCreateRenderPass2Support(this, m_device_extension_names); |
| if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME)) { |
| m_device_extension_names.push_back(VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME); |
| } |
| PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR = |
| (PFN_vkGetPhysicalDeviceFeatures2KHR)vk::GetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR"); |
| auto separate_depth_stencil_layouts_features = lvl_init_struct<VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR>(); |
| auto features2 = lvl_init_struct<VkPhysicalDeviceFeatures2KHR>(&separate_depth_stencil_layouts_features); |
| if (vkGetPhysicalDeviceFeatures2KHR) { |
| vkGetPhysicalDeviceFeatures2KHR(gpu(), &features2); |
| } else { |
| separate_depth_stencil_layouts_features.separateDepthStencilLayouts = VK_FALSE; |
| } |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, (vkGetPhysicalDeviceFeatures2KHR) ? &features2 : nullptr)); |
| |
| VkAttachmentDescription attach_desc = {}; |
| attach_desc.format = VK_FORMAT_R8G8B8A8_UNORM; |
| attach_desc.samples = VK_SAMPLE_COUNT_1_BIT; |
| attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
| attach_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
| attach_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| attach_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| VkAttachmentReference attach_ref = {}; |
| attach_ref.attachment = 0; |
| attach_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| VkSubpassDescription subpass = {}; |
| subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| subpass.colorAttachmentCount = 1; |
| subpass.pColorAttachments = &attach_ref; |
| VkRenderPassCreateInfo rpci = {}; |
| rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
| rpci.attachmentCount = 1; |
| rpci.pAttachments = &attach_desc; |
| rpci.subpassCount = 1; |
| rpci.pSubpasses = &subpass; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-finalLayout-00843", |
| "VUID-VkAttachmentDescription2KHR-finalLayout-03061"); |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-finalLayout-00843", |
| "VUID-VkAttachmentDescription2KHR-finalLayout-03061"); |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| auto depth_format = FindSupportedDepthOnlyFormat(gpu()); |
| auto stencil_format = FindSupportedStencilOnlyFormat(gpu()); |
| auto depth_stencil_format = FindSupportedDepthStencilFormat(gpu()); |
| |
| if (separate_depth_stencil_layouts_features.separateDepthStencilLayouts) { |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-format-03286", |
| "VUID-VkAttachmentDescription2KHR-format-03300"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-format-03286", |
| "VUID-VkAttachmentDescription2KHR-format-03300"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-format-03286", |
| "VUID-VkAttachmentDescription2KHR-format-03300"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-format-03286", |
| "VUID-VkAttachmentDescription2KHR-format-03300"); |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-format-03287", |
| "VUID-VkAttachmentDescription2KHR-format-03301"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-format-03287", |
| "VUID-VkAttachmentDescription2KHR-format-03301"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-format-03287", |
| "VUID-VkAttachmentDescription2KHR-format-03301"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkAttachmentDescription-format-03287", |
| "VUID-VkAttachmentDescription2KHR-format-03301"); |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| if (depth_stencil_format) { |
| attach_desc.format = depth_stencil_format; |
| |
| if (rp2Supported) { |
| safe_VkRenderPassCreateInfo2KHR rpci2; |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| ConvertVkRenderPassCreateInfoToV2KHR(rpci, &rpci2); |
| TestRenderPass2KHRCreate(m_errorMonitor, m_device->device(), rpci2.ptr(), |
| "VUID-VkAttachmentDescription2KHR-format-03302"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| ConvertVkRenderPassCreateInfoToV2KHR(rpci, &rpci2); |
| TestRenderPass2KHRCreate(m_errorMonitor, m_device->device(), rpci2.ptr(), |
| "VUID-VkAttachmentDescription2KHR-format-03302"); |
| } else { |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03288", "VUID-VkAttachmentDescription2KHR-format-03302"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03288", "VUID-VkAttachmentDescription2KHR-format-03302"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03288", "VUID-VkAttachmentDescription2KHR-format-03302"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03288", "VUID-VkAttachmentDescription2KHR-format-03302"); |
| } |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| if (rp2Supported) { |
| safe_VkRenderPassCreateInfo2KHR rpci2; |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| ConvertVkRenderPassCreateInfoToV2KHR(rpci, &rpci2); |
| TestRenderPass2KHRCreate(m_errorMonitor, m_device->device(), rpci2.ptr(), |
| "VUID-VkAttachmentDescription2KHR-format-03303"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| ConvertVkRenderPassCreateInfoToV2KHR(rpci, &rpci2); |
| TestRenderPass2KHRCreate(m_errorMonitor, m_device->device(), rpci2.ptr(), |
| "VUID-VkAttachmentDescription2KHR-format-03303"); |
| } else { |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03289", "VUID-VkAttachmentDescription2KHR-format-03303"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03289", "VUID-VkAttachmentDescription2KHR-format-03303"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03289", "VUID-VkAttachmentDescription2KHR-format-03303"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03289", "VUID-VkAttachmentDescription2KHR-format-03303"); |
| } |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| } |
| |
| if (depth_format) { |
| attach_desc.format = depth_format; |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03290", "VUID-VkAttachmentDescription2KHR-format-03304"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03290", "VUID-VkAttachmentDescription2KHR-format-03304"); |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03291", "VUID-VkAttachmentDescription2KHR-format-03305"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03291", "VUID-VkAttachmentDescription2KHR-format-03305"); |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| } |
| |
| if (stencil_format) { |
| attach_desc.format = stencil_format; |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03292", "VUID-VkAttachmentDescription2KHR-format-03306"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03292", "VUID-VkAttachmentDescription2KHR-format-03306"); |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03293", "VUID-VkAttachmentDescription2KHR-format-03307"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-format-03293", "VUID-VkAttachmentDescription2KHR-format-03307"); |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| } |
| |
| if (rp2Supported && depth_stencil_format) { |
| attach_desc.format = depth_stencil_format; |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| |
| auto attachment_description_stencil_layout = lvl_init_struct<VkAttachmentDescriptionStencilLayoutKHR>(); |
| attachment_description_stencil_layout.stencilInitialLayout = VK_IMAGE_LAYOUT_GENERAL; |
| attachment_description_stencil_layout.stencilFinalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| safe_VkRenderPassCreateInfo2KHR rpci2; |
| ConvertVkRenderPassCreateInfoToV2KHR(rpci, &rpci2); |
| rpci2.pAttachments[0].pNext = &attachment_description_stencil_layout; |
| |
| VkImageLayout forbidden_layouts[] = { |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR, |
| VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR, |
| VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, |
| VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, |
| VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, |
| }; |
| auto forbidden_layouts_array_size = sizeof(forbidden_layouts) / sizeof(forbidden_layouts[0]); |
| |
| for (size_t i = 0; i < forbidden_layouts_array_size; ++i) { |
| attachment_description_stencil_layout.stencilInitialLayout = forbidden_layouts[i]; |
| TestRenderPass2KHRCreate(m_errorMonitor, m_device->device(), rpci2.ptr(), |
| "VUID-VkAttachmentDescriptionStencilLayoutKHR-stencilInitialLayout-03308"); |
| } |
| attachment_description_stencil_layout.stencilInitialLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR; |
| for (size_t i = 0; i < forbidden_layouts_array_size; ++i) { |
| attachment_description_stencil_layout.stencilFinalLayout = forbidden_layouts[i]; |
| TestRenderPass2KHRCreate(m_errorMonitor, m_device->device(), rpci2.ptr(), |
| "VUID-VkAttachmentDescriptionStencilLayoutKHR-stencilFinalLayout-03309"); |
| } |
| attachment_description_stencil_layout.stencilFinalLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| TestRenderPass2KHRCreate(m_errorMonitor, m_device->device(), rpci2.ptr(), |
| "VUID-VkAttachmentDescriptionStencilLayoutKHR-stencilFinalLayout-03310"); |
| attachment_description_stencil_layout.stencilFinalLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; |
| TestRenderPass2KHRCreate(m_errorMonitor, m_device->device(), rpci2.ptr(), |
| "VUID-VkAttachmentDescriptionStencilLayoutKHR-stencilFinalLayout-03310"); |
| |
| rpci2.pAttachments[0].pNext = nullptr; |
| } |
| } else { |
| if (depth_format) { |
| attach_desc.format = depth_format; |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-separateDepthStencilLayouts-03284", |
| "VUID-VkAttachmentDescription2KHR-separateDepthStencilLayouts-03298"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-separateDepthStencilLayouts-03284", |
| "VUID-VkAttachmentDescription2KHR-separateDepthStencilLayouts-03298"); |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-separateDepthStencilLayouts-03285", |
| "VUID-VkAttachmentDescription2KHR-separateDepthStencilLayouts-03299"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-separateDepthStencilLayouts-03285", |
| "VUID-VkAttachmentDescription2KHR-separateDepthStencilLayouts-03299"); |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| } |
| if (stencil_format) { |
| attach_desc.format = stencil_format; |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-separateDepthStencilLayouts-03284", |
| "VUID-VkAttachmentDescription2KHR-separateDepthStencilLayouts-03298"); |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-separateDepthStencilLayouts-03284", |
| "VUID-VkAttachmentDescription2KHR-separateDepthStencilLayouts-03298"); |
| |
| attach_desc.initialLayout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-separateDepthStencilLayouts-03285", |
| "VUID-VkAttachmentDescription2KHR-separateDepthStencilLayouts-03299"); |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL_KHR; |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkAttachmentDescription-separateDepthStencilLayouts-03285", |
| "VUID-VkAttachmentDescription2KHR-separateDepthStencilLayouts-03299"); |
| |
| attach_desc.finalLayout = VK_IMAGE_LAYOUT_GENERAL; |
| } |
| } |
| } |
| |
| TEST_F(VkLayerTest, RenderPassCreateAttachmentsMisc) { |
| TEST_DESCRIPTION( |
| "Ensure that CreateRenderPass produces the expected validation errors when a subpass's attachments violate the valid usage " |
| "conditions."); |
| |
| // Check for VK_KHR_get_physical_device_properties2 |
| if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
| m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor)); |
| bool rp2Supported = CheckCreateRenderPass2Support(this, m_device_extension_names); |
| ASSERT_NO_FATAL_FAILURE(InitState()); |
| |
| std::vector<VkAttachmentDescription> attachments = { |
| // input attachments |
| {0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_4_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL}, |
| // color attachments |
| {0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_4_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| {0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_4_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| // depth attachment |
| {0, VK_FORMAT_D24_UNORM_S8_UINT, VK_SAMPLE_COUNT_4_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}, |
| // resolve attachment |
| {0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| // preserve attachments |
| {0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_4_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| }; |
| |
| std::vector<VkAttachmentReference> input = { |
| {0, VK_IMAGE_LAYOUT_GENERAL}, |
| }; |
| std::vector<VkAttachmentReference> color = { |
| {1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| {2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| }; |
| VkAttachmentReference depth = {3, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; |
| std::vector<VkAttachmentReference> resolve = { |
| {4, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| {VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}, |
| }; |
| std::vector<uint32_t> preserve = {5}; |
| |
| VkSubpassDescription subpass = {0, |
| VK_PIPELINE_BIND_POINT_GRAPHICS, |
| (uint32_t)input.size(), |
| input.data(), |
| (uint32_t)color.size(), |
| color.data(), |
| resolve.data(), |
| &depth, |
| (uint32_t)preserve.size(), |
| preserve.data()}; |
| |
| VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, |
| nullptr, |
| 0, |
| (uint32_t)attachments.size(), |
| attachments.data(), |
| 1, |
| &subpass, |
| 0, |
| nullptr}; |
| |
| // Test too many color attachments |
| const uint32_t max_color_attachments = m_device->props.limits.maxColorAttachments; |
| const uint32_t too_big_max_attachments = 65536 + 1; // let's say this is too much to allocate |
| if (max_color_attachments >= too_big_max_attachments) { |
| printf( |
| "%s VkPhysicalDeviceLimits::maxColorAttachments is too large to practically test against -- skipping part of test.\n", |
| kSkipPrefix); |
| } else { |
| std::vector<VkAttachmentReference> too_many_colors(max_color_attachments + 1, color[0]); |
| VkSubpassDescription test_subpass = subpass; |
| test_subpass.colorAttachmentCount = (uint32_t)too_many_colors.size(); |
| test_subpass.pColorAttachments = too_many_colors.data(); |
| test_subpass.pResolveAttachments = NULL; |
| VkRenderPassCreateInfo test_rpci = rpci; |
| test_rpci.pSubpasses = &test_subpass; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &test_rpci, rp2Supported, |
| "VUID-VkSubpassDescription-colorAttachmentCount-00845", |
| "VUID-VkSubpassDescription2KHR-colorAttachmentCount-03063"); |
| } |
| |
| // Test sample count mismatch between color buffers |
| attachments[subpass.pColorAttachments[1].attachment].samples = VK_SAMPLE_COUNT_8_BIT; |
| depth.attachment = VK_ATTACHMENT_UNUSED; // Avoids triggering 01418 |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkSubpassDescription-pColorAttachments-01417", |
| "VUID-VkSubpassDescription2KHR-pColorAttachments-03069"); |
| |
| depth.attachment = 3; |
| attachments[subpass.pColorAttachments[1].attachment].samples = attachments[subpass.pColorAttachments[0].attachment].samples; |
| |
| // Test sample count mismatch between color buffers and depth buffer |
| attachments[subpass.pDepthStencilAttachment->attachment].samples = VK_SAMPLE_COUNT_8_BIT; |
| subpass.colorAttachmentCount = 1; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkSubpassDescription-pDepthStencilAttachment-01418", |
| "VUID-VkSubpassDescription2KHR-pDepthStencilAttachment-03071"); |
| |
| attachments[subpass.pDepthStencilAttachment->attachment].samples = attachments[subpass.pColorAttachments[0].attachment].samples; |
| subpass.colorAttachmentCount = (uint32_t)color.size(); |
| |
| // Test resolve attachment with UNUSED color attachment |
| color[0].attachment = VK_ATTACHMENT_UNUSED; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkSubpassDescription-pResolveAttachments-00847", |
| "VUID-VkSubpassDescription2KHR-pResolveAttachments-03065"); |
| |
| color[0].attachment = 1; |
| |
| // Test resolve from a single-sampled color attachment |
| attachments[subpass.pColorAttachments[0].attachment].samples = VK_SAMPLE_COUNT_1_BIT; |
| subpass.colorAttachmentCount = 1; // avoid mismatch (00337), and avoid double report |
| subpass.pDepthStencilAttachment = nullptr; // avoid mismatch (01418) |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkSubpassDescription-pResolveAttachments-00848", |
| "VUID-VkSubpassDescription2KHR-pResolveAttachments-03066"); |
| |
| attachments[subpass.pColorAttachments[0].attachment].samples = VK_SAMPLE_COUNT_4_BIT; |
| subpass.colorAttachmentCount = (uint32_t)color.size(); |
| subpass.pDepthStencilAttachment = &depth; |
| |
| // Test resolve to a multi-sampled resolve attachment |
| attachments[subpass.pResolveAttachments[0].attachment].samples = VK_SAMPLE_COUNT_4_BIT; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkSubpassDescription-pResolveAttachments-00849", |
| "VUID-VkSubpassDescription2KHR-pResolveAttachments-03067"); |
| |
| attachments[subpass.pResolveAttachments[0].attachment].samples = VK_SAMPLE_COUNT_1_BIT; |
| |
| // Test with color/resolve format mismatch |
| attachments[subpass.pColorAttachments[0].attachment].format = VK_FORMAT_R8G8B8A8_SRGB; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkSubpassDescription-pResolveAttachments-00850", |
| "VUID-VkSubpassDescription2KHR-pResolveAttachments-03068"); |
| |
| attachments[subpass.pColorAttachments[0].attachment].format = attachments[subpass.pResolveAttachments[0].attachment].format; |
| |
| // Test for UNUSED preserve attachments |
| preserve[0] = VK_ATTACHMENT_UNUSED; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, "VUID-VkSubpassDescription-attachment-00853", |
| "VUID-VkSubpassDescription2KHR-attachment-03073"); |
| |
| preserve[0] = 5; |
| // Test for preserve attachments used elsewhere in the subpass |
| color[0].attachment = preserve[0]; |
| |
| TestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2Supported, |
| "VUID-VkSubpassDescription-pPreserveAttachments-00854", |
| "VUID-VkSubpassDescription2KHR-pPreserveAttachments-03074"); |
| |
| color[0].attachment = 1; |
| input[0].attachment = 0; |
| input[0].layout = VK_IMAGE_LAYOUT_GENERAL; |
| |
| // Test for attachment used first as input with loadOp=CLEAR |
| { |
| std::vector<VkSubpassDescription> subpasses = {subpass, subpass, subpass}; |
| subpasses[0].inputAttachmentCount = 0; |
| |