1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2018 The Khronos Group Inc.
6  * Copyright (c) 2018 Danylo Piliaiev <[email protected]>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Test for conditional rendering of vkCmdDispatch* functions
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktConditionalDispatchTests.hpp"
26 #include "vktConditionalRenderingTestUtil.hpp"
27 
28 #include "tcuTestLog.hpp"
29 #include "tcuResource.hpp"
30 
31 #include "vkDefs.hpp"
32 #include "vkCmdUtil.hpp"
33 #include "vkBuilderUtil.hpp"
34 #include "vkObjUtil.hpp"
35 #include "vkTypeUtil.hpp"
36 #include "vkBufferWithMemory.hpp"
37 
38 namespace vkt
39 {
40 namespace conditional
41 {
42 namespace
43 {
44 
45 enum DispatchCommandType
46 {
47     DISPATCH_COMMAND_TYPE_DISPATCH = 0,
48     DISPATCH_COMMAND_TYPE_DISPATCH_INDIRECT,
49     DISPATCH_COMMAND_TYPE_DISPATCH_BASE,
50     DISPATCH_COMMAND_TYPE_DISPATCH_LAST
51 };
52 
getDispatchCommandTypeName(DispatchCommandType command)53 const char *getDispatchCommandTypeName(DispatchCommandType command)
54 {
55     switch (command)
56     {
57     case DISPATCH_COMMAND_TYPE_DISPATCH:
58         return "dispatch";
59     case DISPATCH_COMMAND_TYPE_DISPATCH_INDIRECT:
60         return "dispatch_indirect";
61     case DISPATCH_COMMAND_TYPE_DISPATCH_BASE:
62         return "dispatch_base";
63     default:
64         DE_ASSERT(false);
65     }
66     return "";
67 }
68 
69 struct ConditionalTestSpec
70 {
71     DispatchCommandType command;
72     int numCalls;
73     ConditionalData conditionalData;
74 };
75 
76 class ConditionalDispatchTest : public vkt::TestCase
77 {
78 public:
79     ConditionalDispatchTest(tcu::TestContext &testCtx, const std::string &name, const ConditionalTestSpec &testSpec);
80 
81     void initPrograms(vk::SourceCollections &sourceCollections) const;
82     void checkSupport(Context &context) const;
83     TestInstance *createInstance(Context &context) const;
84 
85 private:
86     const ConditionalTestSpec m_testSpec;
87 };
88 
89 class ConditionalDispatchTestInstance : public TestInstance
90 {
91 public:
92     ConditionalDispatchTestInstance(Context &context, ConditionalTestSpec testSpec);
93 
94     virtual tcu::TestStatus iterate(void);
95     void recordDispatch(const vk::DeviceInterface &vk, vk::VkCommandBuffer cmdBuffer,
96                         vk::BufferWithMemory &indirectBuffer);
97 
98 protected:
99     const DispatchCommandType m_command;
100     const int m_numCalls;
101     const ConditionalData m_conditionalData;
102 };
103 
ConditionalDispatchTest(tcu::TestContext & testCtx,const std::string & name,const ConditionalTestSpec & testSpec)104 ConditionalDispatchTest::ConditionalDispatchTest(tcu::TestContext &testCtx, const std::string &name,
105                                                  const ConditionalTestSpec &testSpec)
106     : TestCase(testCtx, name)
107     , m_testSpec(testSpec)
108 {
109 }
110 
initPrograms(vk::SourceCollections & sourceCollections) const111 void ConditionalDispatchTest::initPrograms(vk::SourceCollections &sourceCollections) const
112 {
113     std::ostringstream src;
114     src << "#version 310 es\n"
115         << "layout(local_size_x = 1u, local_size_y = 1u, local_size_z = 1u) in;\n"
116         << "layout(set = 0, binding = 0, std140) buffer Out\n"
117         << "{\n"
118         << "    coherent uint count;\n"
119         << "};\n"
120         << "void main(void)\n"
121         << "{\n"
122         << "    atomicAdd(count, 1u);\n"
123         << "}\n";
124 
125     sourceCollections.glslSources.add("comp") << glu::ComputeSource(src.str());
126 }
127 
checkSupport(Context & context) const128 void ConditionalDispatchTest::checkSupport(Context &context) const
129 {
130     checkConditionalRenderingCapabilities(context, m_testSpec.conditionalData);
131 
132     if (m_testSpec.command == DISPATCH_COMMAND_TYPE_DISPATCH_BASE)
133         context.requireDeviceFunctionality("VK_KHR_device_group");
134 }
135 
createInstance(Context & context) const136 TestInstance *ConditionalDispatchTest::createInstance(Context &context) const
137 {
138     return new ConditionalDispatchTestInstance(context, m_testSpec);
139 }
140 
ConditionalDispatchTestInstance(Context & context,ConditionalTestSpec testSpec)141 ConditionalDispatchTestInstance::ConditionalDispatchTestInstance(Context &context, ConditionalTestSpec testSpec)
142     : TestInstance(context)
143     , m_command(testSpec.command)
144     , m_numCalls(testSpec.numCalls)
145     , m_conditionalData(testSpec.conditionalData)
146 {
147 }
148 
recordDispatch(const vk::DeviceInterface & vk,vk::VkCommandBuffer cmdBuffer,vk::BufferWithMemory & indirectBuffer)149 void ConditionalDispatchTestInstance::recordDispatch(const vk::DeviceInterface &vk, vk::VkCommandBuffer cmdBuffer,
150                                                      vk::BufferWithMemory &indirectBuffer)
151 {
152     for (int i = 0; i < m_numCalls; i++)
153     {
154         switch (m_command)
155         {
156         case DISPATCH_COMMAND_TYPE_DISPATCH:
157         {
158             vk.cmdDispatch(cmdBuffer, 1, 1, 1);
159             break;
160         }
161         case DISPATCH_COMMAND_TYPE_DISPATCH_INDIRECT:
162         {
163             vk.cmdDispatchIndirect(cmdBuffer, *indirectBuffer, 0);
164             break;
165         }
166         case DISPATCH_COMMAND_TYPE_DISPATCH_BASE:
167         {
168             vk.cmdDispatchBase(cmdBuffer, 0, 0, 0, 1, 1, 1);
169             break;
170         }
171         default:
172             DE_ASSERT(false);
173         }
174     }
175 }
176 
iterate(void)177 tcu::TestStatus ConditionalDispatchTestInstance::iterate(void)
178 {
179     const vk::DeviceInterface &vk   = m_context.getDeviceInterface();
180     const vk::VkDevice device       = m_context.getDevice();
181     const vk::VkQueue queue         = m_context.getUniversalQueue();
182     const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
183     vk::Allocator &allocator        = m_context.getDefaultAllocator();
184 
185     // Create a buffer and host-visible memory for it
186 
187     const vk::VkDeviceSize bufferSizeBytes = sizeof(uint32_t);
188     const vk::BufferWithMemory outputBuffer(
189         vk, device, allocator, vk::makeBufferCreateInfo(bufferSizeBytes, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
190         vk::MemoryRequirement::HostVisible);
191 
192     {
193         const vk::Allocation &alloc    = outputBuffer.getAllocation();
194         uint8_t *outputBufferPtr       = reinterpret_cast<uint8_t *>(alloc.getHostPtr());
195         *(uint32_t *)(outputBufferPtr) = 0;
196         vk::flushAlloc(vk, device, alloc);
197     }
198 
199     // Create descriptor set
200 
201     const vk::Unique<vk::VkDescriptorSetLayout> descriptorSetLayout(
202         vk::DescriptorSetLayoutBuilder()
203             .addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT)
204             .build(vk, device));
205 
206     const vk::Unique<vk::VkDescriptorPool> descriptorPool(
207         vk::DescriptorPoolBuilder()
208             .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
209             .build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
210 
211     const vk::Unique<vk::VkDescriptorSet> descriptorSet(
212         makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
213 
214     const vk::VkDescriptorBufferInfo descriptorInfo =
215         vk::makeDescriptorBufferInfo(*outputBuffer, 0ull, bufferSizeBytes);
216     vk::DescriptorSetUpdateBuilder()
217         .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u),
218                      vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfo)
219         .update(vk, device);
220 
221     // Setup pipeline
222 
223     const vk::Unique<vk::VkShaderModule> shaderModule(
224         createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0u));
225     const vk::Unique<vk::VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
226     const vk::Unique<vk::VkPipeline> pipeline(makeComputePipeline(vk, device, *pipelineLayout, *shaderModule));
227 
228     const vk::Unique<vk::VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
229     const vk::Unique<vk::VkCommandBuffer> cmdBuffer(
230         vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
231     const vk::Unique<vk::VkCommandBuffer> secondaryCmdBuffer(
232         vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_SECONDARY));
233     const vk::Unique<vk::VkCommandBuffer> nestedCmdBuffer(
234         vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_SECONDARY));
235 
236     // Create indirect buffer
237     const vk::VkDispatchIndirectCommand dispatchCommands[] = {{1u, 1u, 1u}};
238 
239     vk::BufferWithMemory indirectBuffer(
240         vk, device, allocator,
241         vk::makeBufferCreateInfo(sizeof(dispatchCommands),
242                                  vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
243         vk::MemoryRequirement::HostVisible);
244 
245     uint8_t *indirectBufferPtr = reinterpret_cast<uint8_t *>(indirectBuffer.getAllocation().getHostPtr());
246     deMemcpy(indirectBufferPtr, &dispatchCommands[0], sizeof(dispatchCommands));
247 
248     vk::flushAlloc(vk, device, indirectBuffer.getAllocation());
249 
250     // Start recording commands
251 
252     beginCommandBuffer(vk, *cmdBuffer);
253 
254     vk::VkCommandBuffer targetCmdBuffer = *cmdBuffer;
255 
256     const bool useSecondaryCmdBuffer =
257         m_conditionalData.conditionInherited || m_conditionalData.conditionInSecondaryCommandBuffer;
258 
259     if (useSecondaryCmdBuffer)
260     {
261         const vk::VkCommandBufferInheritanceConditionalRenderingInfoEXT conditionalRenderingInheritanceInfo = {
262             vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT, DE_NULL,
263             m_conditionalData.conditionInherited ? VK_TRUE : VK_FALSE // conditionalRenderingEnable
264         };
265 
266         const vk::VkCommandBufferInheritanceInfo inheritanceInfo = {
267             vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
268             &conditionalRenderingInheritanceInfo,
269             0u,                                    // renderPass
270             0u,                                    // subpass
271             0u,                                    // framebuffer
272             VK_FALSE,                              // occlusionQueryEnable
273             (vk::VkQueryControlFlags)0u,           // queryFlags
274             (vk::VkQueryPipelineStatisticFlags)0u, // pipelineStatistics
275         };
276 
277         const vk::VkCommandBufferBeginInfo commandBufferBeginInfo = {
278             vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, DE_NULL, vk::VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
279             &inheritanceInfo};
280 
281         if (m_conditionalData.secondaryCommandBufferNested)
282         {
283             VK_CHECK(vk.beginCommandBuffer(*nestedCmdBuffer, &commandBufferBeginInfo));
284         }
285 
286         VK_CHECK(vk.beginCommandBuffer(*secondaryCmdBuffer, &commandBufferBeginInfo));
287 
288         targetCmdBuffer = *secondaryCmdBuffer;
289     }
290 
291     vk.cmdBindPipeline(targetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
292     vk.cmdBindDescriptorSets(targetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u,
293                              &descriptorSet.get(), 0u, DE_NULL);
294 
295     de::SharedPtr<Draw::Buffer> conditionalBuffer = createConditionalRenderingBuffer(m_context, m_conditionalData);
296 
297     if (m_conditionalData.conditionInSecondaryCommandBuffer)
298     {
299         beginConditionalRendering(vk, *secondaryCmdBuffer, *conditionalBuffer, m_conditionalData);
300         recordDispatch(vk, *secondaryCmdBuffer, indirectBuffer);
301         vk.cmdEndConditionalRenderingEXT(*secondaryCmdBuffer);
302         vk.endCommandBuffer(*secondaryCmdBuffer);
303         if (m_conditionalData.secondaryCommandBufferNested)
304         {
305             vk.cmdExecuteCommands(*nestedCmdBuffer, 1, &secondaryCmdBuffer.get());
306             vk.endCommandBuffer(*nestedCmdBuffer);
307         }
308     }
309     else if (m_conditionalData.conditionInherited)
310     {
311         recordDispatch(vk, *secondaryCmdBuffer, indirectBuffer);
312         vk.endCommandBuffer(*secondaryCmdBuffer);
313         if (m_conditionalData.secondaryCommandBufferNested)
314         {
315             vk.cmdExecuteCommands(*nestedCmdBuffer, 1, &secondaryCmdBuffer.get());
316             vk.endCommandBuffer(*nestedCmdBuffer);
317         }
318     }
319 
320     if (m_conditionalData.conditionInPrimaryCommandBuffer)
321     {
322         beginConditionalRendering(vk, *cmdBuffer, *conditionalBuffer, m_conditionalData);
323 
324         if (m_conditionalData.conditionInherited)
325         {
326             if (m_conditionalData.secondaryCommandBufferNested)
327             {
328                 vk.cmdExecuteCommands(*cmdBuffer, 1, &nestedCmdBuffer.get());
329             }
330             else
331             {
332                 vk.cmdExecuteCommands(*cmdBuffer, 1, &secondaryCmdBuffer.get());
333             }
334         }
335         else
336         {
337             recordDispatch(vk, *cmdBuffer, indirectBuffer);
338         }
339 
340         vk.cmdEndConditionalRenderingEXT(*cmdBuffer);
341     }
342     else if (useSecondaryCmdBuffer)
343     {
344         if (m_conditionalData.secondaryCommandBufferNested)
345         {
346             vk.cmdExecuteCommands(*cmdBuffer, 1, &nestedCmdBuffer.get());
347         }
348         else
349         {
350             vk.cmdExecuteCommands(*cmdBuffer, 1, &secondaryCmdBuffer.get());
351         }
352     }
353 
354     const vk::VkBufferMemoryBarrier outputBufferMemoryBarrier = {vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
355                                                                  DE_NULL,
356                                                                  vk::VK_ACCESS_SHADER_WRITE_BIT,
357                                                                  vk::VK_ACCESS_HOST_READ_BIT,
358                                                                  VK_QUEUE_FAMILY_IGNORED,
359                                                                  VK_QUEUE_FAMILY_IGNORED,
360                                                                  outputBuffer.get(),
361                                                                  0u,
362                                                                  VK_WHOLE_SIZE};
363 
364     vk.cmdPipelineBarrier(*cmdBuffer, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u,
365                           DE_NULL, 1u, &outputBufferMemoryBarrier, 0u, DE_NULL);
366 
367     endCommandBuffer(vk, *cmdBuffer);
368 
369     submitCommandsAndWait(vk, device, queue, *cmdBuffer);
370 
371     // Check result
372 
373     qpTestResult res = QP_TEST_RESULT_PASS;
374 
375     const vk::Allocation &outputBufferAllocation = outputBuffer.getAllocation();
376     invalidateAlloc(vk, device, outputBufferAllocation);
377 
378     const uint32_t *bufferPtr = static_cast<uint32_t *>(outputBufferAllocation.getHostPtr());
379 
380     const uint32_t expectedResult = m_conditionalData.expectCommandExecution ? m_numCalls : 0u;
381     if (bufferPtr[0] != expectedResult)
382     {
383         res = QP_TEST_RESULT_FAIL;
384     }
385 
386     return tcu::TestStatus(res, qpGetTestResultName(res));
387 }
388 
389 } // namespace
390 
ConditionalDispatchTests(tcu::TestContext & testCtx)391 ConditionalDispatchTests::ConditionalDispatchTests(tcu::TestContext &testCtx) : TestCaseGroup(testCtx, "dispatch")
392 {
393     /* Left blank on purpose */
394 }
395 
~ConditionalDispatchTests(void)396 ConditionalDispatchTests::~ConditionalDispatchTests(void)
397 {
398 }
399 
init(void)400 void ConditionalDispatchTests::init(void)
401 {
402     for (int conditionNdx = 0; conditionNdx < DE_LENGTH_OF_ARRAY(conditional::s_testsData); conditionNdx++)
403     {
404         const ConditionalData &conditionData = conditional::s_testsData[conditionNdx];
405 
406         if (conditionData.clearInRenderPass)
407             continue;
408 
409         de::MovePtr<tcu::TestCaseGroup> conditionalDrawRootGroup(
410             new tcu::TestCaseGroup(m_testCtx, de::toString(conditionData).c_str()));
411 
412         for (uint32_t commandTypeIdx = 0; commandTypeIdx < DISPATCH_COMMAND_TYPE_DISPATCH_LAST; ++commandTypeIdx)
413         {
414             const DispatchCommandType command = DispatchCommandType(commandTypeIdx);
415 
416             ConditionalTestSpec testSpec;
417             testSpec.command         = command;
418             testSpec.numCalls        = 3;
419             testSpec.conditionalData = conditionData;
420 
421             conditionalDrawRootGroup->addChild(
422                 new ConditionalDispatchTest(m_testCtx, getDispatchCommandTypeName(command), testSpec));
423         }
424 
425         addChild(conditionalDrawRootGroup.release());
426     }
427 
428     enum class ConditionLocation
429     {
430         PRIMARY_FLAT = 0,
431         PRIMARY_WITH_SECONDARY,
432         SECONDARY_NORMAL,
433         SECONDARY_INHERITED,
434     };
435 
436     // Tests verifying the condition is interpreted as a 32-bit value.
437     {
438         de::MovePtr<tcu::TestCaseGroup> conditionSizeGroup(new tcu::TestCaseGroup(m_testCtx, "condition_size"));
439 
440         struct ValuePaddingExecution
441         {
442             uint32_t value;
443             bool padding;
444             bool execution;
445             const char *name;
446         };
447 
448         const ValuePaddingExecution kConditionValueResults[] = {
449             {0x00000001u, false, true, "first_byte"}, {0x00000100u, false, true, "second_byte"},
450             {0x00010000u, false, true, "third_byte"}, {0x01000000u, false, true, "fourth_byte"},
451             {0u, true, false, "padded_zero"},
452         };
453 
454         struct ConditionLocationSubcase
455         {
456             ConditionLocation location;
457             const char *name;
458         };
459 
460         const ConditionLocationSubcase kConditionLocationSubcase[] = {
461             {ConditionLocation::PRIMARY_FLAT, "primary"},
462             {ConditionLocation::PRIMARY_WITH_SECONDARY, "inherited"},
463             {ConditionLocation::SECONDARY_NORMAL, "secondary"},
464             {ConditionLocation::SECONDARY_INHERITED, "secondary_inherited"},
465         };
466 
467         for (int subcaseNdx = 0; subcaseNdx < DE_LENGTH_OF_ARRAY(kConditionLocationSubcase); ++subcaseNdx)
468         {
469             const auto &subcase = kConditionLocationSubcase[subcaseNdx];
470 
471             de::MovePtr<tcu::TestCaseGroup> subcaseGroup(new tcu::TestCaseGroup(m_testCtx, subcase.name));
472 
473             ConditionalData conditionalData   = {};
474             conditionalData.conditionInverted = false;
475 
476             switch (subcase.location)
477             {
478             case ConditionLocation::PRIMARY_FLAT:
479                 conditionalData.conditionInPrimaryCommandBuffer   = true;
480                 conditionalData.conditionInSecondaryCommandBuffer = false;
481                 conditionalData.conditionInherited                = false;
482                 break;
483 
484             case ConditionLocation::PRIMARY_WITH_SECONDARY:
485                 conditionalData.conditionInPrimaryCommandBuffer   = true;
486                 conditionalData.conditionInSecondaryCommandBuffer = false;
487                 conditionalData.conditionInherited                = true;
488                 break;
489 
490             case ConditionLocation::SECONDARY_NORMAL:
491                 conditionalData.conditionInPrimaryCommandBuffer   = false;
492                 conditionalData.conditionInSecondaryCommandBuffer = true;
493                 conditionalData.conditionInherited                = false;
494                 break;
495 
496             case ConditionLocation::SECONDARY_INHERITED:
497                 conditionalData.conditionInPrimaryCommandBuffer   = false;
498                 conditionalData.conditionInSecondaryCommandBuffer = true;
499                 conditionalData.conditionInherited                = true;
500                 break;
501 
502             default:
503                 DE_ASSERT(false);
504                 break;
505             }
506 
507             for (int valueNdx = 0; valueNdx < DE_LENGTH_OF_ARRAY(kConditionValueResults); ++valueNdx)
508             {
509                 const auto &valueResults = kConditionValueResults[valueNdx];
510 
511                 conditionalData.conditionValue         = valueResults.value;
512                 conditionalData.padConditionValue      = valueResults.padding;
513                 conditionalData.expectCommandExecution = valueResults.execution;
514 
515                 ConditionalTestSpec spec;
516                 spec.command         = DISPATCH_COMMAND_TYPE_DISPATCH;
517                 spec.numCalls        = 1;
518                 spec.conditionalData = conditionalData;
519 
520                 subcaseGroup->addChild(new ConditionalDispatchTest(m_testCtx, valueResults.name, spec));
521             }
522 
523             conditionSizeGroup->addChild(subcaseGroup.release());
524         }
525 
526         addChild(conditionSizeGroup.release());
527     }
528 
529     // Tests checking the buffer allocation offset is applied correctly when reading the condition.
530     {
531         de::MovePtr<tcu::TestCaseGroup> allocOffsetGroup(new tcu::TestCaseGroup(m_testCtx, "alloc_offset"));
532 
533         const struct
534         {
535             ConditionLocation location;
536             const char *name;
537         } kLocationCases[] = {
538             {ConditionLocation::PRIMARY_FLAT, "primary"},
539             {ConditionLocation::PRIMARY_WITH_SECONDARY, "inherited"},
540             {ConditionLocation::SECONDARY_NORMAL, "secondary"},
541             {ConditionLocation::SECONDARY_INHERITED, "secondary_inherited"},
542         };
543 
544         const struct
545         {
546             bool active;
547             const char *name;
548         } kActiveCases[] = {
549             {false, "zero"},
550             {true, "nonzero"},
551         };
552 
553         const struct
554         {
555             ConditionalBufferMemory memoryType;
556             const char *name;
557         } kMemoryTypeCases[] = {
558             {LOCAL, "device_local"},
559             {HOST, "host_visible"},
560         };
561 
562         for (const auto &locationCase : kLocationCases)
563         {
564             de::MovePtr<tcu::TestCaseGroup> locationSubGroup(new tcu::TestCaseGroup(m_testCtx, locationCase.name));
565 
566             for (const auto &activeCase : kActiveCases)
567             {
568                 de::MovePtr<tcu::TestCaseGroup> activeSubGroup(new tcu::TestCaseGroup(m_testCtx, activeCase.name));
569 
570                 for (const auto &memoryTypeCase : kMemoryTypeCases)
571                 {
572                     ConditionalData conditionalData = {
573                         false,                     // bool conditionInPrimaryCommandBuffer;
574                         false,                     // bool conditionInSecondaryCommandBuffer;
575                         false,                     // bool conditionInverted;
576                         false,                     // bool conditionInherited;
577                         0u,                        // uint32_t conditionValue;
578                         false,                     // bool padConditionValue;
579                         true,                      // bool allocationOffset;
580                         false,                     // bool clearInRenderPass;
581                         false,                     // bool expectCommandExecution;
582                         false,                     // bool secondaryCommandBufferNested;
583                         memoryTypeCase.memoryType, // ConditionalBufferMemory memoryType;
584                     };
585 
586                     switch (locationCase.location)
587                     {
588                     case ConditionLocation::PRIMARY_FLAT:
589                         conditionalData.conditionInPrimaryCommandBuffer   = true;
590                         conditionalData.conditionInSecondaryCommandBuffer = false;
591                         conditionalData.conditionInherited                = false;
592                         break;
593 
594                     case ConditionLocation::PRIMARY_WITH_SECONDARY:
595                         conditionalData.conditionInPrimaryCommandBuffer   = true;
596                         conditionalData.conditionInSecondaryCommandBuffer = false;
597                         conditionalData.conditionInherited                = true;
598                         break;
599 
600                     case ConditionLocation::SECONDARY_NORMAL:
601                         conditionalData.conditionInPrimaryCommandBuffer   = false;
602                         conditionalData.conditionInSecondaryCommandBuffer = true;
603                         conditionalData.conditionInherited                = false;
604                         break;
605 
606                     case ConditionLocation::SECONDARY_INHERITED:
607                         conditionalData.conditionInPrimaryCommandBuffer   = false;
608                         conditionalData.conditionInSecondaryCommandBuffer = true;
609                         conditionalData.conditionInherited                = true;
610                         break;
611 
612                     default:
613                         DE_ASSERT(false);
614                         break;
615                     }
616 
617                     conditionalData.conditionValue         = (activeCase.active ? 1u : 0u);
618                     conditionalData.expectCommandExecution = activeCase.active;
619 
620                     const ConditionalTestSpec spec = {
621                         DISPATCH_COMMAND_TYPE_DISPATCH, // DispatchCommandType command;
622                         1,                              // int numCalls;
623                         conditionalData,                // ConditionalData conditionalData;
624                     };
625 
626                     activeSubGroup->addChild(new ConditionalDispatchTest(m_testCtx, memoryTypeCase.name, spec));
627                 }
628 
629                 locationSubGroup->addChild(activeSubGroup.release());
630             }
631 
632             allocOffsetGroup->addChild(locationSubGroup.release());
633         }
634 
635         addChild(allocOffsetGroup.release());
636     }
637 }
638 
639 } // namespace conditional
640 } // namespace vkt
641