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