tests: GH431 Add layer checks and tests for push constant

Change-Id: I46eb5f9adbe702a278cded0f73dc18d1617489f0

Fix windows compilation issues

Change-Id: Ic9b9795e34d1e1e68bae9838b8538bb2e37d18b4
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index 74d44b2..9c5b149 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -3684,61 +3684,236 @@
 }
 
 TEST_F(VkLayerTest, InvalidPushConstants) {
-    // Hit push constant error cases:
-    // 1. Create PipelineLayout where push constant overstep maxPushConstantSize
-    // 2. Incorrectly set push constant size to 0
-    // 3. Incorrectly set push constant size to non-multiple of 4
-    // 4. Attempt push constant update that exceeds maxPushConstantSize
     VkResult err;
-    m_errorMonitor->SetDesiredFailureMsg(
-        VK_DEBUG_REPORT_ERROR_BIT_EXT,
-        "vkCreatePipelineLayout() call has push constants with offset ");
-
     ASSERT_NO_FATAL_FAILURE(InitState());
     ASSERT_NO_FATAL_FAILURE(InitViewport());
     ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
 
+    VkPipelineLayout pipeline_layout;
     VkPushConstantRange pc_range = {};
-    pc_range.size = 0xFFFFFFFFu;
     pc_range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
     VkPipelineLayoutCreateInfo pipeline_layout_ci = {};
     pipeline_layout_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
     pipeline_layout_ci.pushConstantRangeCount = 1;
     pipeline_layout_ci.pPushConstantRanges = &pc_range;
 
-    VkPipelineLayout pipeline_layout;
-    err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL,
-                                 &pipeline_layout);
+    //
+    // Check for invalid push constant ranges in pipeline layouts.
+    //
+    struct PipelineLayoutTestCase {
+        uint32_t const offset;
+        uint32_t const size;
+        char const *msg;
+    };
 
-    m_errorMonitor->VerifyFound();
-    // Now cause errors due to size 0 and non-4 byte aligned size
-    pc_range.size = 0;
+    const uint32_t tooBig = m_device->props.limits.maxPushConstantsSize + 0x4;
+    const std::array<PipelineLayoutTestCase, 10> rangeTests = {{
+        {0, 0, "vkCreatePipelineLayout() call has push constants index 0 with "
+               "size 0."},
+        {0, 1, "vkCreatePipelineLayout() call has push constants index 0 with "
+               "size 1."},
+        {1, 1, "vkCreatePipelineLayout() call has push constants index 0 with "
+               "size 1."},
+        {1, 0, "vkCreatePipelineLayout() call has push constants index 0 with "
+               "size 0."},
+        {1, 4, "vkCreatePipelineLayout() call has push constants index 0 with "
+               "offset 1. Offset must"},
+        {0, tooBig, "vkCreatePipelineLayout() call has push constants index 0 "
+                    "with offset "},
+        {tooBig, tooBig, "vkCreatePipelineLayout() call has push constants "
+                         "index 0 with offset "},
+        {tooBig, 0, "vkCreatePipelineLayout() call has push constants index 0 "
+                    "with offset "},
+        {0xFFFFFFF0, 0x00000020, "vkCreatePipelineLayout() call has push "
+                                 "constants index 0 with offset "},
+        {0x00000020, 0xFFFFFFF0, "vkCreatePipelineLayout() call has push "
+                                 "constants index 0 with offset "},
+    }};
+
+    // Check for invalid offset and size
+    for (const auto &iter : rangeTests) {
+        pc_range.offset = iter.offset;
+        pc_range.size = iter.size;
+        m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
+                                             iter.msg);
+        err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci,
+                                     NULL, &pipeline_layout);
+        m_errorMonitor->VerifyFound();
+        if (VK_SUCCESS == err) {
+            vkDestroyPipelineLayout(m_device->device(), pipeline_layout, NULL);
+        }
+    }
+
+    // Check for invalid stage flag
+    pc_range.offset = 0;
+    pc_range.size = 16;
+    pc_range.stageFlags = 0;
     m_errorMonitor->SetDesiredFailureMsg(
         VK_DEBUG_REPORT_ERROR_BIT_EXT,
-        "vkCreatePipelineLayout() call has push constant index 0 with size 0");
+        "vkCreatePipelineLayout() call has no stageFlags set.");
     err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL,
                                  &pipeline_layout);
     m_errorMonitor->VerifyFound();
