tests: Add InvalidCmdBufferDescriptorSetBufferDestroyed test
This test creates a cmd buffer and binds a buffer-type descriptor set.
Then it detroys the buffer prior to submitting cmd buffer and verifies
that the cmd buffer is flagged as invalid.
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index 4426597..d207b55 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -7738,6 +7738,172 @@
vkDestroyPipelineLayout(m_device->device(), pipeline_layout, NULL);
}
+TEST_F(VkLayerTest, InvalidCmdBufferDescriptorSetBufferDestroyed) {
+ TEST_DESCRIPTION("Attempt to draw with a command buffer that is invalid "
+ "due to a bound descriptor set with a buffer dependency "
+ "being destroyed.");
+ ASSERT_NO_FATAL_FAILURE(InitState());
+ ASSERT_NO_FATAL_FAILURE(InitViewport());
+ ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
+
+ VkDescriptorPoolSize ds_type_count = {};
+ ds_type_count.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ 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.maxSets = 1;
+ ds_pool_ci.poolSizeCount = 1;
+ ds_pool_ci.pPoolSizes = &ds_type_count;
+
+ VkDescriptorPool ds_pool;
+ VkResult err =
+ vkCreateDescriptorPool(m_device->device(), &ds_pool_ci, NULL, &ds_pool);
+ ASSERT_VK_SUCCESS(err);
+
+ VkDescriptorSetLayoutBinding dsl_binding = {};
+ dsl_binding.binding = 0;
+ dsl_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ dsl_binding.descriptorCount = 1;
+ dsl_binding.stageFlags = VK_SHADER_STAGE_ALL;
+ dsl_binding.pImmutableSamplers = NULL;
+
+ VkDescriptorSetLayoutCreateInfo ds_layout_ci = {};
+ ds_layout_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ ds_layout_ci.pNext = NULL;
+ ds_layout_ci.bindingCount = 1;
+ ds_layout_ci.pBindings = &dsl_binding;
+ VkDescriptorSetLayout ds_layout;
+ err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, NULL,
+ &ds_layout);
+ ASSERT_VK_SUCCESS(err);
+
+ VkDescriptorSet descriptorSet;
+ VkDescriptorSetAllocateInfo alloc_info = {};
+ alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ alloc_info.descriptorSetCount = 1;
+ alloc_info.descriptorPool = ds_pool;
+ alloc_info.pSetLayouts = &ds_layout;
+ err = vkAllocateDescriptorSets(m_device->device(), &alloc_info,
+ &descriptorSet);
+ ASSERT_VK_SUCCESS(err);
+
+ VkPipelineLayoutCreateInfo pipeline_layout_ci = {};
+ pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipeline_layout_ci.pNext = NULL;
+ pipeline_layout_ci.setLayoutCount = 1;
+ pipeline_layout_ci.pSetLayouts = &ds_layout;
+
+ VkPipelineLayout pipeline_layout;
+ err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL,
+ &pipeline_layout);
+ ASSERT_VK_SUCCESS(err);
+
+ // Create a buffer to update the descriptor with
+ uint32_t qfi = 0;
+ VkBufferCreateInfo buffCI = {};
+ buffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ buffCI.size = 1024;
+ buffCI.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ buffCI.queueFamilyIndexCount = 1;
+ buffCI.pQueueFamilyIndices = &qfi;
+
+ VkBuffer buffer;
+ err = vkCreateBuffer(m_device->device(), &buffCI, NULL, &buffer);
+ ASSERT_VK_SUCCESS(err);
+ // Allocate memory and bind to buffer so we can make it to the appropriate
+ // error
+ VkMemoryAllocateInfo mem_alloc = {};
+ mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ mem_alloc.pNext = NULL;
+ mem_alloc.allocationSize = 1024;
+ mem_alloc.memoryTypeIndex = 0;
+
+ VkMemoryRequirements memReqs;
+ vkGetBufferMemoryRequirements(m_device->device(), buffer, &memReqs);
+ bool pass =
+ m_device->phy().set_memory_type(memReqs.memoryTypeBits, &mem_alloc, 0);
+ if (!pass) {
+ vkDestroyBuffer(m_device->device(), buffer, NULL);
+ return;
+ }
+
+ VkDeviceMemory mem;
+ err = vkAllocateMemory(m_device->device(), &mem_alloc, NULL, &mem);
+ ASSERT_VK_SUCCESS(err);
+ err = vkBindBufferMemory(m_device->device(), buffer, mem, 0);
+ ASSERT_VK_SUCCESS(err);
+ // Correctly update descriptor to avoid "NOT_UPDATED" error
+ VkDescriptorBufferInfo buffInfo = {};
+ buffInfo.buffer = buffer;
+ buffInfo.offset = 0;
+ buffInfo.range = 1024;
+
+ VkWriteDescriptorSet descriptor_write;
+ memset(&descriptor_write, 0, sizeof(descriptor_write));
+ descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descriptor_write.dstSet = descriptorSet;
+ descriptor_write.dstBinding = 0;
+ descriptor_write.descriptorCount = 1;
+ descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ descriptor_write.pBufferInfo = &buffInfo;
+
+ vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
+
+ // Create PSO to be used for draw-time errors below
+ char const *vsSource = "#version 450\n"
+ "\n"
+ "out gl_PerVertex { \n"
+ " vec4 gl_Position;\n"
+ "};\n"
+ "void main(){\n"
+ " gl_Position = vec4(1);\n"
+ "}\n";
+ char const *fsSource =
+ "#version 450\n"
+ "\n"
+ "layout(location=0) out vec4 x;\n"
+ "layout(set=0) layout(binding=0) uniform foo { int x; int y; } bar;\n"
+ "void main(){\n"
+ " x = vec4(bar.y);\n"
+ "}\n";
+ VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
+ VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
+ VkPipelineObj pipe(m_device);
+ pipe.AddShader(&vs);
+ pipe.AddShader(&fs);
+ pipe.AddColorAttachment();
+ pipe.CreateVKPipeline(pipeline_layout, renderPass());
+
+ BeginCommandBuffer();
+ vkCmdBindPipeline(m_commandBuffer->GetBufferHandle(),
+ VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle());
+ vkCmdBindDescriptorSets(m_commandBuffer->GetBufferHandle(),
+ VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0,
+ 1, &descriptorSet, 0, NULL);
+ Draw(1, 0, 0, 0);
+ EndCommandBuffer();
+ m_errorMonitor->SetDesiredFailureMsg(
+ VK_DEBUG_REPORT_ERROR_BIT_EXT,
+ " that is invalid because bound buffer ");
+ // Destroy buffer should invalidate the cmd buffer, causing error on submit
+ vkDestroyBuffer(m_device->device(), buffer, NULL);
+ // Attempt to submit cmd buffer
+ VkSubmitInfo submit_info = {};
+ submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submit_info.commandBufferCount = 1;
+ submit_info.pCommandBuffers = &m_commandBuffer->handle();
+ vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
+ m_errorMonitor->VerifyFound();
+ // Cleanup
+ vkFreeMemory(m_device->device(), mem, NULL);
+
+ vkDestroyPipelineLayout(m_device->device(), pipeline_layout, NULL);
+ vkDestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
+ vkDestroyDescriptorPool(m_device->device(), ds_pool, NULL);
+}
+
TEST_F(VkLayerTest, InvalidPipeline) {
// Attempt to bind an invalid Pipeline to a valid Command Buffer
// ObjectTracker should catch this.