1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2022 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Robust Index Buffer Access Tests
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktRobustnessIndexAccessTests.hpp"
25 #include "vkBufferWithMemory.hpp"
26 #include "vkImageWithMemory.hpp"
27 #include "vktRobustnessUtil.hpp"
28 #include "vktTestCaseUtil.hpp"
29 #include "vkBuilderUtil.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkMemUtil.hpp"
32 #include "vkPrograms.hpp"
33 #include "vkQueryUtil.hpp"
34 #include "vkDeviceUtil.hpp"
35 #include "vkBarrierUtil.hpp"
36 #include "vkRef.hpp"
37 #include "vkRefUtil.hpp"
38 #include "vkTypeUtil.hpp"
39 #include "vkObjUtil.hpp"
40 #include "vkCmdUtil.hpp"
41 #include "tcuTestLog.hpp"
42 #include "deMath.h"
43 #include "tcuVectorUtil.hpp"
44 #include "deUniquePtr.hpp"
45 #include <algorithm>
46 #include <numeric>
47 #include <tuple>
48 #include <vector>
49 
50 namespace vkt
51 {
52 namespace robustness
53 {
54 
55 using namespace vk;
56 
57 #ifndef CTS_USES_VULKANSC
58 typedef de::MovePtr<vk::DeviceDriver> DeviceDriverPtr;
59 #else
60 typedef de::MovePtr<vk::DeviceDriverSC, vk::DeinitDeviceDeleter> DeviceDriverPtr;
61 #endif // CTS_USES_VULKANSC
62 
63 enum TestMode
64 {
65     TM_DRAW_INDEXED = 0,
66     TM_DRAW_INDEXED_INDIRECT,
67     TM_DRAW_INDEXED_INDIRECT_COUNT,
68     TM_DRAW_MULTI_INDEXED,
69 };
70 
71 enum OOTypes
72 {
73     OO_NONE,
74     OO_INDEX,
75     OO_SIZE,
76     OO_WHOLE_SIZE
77 };
78 
79 struct TestParams
80 {
81     TestMode mode;
82     OOTypes ooType;
83     uint32_t leadingCount;
84 };
85 
86 class DrawIndexedInstance : public vkt::TestInstance
87 {
88 public:
89     DrawIndexedInstance(Context &context,
90 #ifdef CTS_USES_VULKANSC
91                         de::MovePtr<CustomInstance> customInstance,
92 #endif // CTS_USES_VULKANSC
93                         Move<VkDevice> device, DeviceDriverPtr deviceDriver, TestMode mode, uint32_t robustnessVersion);
94 
95     virtual ~DrawIndexedInstance(void) = default;
96 
97     virtual tcu::TestStatus iterate(void);
98 
99 protected:
100 #ifdef CTS_USES_VULKANSC
101     de::MovePtr<CustomInstance> m_customInstance;
102 #endif // CTS_USES_VULKANSC
103     Move<VkDevice> m_device;
104     DeviceDriverPtr m_deviceDriver;
105     TestMode m_mode;
106     uint32_t m_robustnessVersion;
107 };
108 
DrawIndexedInstance(Context & context,de::MovePtr<CustomInstance> customInstance,Move<VkDevice> device,DeviceDriverPtr deviceDriver,TestMode mode,uint32_t robustnessVersion)109 DrawIndexedInstance::DrawIndexedInstance(Context &context,
110 #ifdef CTS_USES_VULKANSC
111                                          de::MovePtr<CustomInstance> customInstance,
112 #endif // CTS_USES_VULKANSC
113                                          Move<VkDevice> device, DeviceDriverPtr deviceDriver, TestMode mode,
114                                          uint32_t robustnessVersion)
115     : vkt::TestInstance(context)
116 #ifdef CTS_USES_VULKANSC
117     , m_customInstance(customInstance)
118 #endif
119     , m_device(device)
120     , m_deviceDriver(deviceDriver)
121     , m_mode(mode)
122     , m_robustnessVersion(robustnessVersion)
123 {
124 }
125 
iterate(void)126 tcu::TestStatus DrawIndexedInstance::iterate(void)
127 {
128     const DeviceInterface &vk       = *m_deviceDriver;
129     const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
130     const auto &vki                 = m_context.getInstanceInterface();
131     const VkPhysicalDevice physicalDevice =
132         chooseDevice(vki, m_context.getInstance(), m_context.getTestContext().getCommandLine());
133     SimpleAllocator memAlloc(vk, *m_device, getPhysicalDeviceMemoryProperties(vki, physicalDevice));
134 
135     // this is testsed - first index in index buffer is outside of bounds
136     const uint32_t oobFirstIndex = std::numeric_limits<uint32_t>::max() - 100;
137 
138     const VkFormat colorFormat{VK_FORMAT_R8G8B8A8_UNORM};
139     const tcu::UVec2 renderSize{16};
140     const std::vector<VkViewport> viewports{makeViewport(renderSize)};
141     const std::vector<VkRect2D> scissors{makeRect2D(renderSize)};
142 
143     // create vertex buffer
144     const std::vector<float> vertices{
145         0.0f, -0.8f, 0.0f, 1.0f, 0.0f,  0.8f,  0.0f, 1.0f, 0.8f,  -0.8f, 0.0f, 1.0f,
146         0.8f, 0.8f,  0.0f, 1.0f, -0.8f, -0.8f, 0.0f, 1.0f, -0.8f, 0.8f,  0.0f, 1.0f,
147     };
148     const VkBufferCreateInfo vertexBufferInfo = makeBufferCreateInfo(
149         vertices.size() * sizeof(float), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
150     BufferWithMemory vertexBuffer(vk, *m_device, memAlloc, vertexBufferInfo, MemoryRequirement::HostVisible);
151     deMemcpy(vertexBuffer.getAllocation().getHostPtr(), vertices.data(), vertices.size() * sizeof(float));
152     flushAlloc(vk, *m_device, vertexBuffer.getAllocation());
153 
154     // create index buffer for 6 points
155     // 4--0--2
156     // |  |  |
157     // 5--1--3
158     const std::vector<uint32_t> index        = {0, 1, 2, 3, 4, 5};
159     const VkBufferCreateInfo indexBufferInfo = makeBufferCreateInfo(
160         index.size() * sizeof(uint32_t), VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
161     BufferWithMemory indexBuffer(vk, *m_device, memAlloc, indexBufferInfo, MemoryRequirement::HostVisible);
162     deMemcpy(indexBuffer.getAllocation().getHostPtr(), index.data(), index.size() * sizeof(uint32_t));
163     flushAlloc(vk, *m_device, indexBuffer.getAllocation());
164 
165     // create indirect buffer
166     const vk::VkDrawIndexedIndirectCommand drawIndirectCommand{
167         (uint32_t)index.size(), // indexCount
168         1u,                     // instanceCount
169         oobFirstIndex,          // firstIndex
170         0u,                     // vertexOffset
171         0u,                     // firstInstance
172     };
173     const VkBufferCreateInfo indirectBufferInfo = makeBufferCreateInfo(
174         sizeof(drawIndirectCommand), VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
175     BufferWithMemory indirectBuffer(vk, *m_device, memAlloc, indirectBufferInfo, MemoryRequirement::HostVisible);
176     if ((m_mode == TM_DRAW_INDEXED_INDIRECT) || (m_mode == TM_DRAW_INDEXED_INDIRECT_COUNT))
177     {
178         deMemcpy(indirectBuffer.getAllocation().getHostPtr(), &drawIndirectCommand, sizeof(drawIndirectCommand));
179         flushAlloc(vk, *m_device, indirectBuffer.getAllocation());
180     }
181 
182     // create indirect count buffer
183     const VkBufferCreateInfo indirectCountBufferInfo =
184         makeBufferCreateInfo(sizeof(uint32_t), VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
185     BufferWithMemory indirectCountBuffer(vk, *m_device, memAlloc, indirectCountBufferInfo,
186                                          MemoryRequirement::HostVisible);
187     if (m_mode == TM_DRAW_INDEXED_INDIRECT_COUNT)
188     {
189         *(reinterpret_cast<uint32_t *>(indirectCountBuffer.getAllocation().getHostPtr())) = 1;
190         flushAlloc(vk, *m_device, indirectCountBuffer.getAllocation());
191     }
192 
193     // create output buffer that will be used to read rendered image
194     const VkDeviceSize outputBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
195     const VkBufferCreateInfo outputBufferInfo =
196         makeBufferCreateInfo(outputBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
197     BufferWithMemory outputBuffer(vk, *m_device, memAlloc, outputBufferInfo, MemoryRequirement::HostVisible);
198 
199     // create color buffer
200     VkExtent3D imageExtent = makeExtent3D(renderSize.x(), renderSize.y(), 1u);
201     const VkImageCreateInfo imageCreateInfo{
202         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,                                   // VkStructureType sType;
203         DE_NULL,                                                               // const void* pNext;
204         0u,                                                                    // VkImageCreateFlags flags;
205         VK_IMAGE_TYPE_2D,                                                      // VkImageType imageType;
206         colorFormat,                                                           // VkFormat format;
207         imageExtent,                                                           // VkExtent3D extent;
208         1u,                                                                    // uint32_t mipLevels;
209         1u,                                                                    // uint32_t arrayLayers;
210         VK_SAMPLE_COUNT_1_BIT,                                                 // VkSampleCountFlagBits samples;
211         VK_IMAGE_TILING_OPTIMAL,                                               // VkImageTiling tiling;
212         VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // VkImageUsageFlags usage;
213         VK_SHARING_MODE_EXCLUSIVE,                                             // VkSharingMode sharingMode;
214         0u,                                                                    // uint32_t queueFamilyIndexCount;
215         DE_NULL,                                                               // const uint32_t* pQueueFamilyIndices;
216         VK_IMAGE_LAYOUT_UNDEFINED,                                             // VkImageLayout initialLayout;
217     };
218     const VkImageSubresourceRange colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
219     ImageWithMemory colorImage(vk, *m_device, memAlloc, imageCreateInfo, MemoryRequirement::Any);
220     Move<VkImageView> colorImageView =
221         makeImageView(vk, *m_device, colorImage.get(), VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorSRR);
222 
223     // create shader modules, renderpass, framebuffer and pipeline
224     Move<VkShaderModule> vertShaderModule =
225         createShaderModule(vk, *m_device, m_context.getBinaryCollection().get("vert"), 0);
226     Move<VkShaderModule> fragShaderModule =
227         createShaderModule(vk, *m_device, m_context.getBinaryCollection().get("frag"), 0);
228     Move<VkRenderPass> renderPass         = makeRenderPass(vk, *m_device, colorFormat);
229     Move<VkPipelineLayout> pipelineLayout = makePipelineLayout(vk, *m_device, DE_NULL);
230     Move<VkFramebuffer> framebuffer =
231         makeFramebuffer(vk, *m_device, *renderPass, *colorImageView, renderSize.x(), renderSize.y());
232     Move<VkPipeline> graphicsPipeline =
233         makeGraphicsPipeline(vk, *m_device, *pipelineLayout, *vertShaderModule, DE_NULL, DE_NULL, DE_NULL,
234                              *fragShaderModule, *renderPass, viewports, scissors, VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
235 
236     Move<VkCommandPool> cmdPool =
237         createCommandPool(vk, *m_device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
238     vk::Move<vk::VkCommandBuffer> cmdBuffer =
239         allocateCommandBuffer(vk, *m_device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
240 
241     beginCommandBuffer(vk, *cmdBuffer);
242 
243     // transition colorbuffer layout
244     VkImageMemoryBarrier imageBarrier =
245         makeImageMemoryBarrier(0u, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
246                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, colorImage.get(), colorSRR);
247     vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u, 0u,
248                           0u, 0u, 0u, 1u, &imageBarrier);
249 
250     const VkRect2D renderArea = makeRect2D(0, 0, renderSize.x(), renderSize.y());
251     beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
252 
253     const VkDeviceSize vBuffOffset = 0;
254     vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
255     vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &vertexBuffer.get(), &vBuffOffset);
256     vk.cmdBindIndexBuffer(*cmdBuffer, indexBuffer.get(), 0, VK_INDEX_TYPE_UINT32);
257 
258     // we will draw all points at index 0
259     if (m_mode == TM_DRAW_INDEXED)
260         vk.cmdDrawIndexed(*cmdBuffer, (uint32_t)index.size(), 1, oobFirstIndex, 0, 0);
261     else if (m_mode == TM_DRAW_INDEXED_INDIRECT)
262         vk.cmdDrawIndexedIndirect(*cmdBuffer, indirectBuffer.get(), 0, 1, 0);
263     else if (m_mode == TM_DRAW_INDEXED_INDIRECT_COUNT)
264         vk.cmdDrawIndexedIndirectCount(*cmdBuffer, indirectBuffer.get(), 0, indirectCountBuffer.get(), 0, 1,
265                                        sizeof(VkDrawIndexedIndirectCommand));
266     else if (m_mode == TM_DRAW_MULTI_INDEXED)
267     {
268 #ifndef CTS_USES_VULKANSC
269         VkMultiDrawIndexedInfoEXT indexInfo[]{
270             {oobFirstIndex, 3, 0},
271             {oobFirstIndex - 3, 3, 0},
272         };
273         vk.cmdDrawMultiIndexedEXT(*cmdBuffer, 2, indexInfo, 1, 0, sizeof(VkMultiDrawIndexedInfoEXT), DE_NULL);
274 #endif // CTS_USES_VULKANSC
275     }
276 
277     endRenderPass(vk, *cmdBuffer);
278 
279     // wait till data is transfered to image
280     imageBarrier = makeImageMemoryBarrier(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
281                                           VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
282                                           VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorImage.get(), colorSRR);
283     vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
284                           0u, 0u, 0u, 0u, 1u, &imageBarrier);
285 
286     // read back color image
287     const VkImageSubresourceLayers colorSL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
288     const VkBufferImageCopy copyRegion     = makeBufferImageCopy(imageExtent, colorSL);
289     vk.cmdCopyImageToBuffer(*cmdBuffer, colorImage.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outputBuffer.get(), 1u,
290                             &copyRegion);
291 
292     auto bufferBarrier = makeBufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT,
293                                                  outputBuffer.get(), 0u, VK_WHOLE_SIZE);
294 
295     vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, 0u, 1u,
296                           &bufferBarrier, 0u, 0u);
297 
298     endCommandBuffer(vk, *cmdBuffer);
299 
300     VkQueue queue;
301     vk.getDeviceQueue(*m_device, queueFamilyIndex, 0, &queue);
302     submitCommandsAndWait(vk, *m_device, queue, *cmdBuffer);
303 
304     // for robustBufferAccess (the original feature) OOB access will return undefined value;
305     // we can only expect that above drawing will be executed without errors (we can't expect any specific result)
306     if (m_robustnessVersion < 2u)
307         return tcu::TestStatus::pass("Pass");
308 
309     // get output buffer
310     invalidateAlloc(vk, *m_device, outputBuffer.getAllocation());
311     const tcu::TextureFormat resultFormat = mapVkFormat(colorFormat);
312     tcu::ConstPixelBufferAccess outputAccess(resultFormat, renderSize.x(), renderSize.y(), 1u,
313                                              outputBuffer.getAllocation().getHostPtr());
314 
315     // for VK_EXT_robustness2 OOB access should return 0 and we can verify
316     // that single fragment is drawn in the middle-top part of the image
317     tcu::UVec4 expectedValue(51, 255, 127, 255);
318     bool fragmentFound = false;
319 
320     for (uint32_t x = 0u; x < renderSize.x(); ++x)
321         for (uint32_t y = 0u; y < renderSize.y(); ++y)
322         {
323             tcu::UVec4 pixel = outputAccess.getPixelUint(x, y, 0);
324 
325             if (tcu::boolAll(tcu::lessThan(tcu::absDiff(pixel, expectedValue), tcu::UVec4(2))))
326             {
327                 if (fragmentFound)
328                 {
329                     m_context.getTestContext().getLog()
330                         << tcu::TestLog::Message << "Expected single fragment with: " << expectedValue
331                         << " color, got more, second at " << tcu::UVec2(x, y) << tcu::TestLog::EndMessage
332                         << tcu::TestLog::Image("Result", "Result", outputAccess);
333                     return tcu::TestStatus::fail("Fail");
334                 }
335                 else if ((y < 3) && (x > 5) && (x < 10))
336                     fragmentFound = true;
337                 else
338                 {
339                     m_context.getTestContext().getLog()
340                         << tcu::TestLog::Message
341                         << "Expected fragment in the middle-top of the image, got at: " << tcu::UVec2(x, y)
342                         << tcu::TestLog::EndMessage << tcu::TestLog::Image("Result", "Result", outputAccess);
343                     return tcu::TestStatus::fail("Fail");
344                 }
345             }
346         }
347 
348     if (fragmentFound)
349         return tcu::TestStatus::pass("Pass");
350     return tcu::TestStatus::fail("Fail");
351 }
352 
353 class DrawIndexedTestCase : public vkt::TestCase
354 {
355 public:
356     DrawIndexedTestCase(tcu::TestContext &testContext, const std::string &name, TestMode mode,
357                         uint32_t robustnessVersion);
358 
359     virtual ~DrawIndexedTestCase(void) = default;
360 
361     void checkSupport(Context &context) const override;
362     TestInstance *createInstance(Context &context) const override;
363     void initPrograms(SourceCollections &programCollection) const override;
364 
365 protected:
366     void createDeviceAndDriver(Context &context,
367 #ifdef CTS_USES_VULKANSC
368                                de::MovePtr<CustomInstance> &customInstance,
369 #endif // CTS_USES_VULKANSC
370                                Move<VkDevice> &device, DeviceDriverPtr &driver) const;
371     const TestMode m_testMode;
372     const uint32_t m_robustnessVersion;
373 };
374 
DrawIndexedTestCase(tcu::TestContext & testContext,const std::string & name,TestMode mode,uint32_t robustnessVersion)375 DrawIndexedTestCase::DrawIndexedTestCase(tcu::TestContext &testContext, const std::string &name, TestMode mode,
376                                          uint32_t robustnessVersion)
377 
378     : vkt::TestCase(testContext, name)
379     , m_testMode(mode)
380     , m_robustnessVersion(robustnessVersion)
381 {
382 }
383 
checkSupport(Context & context) const384 void DrawIndexedTestCase::checkSupport(Context &context) const
385 {
386     if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") &&
387         !context.getDeviceFeatures().robustBufferAccess)
388         TCU_THROW(NotSupportedError,
389                   "VK_KHR_portability_subset: robustBufferAccess not supported by this implementation");
390 
391     if (m_testMode == TestMode::TM_DRAW_INDEXED_INDIRECT_COUNT)
392         context.requireDeviceFunctionality("VK_KHR_draw_indirect_count");
393     if (m_testMode == TestMode::TM_DRAW_MULTI_INDEXED)
394         context.requireDeviceFunctionality("VK_EXT_multi_draw");
395     if (m_robustnessVersion == 2)
396     {
397         context.requireDeviceFunctionality("VK_EXT_robustness2");
398 
399         const auto &vki           = context.getInstanceInterface();
400         const auto physicalDevice = context.getPhysicalDevice();
401 
402         VkPhysicalDeviceRobustness2FeaturesEXT robustness2Features = initVulkanStructure();
403         VkPhysicalDeviceFeatures2 features2                        = initVulkanStructure(&robustness2Features);
404 
405         vki.getPhysicalDeviceFeatures2(physicalDevice, &features2);
406 
407         if (!robustness2Features.robustBufferAccess2)
408             TCU_THROW(NotSupportedError, "robustBufferAccess2 not supported");
409     }
410 }
411 
createDeviceAndDriver(Context & context,de::MovePtr<CustomInstance> & customInstance,Move<VkDevice> & device,DeviceDriverPtr & driver) const412 void DrawIndexedTestCase::createDeviceAndDriver(Context &context,
413 #ifdef CTS_USES_VULKANSC
414                                                 de::MovePtr<CustomInstance> &customInstance,
415 #endif // CTS_USES_VULKANSC
416                                                 Move<VkDevice> &device, DeviceDriverPtr &driver) const
417 {
418     VkPhysicalDeviceFeatures2 features2   = initVulkanStructure();
419     features2.features.robustBufferAccess = true;
420 
421     void **nextPtr = &features2.pNext;
422 
423 #ifndef CTS_USES_VULKANSC
424     VkPhysicalDeviceMultiDrawFeaturesEXT multiDrawFeatures = initVulkanStructure();
425     if (m_testMode == TestMode::TM_DRAW_MULTI_INDEXED)
426     {
427         multiDrawFeatures.multiDraw = true;
428         addToChainVulkanStructure(&nextPtr, multiDrawFeatures);
429     }
430 #endif // CTS_USES_VULKANSC
431 
432     VkPhysicalDeviceRobustness2FeaturesEXT robustness2Features = initVulkanStructure();
433     if (m_robustnessVersion > 1u)
434     {
435         robustness2Features.robustBufferAccess2 = true;
436         addToChainVulkanStructure(&nextPtr, robustness2Features);
437     }
438 
439     uint32_t apiVersion                               = context.getUsedApiVersion();
440     VkPhysicalDeviceVulkan12Features vulkan12Features = initVulkanStructure();
441     if ((m_testMode == TestMode::TM_DRAW_INDEXED_INDIRECT_COUNT) && (apiVersion > VK_MAKE_API_VERSION(0, 1, 1, 0)))
442     {
443         vulkan12Features.drawIndirectCount = true;
444         addToChainVulkanStructure(&nextPtr, vulkan12Features);
445     }
446 
447 #ifndef CTS_USES_VULKANSC
448     device = createRobustBufferAccessDevice(context, &features2);
449     driver = DeviceDriverPtr(new DeviceDriver(context.getPlatformInterface(), context.getInstance(), *device,
450                                               context.getUsedApiVersion(), context.getTestContext().getCommandLine()));
451 #else
452     customInstance = de::MovePtr<CustomInstance>(new CustomInstance(createCustomInstanceFromContext(context)));
453     device         = createRobustBufferAccessDevice(context, *customInstance, &features2);
454     driver         = DeviceDriverPtr(new DeviceDriverSC(context.getPlatformInterface(), *customInstance, *device,
455                                                         context.getTestContext().getCommandLine(),
456                                                         context.getResourceInterface(), context.getDeviceVulkanSC10Properties(),
457                                                         context.getDeviceProperties(), context.getUsedApiVersion()),
458                                      vk::DeinitDeviceDeleter(context.getResourceInterface().get(), *device));
459 #endif // CTS_USES_VULKANSC
460 }
461 
createInstance(Context & context) const462 TestInstance *DrawIndexedTestCase::createInstance(Context &context) const
463 {
464     Move<VkDevice> device;
465     DeviceDriverPtr deviceDriver;
466 #ifndef CTS_USES_VULKANSC
467     createDeviceAndDriver(context, device, deviceDriver);
468 #else
469     de::MovePtr<CustomInstance> customInstance;
470     createDeviceAndDriver(context, customInstance, device, deviceDriver);
471 #endif // CTS_USES_VULKANSC
472 
473     return new DrawIndexedInstance(context,
474 #ifdef CTS_USES_VULKANSC
475                                    customInstance,
476 #endif // CTS_USES_VULKANSC
477                                    device, deviceDriver, m_testMode, m_robustnessVersion);
478 }
479 
initPrograms(SourceCollections & sourceCollections) const480 void DrawIndexedTestCase::initPrograms(SourceCollections &sourceCollections) const
481 {
482     std::string vertexSource("#version 450\n"
483                              "layout(location = 0) in vec4 inPosition;\n"
484                              "void main(void)\n"
485                              "{\n"
486                              "\tgl_Position = inPosition;\n"
487                              "\tgl_PointSize = 1.0;\n"
488                              "}\n");
489     sourceCollections.glslSources.add("vert") << glu::VertexSource(vertexSource);
490 
491     std::string fragmentSource("#version 450\n"
492                                "precision highp float;\n"
493                                "layout(location = 0) out vec4 fragColor;\n"
494                                "void main (void)\n"
495                                "{\n"
496                                "\tfragColor = vec4(0.2, 1.0, 0.5, 1.0);\n"
497                                "}\n");
498 
499     sourceCollections.glslSources.add("frag") << glu::FragmentSource(fragmentSource);
500 }
501 
502 class BindIndexBuffer2Instance : public vkt::TestInstance
503 {
504 public:
505     BindIndexBuffer2Instance(Context &c,
506 #ifdef CTS_USES_VULKANSC
507                              de::MovePtr<CustomInstance> customInstance,
508 #endif // CTS_USES_VULKANSC
509                              Move<VkDevice> device, DeviceDriverPtr driver, const TestParams &params);
510     virtual ~BindIndexBuffer2Instance(void) = default;
511 
512     virtual tcu::TestStatus iterate(void) override;
513 
514 protected:
515 #ifdef CTS_USES_VULKANSC
516     const de::MovePtr<CustomInstance> m_customInstance;
517 #endif // CTS_USES_VULKANSC
518     const Move<VkDevice> m_device;
519     const DeviceDriverPtr m_driver;
520     const TestParams m_params;
521     VkPhysicalDevice m_physDevice;
522     SimpleAllocator m_allocator;
523 
524 protected:
getDeviceInterface() const525     inline const DeviceInterface &getDeviceInterface() const
526     {
527         return *m_driver;
528     }
getDevice() const529     inline VkDevice getDevice() const
530     {
531         return *m_device;
532     }
getPhysicalDevice() const533     inline VkPhysicalDevice getPhysicalDevice() const
534     {
535         return m_physDevice;
536     }
getAllocator()537     inline Allocator &getAllocator()
538     {
539         return m_allocator;
540     }
541     VkQueue getQueue() const;
542 };
543 
BindIndexBuffer2Instance(Context & c,de::MovePtr<CustomInstance> customInstance,Move<VkDevice> device,DeviceDriverPtr driver,const TestParams & params)544 BindIndexBuffer2Instance::BindIndexBuffer2Instance(Context &c,
545 #ifdef CTS_USES_VULKANSC
546                                                    de::MovePtr<CustomInstance> customInstance,
547 #endif
548                                                    Move<VkDevice> device, DeviceDriverPtr driver,
549                                                    const TestParams &params)
550     : vkt::TestInstance(c)
551 #ifdef CTS_USES_VULKANSC
552     , m_customInstance(customInstance)
553 #endif
554     , m_device(device)
555     , m_driver(driver)
556     , m_params(params)
557     , m_physDevice(chooseDevice(c.getInstanceInterface(), c.getInstance(), c.getTestContext().getCommandLine()))
558     , m_allocator(getDeviceInterface(), getDevice(),
559                   getPhysicalDeviceMemoryProperties(c.getInstanceInterface(), m_physDevice))
560 {
561 }
562 
getQueue() const563 VkQueue BindIndexBuffer2Instance::getQueue() const
564 {
565     VkQueue queue = DE_NULL;
566     getDeviceInterface().getDeviceQueue(getDevice(), m_context.getUniversalQueueFamilyIndex(), 0, &queue);
567     return queue;
568 }
569 
570 class BindIndexBuffer2TestCase : public DrawIndexedTestCase
571 {
572 public:
573     BindIndexBuffer2TestCase(tcu::TestContext &testContext, const std::string &name, const TestParams &params);
574     ~BindIndexBuffer2TestCase(void) = default;
575 
576     void checkSupport(Context &context) const override;
577     TestInstance *createInstance(Context &context) const override;
578     void initPrograms(SourceCollections &programs) const override;
579 
580 protected:
581     const OOTypes m_ooType;
582     const uint32_t m_leadingCount;
583 };
584 
BindIndexBuffer2TestCase(tcu::TestContext & testContext,const std::string & name,const TestParams & params)585 BindIndexBuffer2TestCase::BindIndexBuffer2TestCase(tcu::TestContext &testContext, const std::string &name,
586                                                    const TestParams &params)
587     : DrawIndexedTestCase(testContext, name, params.mode, 2)
588     , m_ooType(params.ooType)
589     , m_leadingCount(params.leadingCount)
590 {
591 }
592 
593 #ifdef CTS_USES_VULKANSC
594 #define DEPENDENT_MAINTENANCE_5_EXTENSION_NAME "VK_KHR_maintenance5"
595 #else
596 #define DEPENDENT_MAINTENANCE_5_EXTENSION_NAME VK_KHR_MAINTENANCE_5_EXTENSION_NAME
597 #endif
598 
checkSupport(Context & context) const599 void BindIndexBuffer2TestCase::checkSupport(Context &context) const
600 {
601     DrawIndexedTestCase::checkSupport(context);
602     context.requireDeviceFunctionality(DEPENDENT_MAINTENANCE_5_EXTENSION_NAME);
603 }
604 
initPrograms(SourceCollections & programs) const605 void BindIndexBuffer2TestCase::initPrograms(SourceCollections &programs) const
606 {
607     const std::string vertexSource("#version 450\n"
608                                    "layout(location = 0) in vec4 inPosition;\n"
609                                    "void main(void) {\n"
610                                    "   gl_Position = inPosition;\n"
611                                    "   gl_PointSize = 1.0;\n"
612                                    "}\n");
613     programs.glslSources.add("vert") << glu::VertexSource(vertexSource);
614 
615     const std::string fragmentSource("#version 450\n"
616                                      "layout(location = 0) out vec4 fragColor;\n"
617                                      "void main (void) {\n"
618                                      "   fragColor = vec4(1.0);\n"
619                                      "}\n");
620     programs.glslSources.add("frag") << glu::FragmentSource(fragmentSource);
621 }
622 
createInstance(Context & context) const623 TestInstance *BindIndexBuffer2TestCase::createInstance(Context &context) const
624 {
625     TestParams params;
626     Move<VkDevice> device;
627     DeviceDriverPtr deviceDriver;
628     params.mode         = m_testMode;
629     params.ooType       = m_ooType;
630     params.leadingCount = m_leadingCount;
631 
632 #ifndef CTS_USES_VULKANSC
633     createDeviceAndDriver(context, device, deviceDriver);
634 #else
635     de::MovePtr<CustomInstance> customInstance =
636         de::MovePtr<CustomInstance>(new CustomInstance(createCustomInstanceFromContext(context)));
637     createDeviceAndDriver(context, customInstance, device, deviceDriver);
638 #endif // CTS_USES_VULKANSC
639 
640     return new BindIndexBuffer2Instance(context,
641 #ifdef CTS_USES_VULKANSC
642                                         customInstance,
643 #endif // CTS_USES_VULKANSC
644                                         device, deviceDriver, params);
645 }
646 
iterate(void)647 tcu::TestStatus BindIndexBuffer2Instance::iterate(void)
648 {
649     const DeviceInterface &vk     = this->getDeviceInterface();
650     const VkDevice device         = this->getDevice();
651     Allocator &allocator          = this->getAllocator();
652     const VkQueue queue           = this->getQueue();
653     const uint32_t queueFamilyIdx = m_context.getUniversalQueueFamilyIndex();
654     tcu::TestLog &log             = m_context.getTestContext().getLog();
655 
656     const VkFormat colorFormat{VK_FORMAT_R32G32B32A32_SFLOAT};
657     const tcu::UVec2 renderSize{64, 64};
658     const std::vector<VkViewport> viewports{makeViewport(renderSize)};
659     const std::vector<VkRect2D> scissors{makeRect2D(renderSize)};
660 
661     // build vertices data
662     std::vector<tcu::Vec4> vertices;
663 
664     // first triangle in 2nd quarter, it should not be drawn
665     vertices.emplace_back(-1.0f, 0.1f, 0.0f, 1.0f);
666     vertices.emplace_back(-1.0f, 1.0f, 0.0f, 1.0f);
667     vertices.emplace_back(-0.1f, 0.1f, 0.0f, 1.0f);
668 
669     // second triangle in 2nd quarter, it should not be drawn
670     vertices.emplace_back(-0.1f, 0.1f, 0.0f, 1.0f);
671     vertices.emplace_back(-1.0f, 1.0f, 0.0f, 1.0f);
672     vertices.emplace_back(-0.1f, 1.0f, 0.0f, 1.0f);
673 
674     // first triangle in 3rd quarter, it must be drawn
675     vertices.emplace_back(0.0f, -1.0f, 0.0f, 1.0f);
676     vertices.emplace_back(-1.0f, -1.0f, 0.0f, 1.0f);
677     vertices.emplace_back(-1.0f, 0.0f, 0.0f, 1.0f);
678 
679     // second triangle in 3rd quarter if robustness works as expected,
680     // otherwise will be drawn in 1st quarter as well
681     vertices.emplace_back(0.0f, -1.0f, 0.0f, 1.0f);
682     vertices.emplace_back(-1.0f, 0.0f, 0.0f, 1.0f);
683     vertices.emplace_back(1.0f, 1.0f, 0.0f, 1.0f);
684 
685     // create vertex buffer
686     const VkBufferCreateInfo vertexBufferInfo = makeBufferCreateInfo(
687         vertices.size() * sizeof(tcu::Vec4), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
688     BufferWithMemory vertexBuffer(vk, device, allocator, vertexBufferInfo, MemoryRequirement::HostVisible);
689     deMemcpy(vertexBuffer.getAllocation().getHostPtr(), vertices.data(), vertices.size() * sizeof(tcu::Vec4));
690 
691     // build index data
692     const uint32_t leadingCount = m_params.leadingCount;
693     std::vector<uint32_t> indices(leadingCount * 6 + 6);
694     for (uint32_t j = 0; j < leadingCount; ++j)
695         for (uint32_t k = 0; k < 6; ++k)
696         {
697             indices[j * 6 + k] = k;
698         }
699     std::iota(std::next(indices.begin(), (leadingCount * 6)), indices.end(), 6u);
700 
701     const uint32_t firstIndex        = 0;
702     const uint32_t indexCount        = 6;
703     const VkDeviceSize bindingOffset = leadingCount * 6 * sizeof(uint32_t);
704     VkDeviceSize bindingSize         = 6 * sizeof(uint32_t);
705     VkDeviceSize allocSize           = indices.size() * sizeof(uint32_t);
706     switch (m_params.ooType)
707     {
708     case OOTypes::OO_NONE:
709         // default values already set
710         break;
711     case OOTypes::OO_INDEX:
712         indices.back() = 33; // out of range index
713         break;
714     case OOTypes::OO_SIZE:
715         bindingSize = 5 * sizeof(uint32_t);
716         break;
717     case OOTypes::OO_WHOLE_SIZE:
718         bindingSize = VK_WHOLE_SIZE;
719         allocSize   = (indices.size() - 1) * sizeof(uint32_t);
720         break;
721     }
722 
723     // create index buffer
724     const VkBufferCreateInfo indexBufferInfo =
725         makeBufferCreateInfo(allocSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
726     BufferWithMemory indexBuffer(vk, device, allocator, indexBufferInfo, MemoryRequirement::HostVisible);
727     deMemcpy(indexBuffer.getAllocation().getHostPtr(), indices.data(), size_t(allocSize));
728 
729     // create indirect buffer
730     const vk::VkDrawIndexedIndirectCommand drawIndirectCommand{
731         indexCount, // indexCount
732         1u,         // instanceCount
733         firstIndex, // firstIndex
734         0u,         // vertexOffset
735         0u,         // firstInstance
736     };
737     const VkBufferCreateInfo indirectBufferInfo = makeBufferCreateInfo(
738         sizeof(drawIndirectCommand), VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
739     BufferWithMemory indirectBuffer(vk, *m_device, allocator, indirectBufferInfo, MemoryRequirement::HostVisible);
740     if ((m_params.mode == TM_DRAW_INDEXED_INDIRECT) || (m_params.mode == TM_DRAW_INDEXED_INDIRECT_COUNT))
741     {
742         deMemcpy(indirectBuffer.getAllocation().getHostPtr(), &drawIndirectCommand, sizeof(drawIndirectCommand));
743     }
744 
745     // create indirect count buffer
746     const VkBufferCreateInfo indirectCountBufferInfo =
747         makeBufferCreateInfo(sizeof(uint32_t), VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
748     BufferWithMemory indirectCountBuffer(vk, *m_device, allocator, indirectCountBufferInfo,
749                                          MemoryRequirement::HostVisible);
750     if (m_params.mode == TM_DRAW_INDEXED_INDIRECT_COUNT)
751     {
752         *static_cast<uint32_t *>(indirectCountBuffer.getAllocation().getHostPtr()) = 1u;
753     }
754 
755     // create output buffer that will be used to read rendered image
756     const VkDeviceSize outputBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
757     const VkBufferCreateInfo outputBufferInfo =
758         makeBufferCreateInfo(outputBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
759     BufferWithMemory outputBuffer(vk, device, allocator, outputBufferInfo, MemoryRequirement::HostVisible);
760 
761     // create color buffer
762     const VkExtent3D imageExtent = makeExtent3D(renderSize.x(), renderSize.y(), 1u);
763     const VkImageCreateInfo imageCreateInfo{
764         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,                                   // VkStructureType sType;
765         DE_NULL,                                                               // const void* pNext;
766         0u,                                                                    // VkImageCreateFlags flags;
767         VK_IMAGE_TYPE_2D,                                                      // VkImageType imageType;
768         colorFormat,                                                           // VkFormat format;
769         imageExtent,                                                           // VkExtent3D extent;
770         1u,                                                                    // uint32_t mipLevels;
771         1u,                                                                    // uint32_t arrayLayers;
772         VK_SAMPLE_COUNT_1_BIT,                                                 // VkSampleCountFlagBits samples;
773         VK_IMAGE_TILING_OPTIMAL,                                               // VkImageTiling tiling;
774         VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // VkImageUsageFlags usage;
775         VK_SHARING_MODE_EXCLUSIVE,                                             // VkSharingMode sharingMode;
776         0u,                                                                    // uint32_t queueFamilyIndexCount;
777         DE_NULL,                                                               // const uint32_t* pQueueFamilyIndices;
778         VK_IMAGE_LAYOUT_UNDEFINED,                                             // VkImageLayout initialLayout;
779     };
780     const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
781     const VkImageSubresourceRange colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
782     ImageWithMemory colorImage(vk, *m_device, allocator, imageCreateInfo, MemoryRequirement::Any);
783     Move<VkImageView> colorImageView =
784         makeImageView(vk, *m_device, colorImage.get(), VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorSRR);
785 
786     // create shader modules, renderpass, framebuffer and pipeline
787     Move<VkShaderModule> vertShaderModule =
788         createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0);
789     Move<VkShaderModule> fragShaderModule =
790         createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0);
791     Move<VkRenderPass> renderPass         = makeRenderPass(vk, device, colorFormat);
792     Move<VkPipelineLayout> pipelineLayout = makePipelineLayout(vk, device);
793     Move<VkFramebuffer> framebuffer =
794         makeFramebuffer(vk, device, *renderPass, *colorImageView, renderSize.x(), renderSize.y());
795     Move<VkPipeline> graphicsPipeline =
796         makeGraphicsPipeline(vk, device, *pipelineLayout, *vertShaderModule, DE_NULL, DE_NULL, DE_NULL,
797                              *fragShaderModule, *renderPass, viewports, scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
798 
799     Move<VkCommandPool> cmdPool =
800         createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIdx);
801     vk::Move<vk::VkCommandBuffer> cmdBuffer =
802         allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
803 
804     beginCommandBuffer(vk, *cmdBuffer);
805 
806     // transition colorbuffer layout
807     VkImageMemoryBarrier imageBarrier =
808         makeImageMemoryBarrier(0u, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
809                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, colorImage.get(), colorSRR);
810     vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u, 0u,
811                           0u, 0u, 0u, 1u, &imageBarrier);
812 
813     const VkRect2D renderArea = makeRect2D(0, 0, renderSize.x(), renderSize.y());
814     beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
815 
816     vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
817     vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &vertexBuffer.get(), &static_cast<const VkDeviceSize &>(0));
818 
819 #ifndef CTS_USES_VULKANSC
820     vk.cmdBindIndexBuffer2KHR(*cmdBuffer, indexBuffer.get(), bindingOffset, bindingSize, VK_INDEX_TYPE_UINT32);
821 #else
822     DE_UNREF(bindingOffset);
823     DE_UNREF(bindingSize);
824 #endif
825 
826     // we will draw all points at index 0
827     switch (m_params.mode)
828     {
829     case TM_DRAW_INDEXED:
830         vk.cmdDrawIndexed(*cmdBuffer, indexCount, 1u, firstIndex, 0, 0);
831         break;
832 
833     case TM_DRAW_INDEXED_INDIRECT:
834         vk.cmdDrawIndexedIndirect(*cmdBuffer, indirectBuffer.get(), 0, 1, uint32_t(sizeof(drawIndirectCommand)));
835         break;
836 
837     case TM_DRAW_INDEXED_INDIRECT_COUNT:
838         vk.cmdDrawIndexedIndirectCount(*cmdBuffer, indirectBuffer.get(), 0, indirectCountBuffer.get(), 0, 1,
839                                        uint32_t(sizeof(drawIndirectCommand)));
840         break;
841 
842     case TM_DRAW_MULTI_INDEXED:
843 #ifndef CTS_USES_VULKANSC
844     {
845         const VkMultiDrawIndexedInfoEXT indexInfo[/* { firstIndex, indexCount, vertexOffset } */]{
846             {firstIndex + 3, 3, 0},
847             {firstIndex, 3, 0},
848         };
849         vk.cmdDrawMultiIndexedEXT(*cmdBuffer, DE_LENGTH_OF_ARRAY(indexInfo), indexInfo, 1, 0,
850                                   sizeof(VkMultiDrawIndexedInfoEXT), DE_NULL);
851     }
852 #endif
853     break;
854     }
855 
856     endRenderPass(vk, *cmdBuffer);
857 
858     // wait till data is transfered to image
859     imageBarrier = makeImageMemoryBarrier(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
860                                           VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
861                                           VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorImage.get(), colorSRR);
862     vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
863                           0u, 0u, 0u, 0u, 1u, &imageBarrier);
864 
865     // read back color image
866     const VkImageSubresourceLayers colorSL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
867     const VkBufferImageCopy copyRegion     = makeBufferImageCopy(imageExtent, colorSL);
868     vk.cmdCopyImageToBuffer(*cmdBuffer, colorImage.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outputBuffer.get(), 1u,
869                             &copyRegion);
870 
871     endCommandBuffer(vk, *cmdBuffer);
872     submitCommandsAndWait(vk, device, queue, *cmdBuffer);
873 
874     // get output buffer
875     invalidateAlloc(vk, device, outputBuffer.getAllocation());
876     const tcu::TextureFormat resultFormat = mapVkFormat(colorFormat);
877     tcu::ConstPixelBufferAccess resultAccess(resultFormat, renderSize.x(), renderSize.y(), 1u,
878                                              outputBuffer.getAllocation().getHostPtr());
879 
880     // neither one triangle should be drawn in the second quarter, they are omitted by the offset or the firstIndex parameters
881     const tcu::Vec4 p11 = resultAccess.getPixel((1 * renderSize.x()) / 8, (5 * renderSize.y()) / 8);
882     const tcu::Vec4 p12 = resultAccess.getPixel((3 * renderSize.x()) / 8, (7 * renderSize.y()) / 8);
883     const bool c1       = p11.x() == clearColor.x() && p11.y() == clearColor.y() && p11.z() == clearColor.z() &&
884                     p12.x() == clearColor.x() && p12.y() == clearColor.y() && p12.z() == clearColor.z();
885 
886     // small triangle in the third quarter must be drawn always
887     const tcu::Vec4 p2 = resultAccess.getPixel((1 * renderSize.x()) / 8, (1 * renderSize.y()) / 8);
888     const bool c2      = p2.x() != clearColor.x() && p2.y() != clearColor.y() && p2.z() != clearColor.z();
889 
890     // if robustness works, then the origin of coordinate system will be read in shader instead of a value that an index points (1,1)
891     const tcu::Vec4 p3 = resultAccess.getPixel((3 * renderSize.x()) / 4, (3 * renderSize.y()) / 4);
892     const bool c3      = p3.x() == clearColor.x() && p3.y() == clearColor.y() && p3.z() == clearColor.z();
893 
894     bool verdict = false;
895     switch (m_params.ooType)
896     {
897     case OOTypes::OO_NONE:
898         verdict = c1 && c2 && !c3;
899         break;
900     default:
901         verdict = c1 && c2 && c3;
902         break;
903     }
904 
905     log << tcu::TestLog::ImageSet("Result", "") << tcu::TestLog::Image(std::to_string(m_params.mode), "", resultAccess)
906         << tcu::TestLog::EndImageSet;
907     return (*(verdict ? &tcu::TestStatus::pass : &tcu::TestStatus::fail))(std::string());
908 }
909 
createCmdBindIndexBuffer2Tests(tcu::TestContext & testCtx)910 tcu::TestCaseGroup *createCmdBindIndexBuffer2Tests(tcu::TestContext &testCtx)
911 {
912     const std::pair<const char *, TestMode> modes[]{
913         {"draw_indexed", TestMode::TM_DRAW_INDEXED},
914         {"draw_indexed_indirect", TestMode::TM_DRAW_INDEXED_INDIRECT},
915         {"draw_indexed_indirect_count", TestMode::TM_DRAW_INDEXED_INDIRECT_COUNT},
916         {"draw_multi_indexed", TestMode::TM_DRAW_MULTI_INDEXED},
917     };
918 
919     const std::pair<std::string, OOTypes> OutOfTypes[]{
920         {"oo_none", OOTypes::OO_NONE},
921         {"oo_index", OOTypes::OO_INDEX},
922         {"oo_size", OOTypes::OO_SIZE},
923         {"oo_whole_size", OOTypes::OO_WHOLE_SIZE},
924     };
925 
926     const uint32_t offsets[] = {0, 100};
927 
928     // Test access outside of the buffer with using the vkCmdBindIndexBuffer2 function from VK_KHR_maintenance5 extension.
929     de::MovePtr<tcu::TestCaseGroup> gRoot(new tcu::TestCaseGroup(testCtx, "bind_index_buffer2"));
930     for (uint32_t offset : offsets)
931     {
932         de::MovePtr<tcu::TestCaseGroup> gOffset(
933             new tcu::TestCaseGroup(testCtx, ("offset_" + std::to_string(offset)).c_str()));
934         for (const auto &mode : modes)
935         {
936             de::MovePtr<tcu::TestCaseGroup> gMode(new tcu::TestCaseGroup(testCtx, mode.first));
937             for (const auto &ooType : OutOfTypes)
938             {
939                 TestParams p;
940                 p.mode         = mode.second;
941                 p.ooType       = ooType.second;
942                 p.leadingCount = offset;
943                 gMode->addChild(new BindIndexBuffer2TestCase(testCtx, ooType.first, p));
944             }
945             gOffset->addChild(gMode.release());
946         }
947         gRoot->addChild(gOffset.release());
948     }
949 
950     return gRoot.release();
951 }
952 
createIndexAccessTests(tcu::TestContext & testCtx)953 tcu::TestCaseGroup *createIndexAccessTests(tcu::TestContext &testCtx)
954 {
955     // Test access outside of the buffer for indices
956     de::MovePtr<tcu::TestCaseGroup> indexAccessTests(new tcu::TestCaseGroup(testCtx, "index_access"));
957 
958     struct TestConfig
959     {
960         std::string name;
961         TestMode mode;
962     };
963 
964     const std::vector<TestConfig> testConfigs{
965         {"draw_indexed", TestMode::TM_DRAW_INDEXED},
966         {"draw_indexed_indirect", TestMode::TM_DRAW_INDEXED_INDIRECT},
967         {"draw_indexed_indirect_count", TestMode::TM_DRAW_INDEXED_INDIRECT_COUNT},
968         {"draw_multi_indexed", TestMode::TM_DRAW_MULTI_INDEXED},
969     };
970 
971     const uint32_t robustnessVersion = 2;
972     for (const auto &c : testConfigs)
973     {
974         std::string name = c.name + "_" + std::to_string(robustnessVersion);
975         indexAccessTests->addChild(new DrawIndexedTestCase(testCtx, name, c.mode, robustnessVersion));
976     }
977 
978     return indexAccessTests.release();
979 }
980 
981 } // namespace robustness
982 } // namespace vkt
983