-    pc_range.size = 1;
-    m_errorMonitor->SetDesiredFailureMsg(
-        VK_DEBUG_REPORT_ERROR_BIT_EXT,
-        "vkCreatePipelineLayout() call has push constant index 0 with size 1");
-    err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL,
-                                 &pipeline_layout);
-    m_errorMonitor->VerifyFound();
-    // Cause error due to bad size in vkCmdPushConstants() call
-    m_errorMonitor->SetDesiredFailureMsg(
-        VK_DEBUG_REPORT_ERROR_BIT_EXT,
-        "vkCmdPushConstants() call has push constants with offset ");
-    pipeline_layout_ci.pushConstantRangeCount = 0;
-    pipeline_layout_ci.pPushConstantRanges = NULL;
+    if (VK_SUCCESS == err) {
+        vkDestroyPipelineLayout(m_device->device(), pipeline_layout, NULL);
+    }
+
+    // Check for overlapping ranges
+    const uint32_t rangesPerTest = 5;
+    struct OverlappingRangeTestCase {
+        VkPushConstantRange ranges[rangesPerTest];
+        char const *msg;
+    };
+
+    const std::array<OverlappingRangeTestCase, 5> orTests = {
+        {{{{VK_SHADER_STAGE_VERTEX_BIT, 0, 4},
+           {VK_SHADER_STAGE_VERTEX_BIT, 0, 4},
+           {VK_SHADER_STAGE_VERTEX_BIT, 0, 4},
+           {VK_SHADER_STAGE_VERTEX_BIT, 0, 4},
+           {VK_SHADER_STAGE_VERTEX_BIT, 0, 4}},
+          "vkCreatePipelineLayout() call has push constants with overlapping "
+          "ranges: 0:[0, 4), 1:[0, 4)"},
+         {
+             {{VK_SHADER_STAGE_VERTEX_BIT, 0, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 4, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 8, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 12, 8},
+              {VK_SHADER_STAGE_VERTEX_BIT, 16, 4}},
+             "vkCreatePipelineLayout() call has push constants with "
+             "overlapping "
+             "ranges: 3:[12, 20), 4:[16, 20)",
+         },
+         {
+             {{VK_SHADER_STAGE_VERTEX_BIT, 16, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 12, 8},
+              {VK_SHADER_STAGE_VERTEX_BIT, 8, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 4, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 0, 4}},
+             "vkCreatePipelineLayout() call has push constants with "
+             "overlapping "
+             "ranges: 0:[16, 20), 1:[12, 20)",
+         },
+         {
+             {{VK_SHADER_STAGE_VERTEX_BIT, 16, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 8, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 4, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 12, 8},
+              {VK_SHADER_STAGE_VERTEX_BIT, 0, 4}},
+             "vkCreatePipelineLayout() call has push constants with "
+             "overlapping "
+             "ranges: 0:[16, 20), 3:[12, 20)",
+         },
+         {
+             {{VK_SHADER_STAGE_VERTEX_BIT, 16, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 32, 4},
+              {VK_SHADER_STAGE_VERTEX_BIT, 4, 96},
+              {VK_SHADER_STAGE_VERTEX_BIT, 40, 8},
+              {VK_SHADER_STAGE_VERTEX_BIT, 52, 4}},
+             "vkCreatePipelineLayout() call has push constants with "
+             "overlapping "
+             "ranges: 0:[16, 20), 2:[4, 100)",
+         }}};
+    pipeline_layout_ci.pushConstantRangeCount = rangesPerTest;
+
+    for (const auto &iter : orTests) {
+        pipeline_layout_ci.pPushConstantRanges = iter.ranges;
+        m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
+                                             iter.msg);
+        err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci,
+                                     NULL, &pipeline_layout);
+        m_errorMonitor->VerifyFound();
+        if (VK_SUCCESS == err) {
+            vkDestroyPipelineLayout(m_device->device(), pipeline_layout, NULL);
+        }
+    }
+
+    // Run some positive tests to make sure overlap checking in the layer is OK
+    const std::array<OverlappingRangeTestCase, 2> orPosTests = {
+        {{{{VK_SHADER_STAGE_VERTEX_BIT, 0, 4},
+           {VK_SHADER_STAGE_VERTEX_BIT, 4, 4},
+           {VK_SHADER_STAGE_VERTEX_BIT, 8, 4},
+           {VK_SHADER_STAGE_VERTEX_BIT, 12, 4},
+           {VK_SHADER_STAGE_VERTEX_BIT, 16, 4}},
+          ""},
+         {{{VK_SHADER_STAGE_VERTEX_BIT, 92, 24},
+           {VK_SHADER_STAGE_VERTEX_BIT, 80, 4},
+           {VK_SHADER_STAGE_VERTEX_BIT, 64, 8},
+           {VK_SHADER_STAGE_VERTEX_BIT, 4, 16},
+           {VK_SHADER_STAGE_VERTEX_BIT, 0, 4}},
+          ""}}};
+    for (const auto &iter : orPosTests) {
+        pipeline_layout_ci.pPushConstantRanges = iter.ranges;
+        m_errorMonitor->ExpectSuccess();
+        err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci,
+                                     NULL, &pipeline_layout);
+        m_errorMonitor->VerifyNotFound();
+        if (VK_SUCCESS == err) {
+            vkDestroyPipelineLayout(m_device->device(), pipeline_layout, NULL);
+        }
+    }
+
+    //
+    // CmdPushConstants tests
+    //
+
+    // Check for invalid offset and size and if range is within layout range(s)
+    std::array<PipelineLayoutTestCase, 13> cmdRangeTests = {{
+        {0, 0, "vkCmdPushConstants() call has push constants with size 0. Size "
+               "must be greater than zero and a multiple of 4."},
+        {0, 1, "vkCmdPushConstants() call has push constants with size 1. Size "
+               "must be greater than zero and a multiple of 4."},
+        {1, 1, "vkCmdPushConstants() call has push constants with size 1. Size "
+               "must be greater than zero and a multiple of 4."},
+        {1, 0, "vkCmdPushConstants() call has push constants with offset 1. "
+               "Offset must be a multiple of 4."},
+        {1, 4, "vkCmdPushConstants() call has push constants with offset 1. "
+               "Offset must be a multiple of 4."},
+        {0, 20, "vkCmdPushConstants() Push constant range [0, 20) not within "
+                "any of the ranges in pipeline layout"},
+        {60, 8, "vkCmdPushConstants() Push constant range [60, 68) not within "
+                "any of the ranges in pipeline layout"},
+        {76, 8, "vkCmdPushConstants() Push constant range [76, 84) not within "
+                "any of the ranges in pipeline layout"},
+        {0, tooBig,
+         "vkCmdPushConstants() call has push constants with offset "},
+        {tooBig, tooBig,
+         "vkCmdPushConstants() call has push constants with offset "},
+        {tooBig, 0,
+         "vkCmdPushConstants() call has push constants with offset "},
+        {0xFFFFFFF0, 0x00000020,
+         "vkCmdPushConstants() call has push constants with offset "},
+        {0x00000020, 0xFFFFFFF0,
+         "vkCmdPushConstants() call has push constants with offset "},
+    }};
+
+    // Two ranges for testing robustness
+    VkPushConstantRange pc_range2[] = {
+        {VK_SHADER_STAGE_VERTEX_BIT, 0, 16},
+        {VK_SHADER_STAGE_VERTEX_BIT, 64, 16},
+    };
+    pipeline_layout_ci.pushConstantRangeCount = 2;
+    pipeline_layout_ci.pPushConstantRanges = pc_range2;
     err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, NULL,
                                  &pipeline_layout);
     ASSERT_VK_SUCCESS(err);
     BeginCommandBuffer();
-    vkCmdPushConstants(m_commandBuffer->GetBufferHandle(), pipeline_layout,
-                       VK_SHADER_STAGE_VERTEX_BIT, 0, 0xFFFFFFFFu, NULL);
+    for (const auto &iter : cmdRangeTests) {
+        m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
+                                             iter.msg);
+        vkCmdPushConstants(m_commandBuffer->GetBufferHandle(), pipeline_layout,
+                           VK_SHADER_STAGE_VERTEX_BIT, iter.offset, iter.size,
+                           NULL);
+        m_errorMonitor->VerifyFound();
+    }
+
+    // Check for invalid stage flag
+    m_errorMonitor->SetDesiredFailureMsg(
+        VK_DEBUG_REPORT_ERROR_BIT_EXT,
+        "vkCmdPushConstants() call has no stageFlags set.");
+    vkCmdPushConstants(m_commandBuffer->GetBufferHandle(), pipeline_layout, 0,
+                       0, 16, NULL);
     m_errorMonitor->VerifyFound();
+
     vkDestroyPipelineLayout(m_device->device(), pipeline_layout, NULL);
 }