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 vkCmdDraw* functions
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktConditionalDrawTests.hpp"
26 #include "vktConditionalRenderingTestUtil.hpp"
27 
28 #include "vktTestCaseUtil.hpp"
29 #include "vktDrawTestCaseUtil.hpp"
30 
31 #include "vktDrawBaseClass.hpp"
32 
33 #include "vkTypeUtil.hpp"
34 
35 #include "tcuTestLog.hpp"
36 #include "tcuResource.hpp"
37 #include "tcuImageCompare.hpp"
38 #include "tcuTextureUtil.hpp"
39 #include "tcuRGBA.hpp"
40 
41 #include "vkDefs.hpp"
42 #include "vkCmdUtil.hpp"
43 
44 namespace vkt
45 {
46 namespace conditional
47 {
48 namespace
49 {
50 
51 enum DrawCommandType
52 {
53     DRAW_COMMAND_TYPE_DRAW = 0,
54     DRAW_COMMAND_TYPE_DRAW_INDEXED,
55     DRAW_COMMAND_TYPE_DRAW_INDIRECT,
56     DRAW_COMMAND_TYPE_DRAW_INDEXED_INDIRECT,
57     DRAW_COMMAND_TYPE_DRAW_INDIRECT_COUNT,
58     DRAW_COMMAND_TYPE_DRAW_INDEXED_INDIRECT_COUNT,
59 
60     DRAW_COMMAND_TYPE_DRAW_LAST
61 };
62 
getDrawCommandTypeName(DrawCommandType command)63 const char *getDrawCommandTypeName(DrawCommandType command)
64 {
65     switch (command)
66     {
67     case DRAW_COMMAND_TYPE_DRAW:
68         return "draw";
69     case DRAW_COMMAND_TYPE_DRAW_INDEXED:
70         return "draw_indexed";
71     case DRAW_COMMAND_TYPE_DRAW_INDIRECT:
72         return "draw_indirect";
73     case DRAW_COMMAND_TYPE_DRAW_INDEXED_INDIRECT:
74         return "draw_indexed_indirect";
75     case DRAW_COMMAND_TYPE_DRAW_INDIRECT_COUNT:
76         return "draw_indirect_count";
77     case DRAW_COMMAND_TYPE_DRAW_INDEXED_INDIRECT_COUNT:
78         return "draw_indexed_indirect_count";
79     default:
80         DE_ASSERT(false);
81     }
82     return "";
83 }
84 
85 struct ConditionalTestSpec : public Draw::TestSpecBase
86 {
87     DrawCommandType command;
88     uint32_t drawCalls;
89     ConditionalData conditionalData;
90 };
91 
92 class ConditionalDraw : public Draw::DrawTestsBaseClass
93 {
94 public:
95     typedef ConditionalTestSpec TestSpec;
96 
97     ConditionalDraw(Context &context, ConditionalTestSpec testSpec);
98 
99     virtual tcu::TestStatus iterate(void);
100     void createAndBindIndexBuffer(vk::VkCommandBuffer cmdBuffer);
101     void createIndirectBuffer(void);
102     void createIndexedIndirectBuffer(void);
103     void createIndirectCountBuffer(void);
104     void recordDraw(vk::VkCommandBuffer cmdBuffer);
105 
106 protected:
107     void createRenderPassWithClear(void);
108 
109     const DrawCommandType m_command;
110     const uint32_t m_drawCalls;
111 
112     const ConditionalData m_conditionalData;
113     de::SharedPtr<Draw::Buffer> m_conditionalBuffer;
114 
115     vk::Move<vk::VkCommandBuffer> m_secondaryCmdBuffer;
116     vk::Move<vk::VkCommandBuffer> m_nestedCmdBuffer;
117 
118     std::vector<uint32_t> m_indexes;
119     de::SharedPtr<Draw::Buffer> m_indexBuffer;
120 
121     de::SharedPtr<Draw::Buffer> m_indirectBuffer;
122     de::SharedPtr<Draw::Buffer> m_indirectCountBuffer;
123 
124     // For cases where we want to clear the attachment in the render pass begin operation.
125     vk::Move<vk::VkRenderPass> m_rpWithClear;
126     vk::Move<vk::VkFramebuffer> m_fbWithClear;
127 };
128 
checkSupport(Context & context,DrawCommandType command)129 void checkSupport(Context &context, DrawCommandType command)
130 {
131     if (command == DRAW_COMMAND_TYPE_DRAW_INDIRECT_COUNT || command == DRAW_COMMAND_TYPE_DRAW_INDEXED_INDIRECT_COUNT)
132         context.requireDeviceFunctionality("VK_KHR_draw_indirect_count");
133 }
134 
ConditionalDraw(Context & context,ConditionalTestSpec testSpec)135 ConditionalDraw::ConditionalDraw(Context &context, ConditionalTestSpec testSpec)
136     : Draw::DrawTestsBaseClass(context, testSpec.shaders[glu::SHADERTYPE_VERTEX],
137                                testSpec.shaders[glu::SHADERTYPE_FRAGMENT],
138                                Draw::SharedGroupParams(new Draw::GroupParams{false, false, false, false}),
139                                vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
140     , m_command(testSpec.command)
141     , m_drawCalls(testSpec.drawCalls)
142     , m_conditionalData(testSpec.conditionalData)
143 {
144     checkConditionalRenderingCapabilities(context, m_conditionalData);
145     checkNestedRenderPassCapabilities(context);
146     checkSupport(context, m_command);
147 
148     const float minX     = -0.3f;
149     const float maxX     = 0.3f;
150     const float drawStep = 0.6f / static_cast<float>(m_drawCalls);
151 
152     for (uint32_t drawIdx = 0; drawIdx < m_drawCalls; drawIdx++)
153     {
154         const float minY = minX + static_cast<float>(drawIdx) * drawStep;
155         const float maxY = minY + drawStep;
156 
157         m_data.push_back(Draw::VertexElementData(tcu::Vec4(minX, maxY, 0.5f, 1.0f), tcu::RGBA::blue().toVec(), 0));
158         m_data.push_back(Draw::VertexElementData(tcu::Vec4(minX, minY, 0.5f, 1.0f), tcu::RGBA::blue().toVec(), 0));
159         m_data.push_back(Draw::VertexElementData(tcu::Vec4(maxX, maxY, 0.5f, 1.0f), tcu::RGBA::blue().toVec(), 0));
160 
161         m_data.push_back(Draw::VertexElementData(tcu::Vec4(minX, minY, 0.5f, 1.0f), tcu::RGBA::blue().toVec(), 0));
162         m_data.push_back(Draw::VertexElementData(tcu::Vec4(maxX, maxY, 0.5f, 1.0f), tcu::RGBA::blue().toVec(), 0));
163         m_data.push_back(Draw::VertexElementData(tcu::Vec4(maxX, minY, 0.5f, 1.0f), tcu::RGBA::blue().toVec(), 0));
164     }
165 
166     m_data.push_back(Draw::VertexElementData(tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), tcu::RGBA::red().toVec(), 0));
167     m_data.push_back(Draw::VertexElementData(tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), tcu::RGBA::red().toVec(), 0));
168     m_data.push_back(Draw::VertexElementData(tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f), tcu::RGBA::red().toVec(), 0));
169 
170     m_data.push_back(Draw::VertexElementData(tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), tcu::RGBA::red().toVec(), 0));
171     m_data.push_back(Draw::VertexElementData(tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f), tcu::RGBA::red().toVec(), 0));
172     m_data.push_back(Draw::VertexElementData(tcu::Vec4(1.0f, -1.0f, 0.0f, 1.0f), tcu::RGBA::red().toVec(), 0));
173 
174     for (uint32_t index = 0; index < m_data.size(); index++)
175     {
176         m_indexes.push_back(index);
177     }
178 
179     initialize();
180 
181     DE_ASSERT(!(m_conditionalData.clearInRenderPass && m_conditionalData.conditionInSecondaryCommandBuffer));
182 
183     if (m_conditionalData.clearInRenderPass)
184         createRenderPassWithClear();
185 
186     m_secondaryCmdBuffer =
187         vk::allocateCommandBuffer(m_vk, m_context.getDevice(), *m_cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_SECONDARY);
188     m_nestedCmdBuffer =
189         vk::allocateCommandBuffer(m_vk, m_context.getDevice(), *m_cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_SECONDARY);
190 }
191 
createRenderPassWithClear(void)192 void ConditionalDraw::createRenderPassWithClear(void)
193 {
194     const auto device = m_context.getDevice();
195     Draw::RenderPassCreateInfo renderPassCreateInfo;
196 
197     renderPassCreateInfo.addAttachment(Draw::AttachmentDescription(
198         m_colorAttachmentFormat, vk::VK_SAMPLE_COUNT_1_BIT,
199         vk::VK_ATTACHMENT_LOAD_OP_CLEAR, // Clear with the render pass.
200         vk::VK_ATTACHMENT_STORE_OP_STORE, vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, vk::VK_ATTACHMENT_STORE_OP_STORE,
201         vk::VK_IMAGE_LAYOUT_UNDEFINED, vk::VK_IMAGE_LAYOUT_GENERAL));
202 
203     const vk::VkAttachmentReference colorAttachmentReference{0, vk::VK_IMAGE_LAYOUT_GENERAL};
204 
205     renderPassCreateInfo.addSubpass(Draw::SubpassDescription(vk::VK_PIPELINE_BIND_POINT_GRAPHICS, 0, 0, DE_NULL, 1,
206                                                              &colorAttachmentReference, DE_NULL,
207                                                              Draw::AttachmentReference(), 0, DE_NULL));
208 
209     m_rpWithClear = vk::createRenderPass(m_vk, device, &renderPassCreateInfo);
210 
211     // Framebuffer.
212     std::vector<vk::VkImageView> colorAttachments{*m_colorTargetView};
213     const Draw::FramebufferCreateInfo framebufferCreateInfo(*m_rpWithClear, colorAttachments, WIDTH, HEIGHT, 1);
214 
215     m_fbWithClear = vk::createFramebuffer(m_vk, device, &framebufferCreateInfo);
216 }
217 
createAndBindIndexBuffer(vk::VkCommandBuffer cmdBuffer)218 void ConditionalDraw::createAndBindIndexBuffer(vk::VkCommandBuffer cmdBuffer)
219 {
220     const vk::VkDeviceSize indexDataSize = m_indexes.size() * sizeof(uint32_t);
221     m_indexBuffer                        = Draw::Buffer::createAndAlloc(
222         m_vk, m_context.getDevice(), Draw::BufferCreateInfo(indexDataSize, vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT),
223         m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible);
224 
225     uint8_t *indexBufferPtr = reinterpret_cast<uint8_t *>(m_indexBuffer->getBoundMemory().getHostPtr());
226     deMemcpy(indexBufferPtr, &m_indexes[0], static_cast<size_t>(indexDataSize));
227 
228     vk::flushAlloc(m_vk, m_context.getDevice(), m_indexBuffer->getBoundMemory());
229 
230     const vk::VkBuffer indexBuffer = m_indexBuffer->object();
231     m_vk.cmdBindIndexBuffer(cmdBuffer, indexBuffer, 0, vk::VK_INDEX_TYPE_UINT32);
232 }
233 
createIndirectBuffer(void)234 void ConditionalDraw::createIndirectBuffer(void)
235 {
236     const vk::VkDrawIndirectCommand badDrawCommand = {
237         6u,               // vertexCount
238         1u,               // instanceCount
239         m_drawCalls * 6u, // firstVertex
240         0u                // firstInstance
241     };
242 
243     std::vector<vk::VkDrawIndirectCommand> drawCommands;
244     for (uint32_t drawIdx = 0; drawIdx < m_drawCalls; drawIdx++)
245     {
246         const vk::VkDrawIndirectCommand goodDrawCommand = {
247             6u,           // vertexCount
248             1u,           // instanceCount
249             6u * drawIdx, // firstVertex
250             0u            // firstInstance
251         };
252 
253         drawCommands.push_back(goodDrawCommand);
254         // *Bad* commands should not be rendered by vkCmdDrawIndirectCountKHR
255         drawCommands.push_back(badDrawCommand);
256         drawCommands.push_back(badDrawCommand);
257     }
258 
259     const vk::VkDeviceSize drawCommandsSize = drawCommands.size() * sizeof(vk::VkDrawIndirectCommand);
260 
261     m_indirectBuffer = Draw::Buffer::createAndAlloc(
262         m_vk, m_context.getDevice(), Draw::BufferCreateInfo(drawCommandsSize, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT),
263         m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible);
264 
265     uint8_t *ptr = reinterpret_cast<uint8_t *>(m_indirectBuffer->getBoundMemory().getHostPtr());
266     deMemcpy(ptr, &drawCommands[0], static_cast<size_t>(drawCommandsSize));
267 
268     vk::flushAlloc(m_vk, m_context.getDevice(), m_indirectBuffer->getBoundMemory());
269 }
270 
createIndexedIndirectBuffer(void)271 void ConditionalDraw::createIndexedIndirectBuffer(void)
272 {
273     const vk::VkDrawIndexedIndirectCommand badDrawCommand = {
274         6u,               // indexCount
275         1u,               // instanceCount
276         m_drawCalls * 6u, // firstIndex
277         0u,               // vertexOffset
278         0u,               // firstInstance
279     };
280 
281     std::vector<vk::VkDrawIndexedIndirectCommand> drawCommands;
282     for (uint32_t drawIdx = 0; drawIdx < m_drawCalls; drawIdx++)
283     {
284         const vk::VkDrawIndexedIndirectCommand goodDrawCommand = {
285             6u,           // indexCount
286             1u,           // instanceCount
287             6u * drawIdx, // firstIndex
288             0u,           // vertexOffset
289             0u,           // firstInstance
290         };
291 
292         drawCommands.push_back(goodDrawCommand);
293         // *Bad* commands should not be rendered by vkCmdDrawIndexedIndirectCountKHR
294         drawCommands.push_back(badDrawCommand);
295         drawCommands.push_back(badDrawCommand);
296     }
297 
298     const vk::VkDeviceSize drawCommandsSize = drawCommands.size() * sizeof(vk::VkDrawIndexedIndirectCommand);
299 
300     m_indirectBuffer = Draw::Buffer::createAndAlloc(
301         m_vk, m_context.getDevice(), Draw::BufferCreateInfo(drawCommandsSize, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT),
302         m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible);
303 
304     uint8_t *ptr = reinterpret_cast<uint8_t *>(m_indirectBuffer->getBoundMemory().getHostPtr());
305     deMemcpy(ptr, &drawCommands[0], static_cast<size_t>(drawCommandsSize));
306 
307     vk::flushAlloc(m_vk, m_context.getDevice(), m_indirectBuffer->getBoundMemory());
308 }
309 
createIndirectCountBuffer(void)310 void ConditionalDraw::createIndirectCountBuffer(void)
311 {
312     m_indirectCountBuffer = Draw::Buffer::createAndAlloc(
313         m_vk, m_context.getDevice(), Draw::BufferCreateInfo(sizeof(uint32_t), vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT),
314         m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible);
315 
316     uint8_t *countBufferPtr       = reinterpret_cast<uint8_t *>(m_indirectCountBuffer->getBoundMemory().getHostPtr());
317     *(uint32_t *)(countBufferPtr) = 1;
318 
319     vk::flushAlloc(m_vk, m_context.getDevice(), m_indirectCountBuffer->getBoundMemory());
320 }
321 
recordDraw(vk::VkCommandBuffer cmdBuffer)322 void ConditionalDraw::recordDraw(vk::VkCommandBuffer cmdBuffer)
323 {
324     for (uint32_t drawIdx = 0; drawIdx < m_drawCalls; drawIdx++)
325     {
326         /* Indirect buffer has next layout:
327          * goodCommand badCommand badCommand goodCommand badCommand badCommand ...
328          */
329         const vk::VkDeviceSize indirectOffset        = sizeof(vk::VkDrawIndirectCommand) * drawIdx * 3;
330         const vk::VkDeviceSize indexedIndirectOffset = sizeof(vk::VkDrawIndexedIndirectCommand) * drawIdx * 3;
331         switch (m_command)
332         {
333         case DRAW_COMMAND_TYPE_DRAW:
334         {
335             m_vk.cmdDraw(cmdBuffer, 6, 1, 6 * drawIdx, 0);
336             break;
337         }
338         case DRAW_COMMAND_TYPE_DRAW_INDEXED:
339         {
340             m_vk.cmdDrawIndexed(cmdBuffer, 6, 1, 6 * drawIdx, 0, 0);
341             break;
342         }
343         case DRAW_COMMAND_TYPE_DRAW_INDIRECT:
344         {
345             m_vk.cmdDrawIndirect(cmdBuffer, m_indirectBuffer->object(), indirectOffset, 1, 0);
346             break;
347         }
348         case DRAW_COMMAND_TYPE_DRAW_INDEXED_INDIRECT:
349         {
350             m_vk.cmdDrawIndexedIndirect(cmdBuffer, m_indirectBuffer->object(), indexedIndirectOffset, 1, 0);
351             break;
352         }
353         case DRAW_COMMAND_TYPE_DRAW_INDIRECT_COUNT:
354         {
355             m_vk.cmdDrawIndirectCount(cmdBuffer, m_indirectBuffer->object(), indirectOffset,
356                                       m_indirectCountBuffer->object(), 0, 3, sizeof(vk::VkDrawIndirectCommand));
357             break;
358         }
359         case DRAW_COMMAND_TYPE_DRAW_INDEXED_INDIRECT_COUNT:
360         {
361             m_vk.cmdDrawIndexedIndirectCount(cmdBuffer, m_indirectBuffer->object(), indexedIndirectOffset,
362                                              m_indirectCountBuffer->object(), 0, 3,
363                                              sizeof(vk::VkDrawIndexedIndirectCommand));
364             break;
365         }
366         default:
367             DE_ASSERT(false);
368         }
369     }
370 }
371 
iterate(void)372 tcu::TestStatus ConditionalDraw::iterate(void)
373 {
374     tcu::TestLog &log         = m_context.getTestContext().getLog();
375     const vk::VkQueue queue   = m_context.getUniversalQueue();
376     const vk::VkDevice device = m_context.getDevice();
377 
378     // We will clear to a different color to be sure.
379     const auto clearColor =
380         (m_conditionalData.clearInRenderPass ? tcu::RGBA::white().toVec() : tcu::RGBA::black().toVec());
381 
382     m_conditionalBuffer = createConditionalRenderingBuffer(m_context, m_conditionalData);
383 
384     beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
385     preRenderBarriers();
386 
387     const bool useSecondaryCmdBuffer =
388         m_conditionalData.conditionInherited || m_conditionalData.conditionInSecondaryCommandBuffer;
389     const auto subpassContents =
390         (useSecondaryCmdBuffer ? vk::VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : vk::VK_SUBPASS_CONTENTS_INLINE);
391 
392     if (m_conditionalData.clearInRenderPass)
393     {
394         // When clearing in the render pass we want to check the render pass clear is executed properly.
395         beginConditionalRendering(m_vk, *m_cmdBuffer, *m_conditionalBuffer, m_conditionalData);
396         vk::beginRenderPass(m_vk, *m_cmdBuffer, *m_rpWithClear, *m_fbWithClear, vk::makeRect2D(WIDTH, HEIGHT),
397                             clearColor, subpassContents);
398     }
399     else
400     {
401         beginLegacyRender(*m_cmdBuffer, subpassContents);
402     }
403 
404     vk::VkCommandBuffer targetCmdBuffer = *m_cmdBuffer;
405 
406     if (useSecondaryCmdBuffer)
407     {
408         const vk::VkCommandBufferInheritanceConditionalRenderingInfoEXT conditionalRenderingInheritanceInfo = {
409             vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT, DE_NULL,
410             m_conditionalData.conditionInherited ? VK_TRUE : VK_FALSE // conditionalRenderingEnable
411         };
412 
413         const vk::VkCommandBufferInheritanceInfo inheritanceInfo = {
414             vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
415             &conditionalRenderingInheritanceInfo,
416             *m_renderPass,                         // renderPass
417             0u,                                    // subpass
418             *m_framebuffer,                        // framebuffer
419             VK_FALSE,                              // occlusionQueryEnable
420             (vk::VkQueryControlFlags)0u,           // queryFlags
421             (vk::VkQueryPipelineStatisticFlags)0u, // pipelineStatistics
422         };
423 
424         const vk::VkCommandBufferBeginInfo commandBufferBeginInfo = {
425             vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, DE_NULL,
426             vk::VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, &inheritanceInfo};
427 
428         if (m_conditionalData.secondaryCommandBufferNested)
429         {
430             VK_CHECK(m_vk.beginCommandBuffer(*m_nestedCmdBuffer, &commandBufferBeginInfo));
431         }
432 
433         VK_CHECK(m_vk.beginCommandBuffer(*m_secondaryCmdBuffer, &commandBufferBeginInfo));
434 
435         targetCmdBuffer = *m_secondaryCmdBuffer;
436     }
437 
438     const vk::VkDeviceSize vertexBufferOffset = 0;
439     const vk::VkBuffer vertexBuffer           = m_vertexBuffer->object();
440 
441     m_vk.cmdBindVertexBuffers(targetCmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
442 
443     switch (m_command)
444     {
445     case DRAW_COMMAND_TYPE_DRAW:
446     {
447         break;
448     }
449     case DRAW_COMMAND_TYPE_DRAW_INDEXED:
450     {
451         createAndBindIndexBuffer(targetCmdBuffer);
452         break;
453     }
454     case DRAW_COMMAND_TYPE_DRAW_INDIRECT:
455     {
456         createIndirectBuffer();
457         break;
458     }
459     case DRAW_COMMAND_TYPE_DRAW_INDEXED_INDIRECT:
460     {
461         createAndBindIndexBuffer(targetCmdBuffer);
462         createIndexedIndirectBuffer();
463         break;
464     }
465     case DRAW_COMMAND_TYPE_DRAW_INDIRECT_COUNT:
466     {
467         createIndirectBuffer();
468         createIndirectCountBuffer();
469         break;
470     }
471     case DRAW_COMMAND_TYPE_DRAW_INDEXED_INDIRECT_COUNT:
472     {
473         createAndBindIndexBuffer(targetCmdBuffer);
474         createIndexedIndirectBuffer();
475         createIndirectCountBuffer();
476         break;
477     }
478     default:
479         DE_ASSERT(false);
480     }
481 
482     m_vk.cmdBindPipeline(targetCmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
483 
484     if (m_conditionalData.conditionInSecondaryCommandBuffer)
485     {
486         beginConditionalRendering(m_vk, *m_secondaryCmdBuffer, *m_conditionalBuffer, m_conditionalData);
487         recordDraw(*m_secondaryCmdBuffer);
488         m_vk.cmdEndConditionalRenderingEXT(*m_secondaryCmdBuffer);
489         m_vk.endCommandBuffer(*m_secondaryCmdBuffer);
490     }
491     else if (m_conditionalData.conditionInherited)
492     {
493         recordDraw(*m_secondaryCmdBuffer);
494         m_vk.endCommandBuffer(*m_secondaryCmdBuffer);
495     }
496 
497     if (useSecondaryCmdBuffer && m_conditionalData.secondaryCommandBufferNested)
498     {
499         m_vk.cmdExecuteCommands(*m_nestedCmdBuffer, 1, &m_secondaryCmdBuffer.get());
500         m_vk.endCommandBuffer(*m_nestedCmdBuffer);
501     }
502 
503     if (m_conditionalData.conditionInPrimaryCommandBuffer)
504     {
505         if (!m_conditionalData.clearInRenderPass)
506             beginConditionalRendering(m_vk, *m_cmdBuffer, *m_conditionalBuffer, m_conditionalData);
507 
508         if (m_conditionalData.conditionInherited)
509         {
510             if (m_conditionalData.secondaryCommandBufferNested)
511             {
512                 m_vk.cmdExecuteCommands(*m_cmdBuffer, 1, &m_nestedCmdBuffer.get());
513             }
514             else
515             {
516                 m_vk.cmdExecuteCommands(*m_cmdBuffer, 1, &m_secondaryCmdBuffer.get());
517             }
518         }
519         else
520         {
521             recordDraw(*m_cmdBuffer);
522         }
523 
524         if (!m_conditionalData.clearInRenderPass)
525             m_vk.cmdEndConditionalRenderingEXT(*m_cmdBuffer);
526     }
527     else if (useSecondaryCmdBuffer)
528     {
529         if (m_conditionalData.secondaryCommandBufferNested)
530         {
531             m_vk.cmdExecuteCommands(*m_cmdBuffer, 1, &m_nestedCmdBuffer.get());
532         }
533         else
534         {
535             m_vk.cmdExecuteCommands(*m_cmdBuffer, 1, &m_secondaryCmdBuffer.get());
536         }
537     }
538 
539     if (m_conditionalData.clearInRenderPass)
540     {
541         // Finish conditional rendering outside the render pass.
542         vk::endRenderPass(m_vk, *m_cmdBuffer);
543         m_vk.cmdEndConditionalRenderingEXT(*m_cmdBuffer);
544     }
545     else
546     {
547         endLegacyRender(*m_cmdBuffer);
548     }
549 
550     endCommandBuffer(m_vk, *m_cmdBuffer);
551 
552     submitCommandsAndWait(m_vk, device, queue, m_cmdBuffer.get());
553 
554     // Validation
555     tcu::Texture2D referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5f + static_cast<float>(WIDTH)),
556                                   (int)(0.5f + static_cast<float>(HEIGHT)));
557     referenceFrame.allocLevel(0);
558 
559     const int32_t frameWidth  = referenceFrame.getWidth();
560     const int32_t frameHeight = referenceFrame.getHeight();
561 
562     tcu::clear(referenceFrame.getLevel(0), clearColor);
563 
564     const tcu::Vec4 drawColor      = tcu::RGBA::blue().toVec();
565     const tcu::Vec4 referenceColor = m_conditionalData.expectCommandExecution ? drawColor : clearColor;
566 
567     Draw::ReferenceImageCoordinates refCoords;
568 
569     for (int y = 0; y < frameHeight; y++)
570     {
571         const float yCoord = (float)(y / (0.5 * frameHeight)) - 1.0f;
572 
573         for (int x = 0; x < frameWidth; x++)
574         {
575             const float xCoord = (float)(x / (0.5 * frameWidth)) - 1.0f;
576 
577             if ((yCoord >= refCoords.bottom && yCoord <= refCoords.top && xCoord >= refCoords.left &&
578                  xCoord <= refCoords.right))
579                 referenceFrame.getLevel(0).setPixel(referenceColor, x, y);
580         }
581     }
582 
583     const vk::VkOffset3D zeroOffset = {0, 0, 0};
584     const tcu::ConstPixelBufferAccess renderedFrame =
585         m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset,
586                                         WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT);
587 
588     qpTestResult res = QP_TEST_RESULT_PASS;
589 
590     if (!tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame.getLevel(0), renderedFrame, 0.05f,
591                            tcu::COMPARE_LOG_RESULT))
592     {
593         res = QP_TEST_RESULT_FAIL;
594     }
595 
596     return tcu::TestStatus(res, qpGetTestResultName(res));
597 }
598 
599 } // namespace
600 
ConditionalDrawTests(tcu::TestContext & testCtx)601 ConditionalDrawTests::ConditionalDrawTests(tcu::TestContext &testCtx) : TestCaseGroup(testCtx, "draw")
602 {
603     /* Left blank on purpose */
604 }
605 
~ConditionalDrawTests(void)606 ConditionalDrawTests::~ConditionalDrawTests(void)
607 {
608 }
609 
init(void)610 void ConditionalDrawTests::init(void)
611 {
612     for (int conditionNdx = 0; conditionNdx < DE_LENGTH_OF_ARRAY(conditional::s_testsData); conditionNdx++)
613     {
614         const ConditionalData &conditionData = conditional::s_testsData[conditionNdx];
615 
616         tcu::TestCaseGroup *conditionalDrawRootGroup =
617             new tcu::TestCaseGroup(m_testCtx, de::toString(conditionData).c_str());
618 
619         for (uint32_t commandTypeIdx = 0; commandTypeIdx < DRAW_COMMAND_TYPE_DRAW_LAST; ++commandTypeIdx)
620         {
621             const DrawCommandType command = DrawCommandType(commandTypeIdx);
622 
623             ConditionalTestSpec testSpec;
624             testSpec.command                           = command;
625             testSpec.drawCalls                         = 4;
626             testSpec.conditionalData                   = conditionData;
627             testSpec.shaders[glu::SHADERTYPE_VERTEX]   = "vulkan/dynamic_state/VertexFetch.vert";
628             testSpec.shaders[glu::SHADERTYPE_FRAGMENT] = "vulkan/dynamic_state/VertexFetch.frag";
629 
630             conditionalDrawRootGroup->addChild(
631                 new Draw::InstanceFactory<ConditionalDraw>(m_testCtx, getDrawCommandTypeName(command), testSpec));
632         }
633 
634         addChild(conditionalDrawRootGroup);
635     }
636 }
637 
638 } // namespace conditional
639 } // namespace vkt
640