blob: 7e74eb752441802c71d39f8ebfddcde7e961d180 [file] [log] [blame]
/*
* 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;