test: GH422 - Buffer Validation Tests
Add classes to support vertex buffer creation and binding
to the pipe and command buffer. Create VertexBufferInvalid
Test to verify INVALID_BUFFER case.
Change-Id: Ief6cd6a86522f9c23db7b641a613cf53cb907012
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index 0cd41d6..bba6907 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -252,7 +252,7 @@
m_commandBuffer->DrawIndexed(indexCount, instanceCount, firstIndex,
vertexOffset, firstInstance);
}
- void QueueCommandBuffer() { m_commandBuffer->QueueCommandBuffer(); }
+ void QueueCommandBuffer(bool checkSuccess = true) { m_commandBuffer->QueueCommandBuffer(checkSuccess); }
void QueueCommandBuffer(const VkFence &fence) {
m_commandBuffer->QueueCommandBuffer(fence);
}
@@ -9390,6 +9390,64 @@
vkDestroyDescriptorSetLayout(m_device->device(), ds_layout, NULL);
vkDestroyDescriptorPool(m_device->device(), ds_pool, NULL);
}
+
+TEST_F(VkLayerTest, VertexBufferInvalid) {
+ TEST_DESCRIPTION("Submit a command buffer using deleted vertex buffer");
+ m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
+ "Cannot submit cmd buffer using deleted buffer ");
+
+ ASSERT_NO_FATAL_FAILURE(InitState());
+ ASSERT_NO_FATAL_FAILURE(InitViewport());
+ ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
+
+ VkPipelineMultisampleStateCreateInfo pipe_ms_state_ci = {};
+ pipe_ms_state_ci.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ pipe_ms_state_ci.pNext = NULL;
+ pipe_ms_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ pipe_ms_state_ci.sampleShadingEnable = 0;
+ pipe_ms_state_ci.minSampleShading = 1.0;
+ pipe_ms_state_ci.pSampleMask = nullptr;
+
+ VkPipelineLayoutCreateInfo pipeline_layout_ci = {};
+ pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ VkPipelineLayout pipeline_layout;
+
+ VkResult err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, nullptr, &pipeline_layout);
+ 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); // We shouldn't need a fragment shader
+ // but add it to be able to run on more devices
+ VkPipelineObj pipe(m_device);
+ pipe.AddShader(&vs);
+ pipe.AddShader(&fs);
+ pipe.AddColorAttachment();
+ pipe.SetMSAA(&pipe_ms_state_ci);
+ pipe.SetViewport(m_viewports);
+ pipe.SetScissor(m_scissors);
+ pipe.CreateVKPipeline(pipeline_layout, renderPass());
+
+ BeginCommandBuffer();
+ vkCmdBindPipeline(m_commandBuffer->GetBufferHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle());
+
+ {
+ // Create and bind a vertex buffer in a reduced scope, which will cause it to be deleted opon leaving this scope
+ static const float vbo_data[3] = {1.f, 0.f, 1.f};
+ cVertices draw_verticies(m_device, 1, 1, sizeof(vbo_data), 3, vbo_data);
+ draw_verticies.BindVertexBuffers(m_commandBuffer->handle());
+ draw_verticies.AddVertexInputToPipe(pipe);
+ }
+
+ Draw(1, 0, 0, 0);
+
+ EndCommandBuffer();
+ QueueCommandBuffer(false);
+ m_errorMonitor->VerifyFound();
+
+ vkDestroyPipelineLayout(m_device->device(), pipeline_layout, NULL);
+}
+
// INVALID_IMAGE_LAYOUT tests (one other case is hit by MapMemWithoutHostVisibleBit and not here)
TEST_F(VkLayerTest, InvalidImageLayout) {
TEST_DESCRIPTION("Hit all possible validation checks associated with the "
diff --git a/tests/vkrenderframework.cpp b/tests/vkrenderframework.cpp
index 1723655..edd8cf3 100644
--- a/tests/vkrenderframework.cpp
+++ b/tests/vkrenderframework.cpp
@@ -1237,13 +1237,12 @@
}
void VkPipelineObj::AddVertexInputAttribs(
- VkVertexInputAttributeDescription *vi_attrib, int count) {
+ VkVertexInputAttributeDescription *vi_attrib, unsigned count) {
m_vi_state.pVertexAttributeDescriptions = vi_attrib;
m_vi_state.vertexAttributeDescriptionCount = count;
}
-void VkPipelineObj::AddVertexInputBindings(
- VkVertexInputBindingDescription *vi_binding, int count) {
+void VkPipelineObj::AddVertexInputBindings(VkVertexInputBindingDescription *vi_binding, unsigned count) {
m_vi_state.pVertexBindingDescriptions = vi_binding;
m_vi_state.vertexBindingDescriptionCount = count;
}
@@ -1640,12 +1639,12 @@
vkCmdDraw(handle(), vertexCount, instanceCount, firstVertex, firstInstance);
}
-void VkCommandBufferObj::QueueCommandBuffer() {
+void VkCommandBufferObj::QueueCommandBuffer(bool checkSuccess) {
VkFence nullFence = {VK_NULL_HANDLE};
- QueueCommandBuffer(nullFence);
+ QueueCommandBuffer(nullFence, checkSuccess);
}
-void VkCommandBufferObj::QueueCommandBuffer(VkFence fence) {
+void VkCommandBufferObj::QueueCommandBuffer(VkFence fence, bool checkSuccess) {
VkResult err = VK_SUCCESS;
// submit the command buffer to the universal queue
@@ -1661,10 +1660,14 @@
submit_info.pSignalSemaphores = NULL;
err = vkQueueSubmit(m_device->m_queue, 1, &submit_info, fence);
- ASSERT_VK_SUCCESS(err);
+ if (checkSuccess) {
+ ASSERT_VK_SUCCESS(err);
+ }
err = vkQueueWaitIdle(m_device->m_queue);
- ASSERT_VK_SUCCESS(err);
+ if (checkSuccess) {
+ ASSERT_VK_SUCCESS(err);
+ }
// Wait for work to finish before cleaning up.
vkDeviceWaitIdle(m_device->device());
@@ -1739,3 +1742,5 @@
m_attachmentBindInfo = m_imageView.handle();
}
+
+unsigned cVertices::BindIdGenerator;
diff --git a/tests/vkrenderframework.h b/tests/vkrenderframework.h
index 336d0e6..5ffc65a 100644
--- a/tests/vkrenderframework.h
+++ b/tests/vkrenderframework.h
@@ -185,8 +185,8 @@
void DrawIndexed(uint32_t indexCount, uint32_t instanceCount,
uint32_t firstIndex, int32_t vertexOffset,
uint32_t firstInstance);
- void QueueCommandBuffer();
- void QueueCommandBuffer(VkFence fence);
+ void QueueCommandBuffer(bool checkSuccess = true);
+ void QueueCommandBuffer(VkFence fence, bool checkSuccess = true);
void SetViewport(uint32_t firstViewport, uint32_t viewportCount,
const VkViewport *pViewports);
void SetScissor(uint32_t firstScissor, uint32_t scissorCount,
@@ -411,9 +411,9 @@
VkPipelineObj(VkDeviceObj *device);
void AddShader(VkShaderObj *shaderObj);
void AddVertexInputAttribs(VkVertexInputAttributeDescription *vi_attrib,
- int count);
+ unsigned count);
void AddVertexInputBindings(VkVertexInputBindingDescription *vi_binding,
- int count);
+ unsigned count);
void AddColorAttachment(uint32_t binding,
const VkPipelineColorBlendAttachmentState *att);
void MakeDynamic(VkDynamicState state);
@@ -453,4 +453,164 @@
vector<VkPipelineColorBlendAttachmentState> m_colorAttachments;
int m_vertexBufferCount;
};
+class cMemoryBuffer {
+public:
+ cMemoryBuffer(VkDeviceObj *aVulkanDevice, VkBufferUsageFlags aBufferUsage, VkDeviceSize &aBufferByteCount, const float *aClientData) :
+ AllocateCurrent(false),
+ BoundCurrent(false),
+ CreateCurrent(false),
+ VulkanDevice(aVulkanDevice->device()) {
+
+ VkBufferCreateInfo buffer_create_info = {};
+ buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ buffer_create_info.size = aBufferByteCount;
+ buffer_create_info.usage = aBufferUsage;
+
+ VkResult returnValue = vkCreateBuffer(VulkanDevice, &buffer_create_info, nullptr, &VulkanBuffer);
+ assert(!returnValue);
+ CreateCurrent = true;
+
+ VkMemoryRequirements memory_requirements;
+ vkGetBufferMemoryRequirements(VulkanDevice, VulkanBuffer, &memory_requirements);
+
+ VkMemoryAllocateInfo memory_allocate_info = {};
+ memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ memory_allocate_info.allocationSize = memory_requirements.size;
+ bool pass = aVulkanDevice->phy().set_memory_type(memory_requirements.memoryTypeBits, &memory_allocate_info, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+ if (!pass) {
+ vkDestroyBuffer(VulkanDevice, VulkanBuffer, nullptr);
+ return;
+ }
+
+ returnValue = vkAllocateMemory(VulkanDevice, &memory_allocate_info, NULL, &VulkanMemory);
+ assert(!returnValue);
+ AllocateCurrent = true;
+
+ void *mappedMemory;
+ returnValue = vkMapMemory(VulkanDevice, VulkanMemory, 0, memory_allocate_info.allocationSize, 0, &mappedMemory);
+ assert(!returnValue);
+
+ memcpy(mappedMemory, aClientData, static_cast<size_t>(aBufferByteCount));
+
+ vkUnmapMemory(VulkanDevice, VulkanMemory);
+
+ returnValue = vkBindBufferMemory(VulkanDevice, VulkanBuffer, VulkanMemory, 0);
+ assert(!returnValue);
+ BoundCurrent = true;
+ }
+
+ ~cMemoryBuffer() {
+ if (CreateCurrent) {
+ vkDestroyBuffer(VulkanDevice, VulkanBuffer, nullptr);
+ }
+ if (AllocateCurrent) {
+ vkFreeMemory(VulkanDevice, VulkanMemory, nullptr);
+ }
+ }
+
+ const VkBuffer &GetBuffer() {
+ return VulkanBuffer;
+ }
+
+protected:
+ bool AllocateCurrent;
+ bool BoundCurrent;
+ bool CreateCurrent;
+
+ VkBuffer VulkanBuffer;
+ VkDevice VulkanDevice;
+ VkDeviceMemory VulkanMemory;
+
+};
+
+class cVertices {
+public:
+ cVertices(VkDeviceObj *aVulkanDevice, unsigned aAttributeCount, unsigned aBindingCount, unsigned aByteStride, VkDeviceSize aVertexCount, const float *aVerticies) :
+ BoundCurrent(false),
+ AttributeCount(aAttributeCount),
+ BindingCount(aBindingCount),
+ BindId(BindIdGenerator),
+ PipelineVertexInputStateCreateInfo(),
+ VulkanDevice(aVulkanDevice->device()),
+ VulkanMemoryBuffer(aVulkanDevice, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, aVertexCount, aVerticies) {
+ BindIdGenerator++; // NB: This can wrap w/misuse
+ VertexInputAttributeDescription = new VkVertexInputAttributeDescription[AttributeCount];
+ VertexInputBindingDescription = new VkVertexInputBindingDescription[BindingCount];
+
+ PipelineVertexInputStateCreateInfo.pVertexAttributeDescriptions = VertexInputAttributeDescription;
+ PipelineVertexInputStateCreateInfo.vertexAttributeDescriptionCount = AttributeCount;
+ PipelineVertexInputStateCreateInfo.pVertexBindingDescriptions = VertexInputBindingDescription;
+ PipelineVertexInputStateCreateInfo.vertexBindingDescriptionCount = BindingCount;
+ PipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+
+ unsigned i = 0;
+ do {
+ VertexInputAttributeDescription[i].binding = BindId;
+ VertexInputAttributeDescription[i].location = i;
+ VertexInputAttributeDescription[i].format = VK_FORMAT_R32G32B32_SFLOAT;
+ VertexInputAttributeDescription[i].offset = sizeof(float) * aByteStride;
+ i++;
+ } while (AttributeCount < i);
+
+ i = 0;
+ do {
+ VertexInputBindingDescription[i].binding = BindId;
+ VertexInputBindingDescription[i].stride = aByteStride;
+ VertexInputBindingDescription[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+ i++;
+ } while (BindingCount < i);
+ }
+
+ ~cVertices() {
+ if (VertexInputAttributeDescription) {
+ delete[] VertexInputAttributeDescription;
+ }
+ if (VertexInputBindingDescription) {
+ delete[] VertexInputBindingDescription;
+ }
+ }
+
+ void BindVertexBuffers(VkCommandBuffer aCommandBuffer, unsigned aOffsetCount = 0, VkDeviceSize *aOffsetList = nullptr) {
+ VkDeviceSize *offsetList;
+ unsigned offsetCount;
+
+ if (aOffsetCount) {
+ offsetList = aOffsetList;
+ offsetCount = aOffsetCount;
+ }
+ else {
+ offsetList = new VkDeviceSize[1]();
+ offsetCount = 1;
+ }
+
+ vkCmdBindVertexBuffers(aCommandBuffer, BindId, offsetCount, &VulkanMemoryBuffer.GetBuffer(), offsetList);
+ BoundCurrent = true;
+
+ if (!aOffsetCount) {
+ delete [] offsetList;
+ }
+ }
+
+ void AddVertexInputToPipe(VkPipelineObj &aPipelineObj) {
+ aPipelineObj.AddVertexInputAttribs(VertexInputAttributeDescription, AttributeCount);
+ aPipelineObj.AddVertexInputBindings(VertexInputBindingDescription, BindingCount);
+ }
+
+protected:
+ static unsigned BindIdGenerator;
+
+ bool BoundCurrent;
+ unsigned AttributeCount;
+ unsigned BindingCount;
+ uint32_t BindId;
+ VkDeviceMemory DeviceMemory;
+
+ VkPipelineVertexInputStateCreateInfo PipelineVertexInputStateCreateInfo;
+ VkVertexInputAttributeDescription *VertexInputAttributeDescription;
+ VkVertexInputBindingDescription *VertexInputBindingDescription;
+ VkDevice VulkanDevice;
+ cMemoryBuffer VulkanMemoryBuffer;
+};
+
+
#endif // VKRENDERFRAMEWORK_H