1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2022 The Khronos Group Inc.
6  * Copyright (c) 2022 Valve Corporation.
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 Tests using VK_EXT_mesh_shader and VK_EXT_conditional_rendering
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktMeshShaderConditionalRenderingTestsEXT.hpp"
26 #include "vktMeshShaderUtil.hpp"
27 #include "vktTestCase.hpp"
28 
29 #include "vkObjUtil.hpp"
30 #include "vkQueryUtil.hpp"
31 #include "vkMemUtil.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkImageUtil.hpp"
34 #include "vkImageWithMemory.hpp"
35 #include "vkBufferWithMemory.hpp"
36 #include "vkCmdUtil.hpp"
37 #include "vkBarrierUtil.hpp"
38 
39 #include "tcuVector.hpp"
40 #include "tcuImageCompare.hpp"
41 
42 #include <vector>
43 #include <sstream>
44 #include <memory>
45 
46 namespace vkt
47 {
48 namespace MeshShader
49 {
50 
51 namespace
52 {
53 
54 using namespace vk;
55 
56 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
57 
58 enum class DrawType
59 {
60     DRAW,
61     DRAW_INDIRECT,
62     DRAW_INDIRECT_WITH_COUNT,
63 };
64 
65 enum class CmdBufferType
66 {
67     PRIMARY,
68     SECONDARY,
69     SECONDARY_WITH_INHERITANCE,
70 };
71 
72 static constexpr VkDeviceSize kBindOffset = 16u;
73 
getCondValues(void)74 std::vector<uint32_t> getCondValues(void)
75 {
76     const std::vector<uint32_t> values = {
77         0x01000000u, 0x00010000u, 0x00000100u, 0x00000001u, 0x00000000u,
78     };
79 
80     return values;
81 }
82 
paddedHex(uint32_t value)83 std::string paddedHex(uint32_t value)
84 {
85     std::ostringstream repr;
86     repr << "0x" << std::hex << std::setw(8u) << std::setfill('0') << value;
87     return repr.str();
88 }
89 
getOutputColor(void)90 tcu::Vec4 getOutputColor(void)
91 {
92     return tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
93 }
94 
getClearColor(void)95 tcu::Vec4 getClearColor(void)
96 {
97     return tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
98 }
99 
100 struct TestParams
101 {
102     DrawType drawType;
103     CmdBufferType cmdBufferType;
104     bool bindWithOffset;
105     bool condWithOffset;
106     uint32_t condValue;
107     bool inverted;
108     bool useTask;
109 
needsSecondaryCmdBuffervkt::MeshShader::__anon427f83760111::TestParams110     bool needsSecondaryCmdBuffer(void) const
111     {
112         return (cmdBufferType != CmdBufferType::PRIMARY);
113     }
114 };
115 
116 class ConditionalRenderingCase : public vkt::TestCase
117 {
118 public:
ConditionalRenderingCase(tcu::TestContext & testCtx,const std::string & name,const TestParams & params)119     ConditionalRenderingCase(tcu::TestContext &testCtx, const std::string &name, const TestParams &params)
120         : vkt::TestCase(testCtx, name)
121         , m_params(params)
122     {
123     }
~ConditionalRenderingCase(void)124     virtual ~ConditionalRenderingCase(void)
125     {
126     }
127 
128     void initPrograms(vk::SourceCollections &programCollection) const override;
129     TestInstance *createInstance(Context &context) const override;
130     void checkSupport(Context &context) const override;
131 
132 protected:
133     const TestParams m_params;
134 };
135 
136 class ConditionBuffer
137 {
138 public:
ConditionBuffer(const DeviceInterface & vkd,VkDevice device,Allocator & alloc,uint32_t condValue,bool bindWithOffset,bool condWithOffset)139     ConditionBuffer(const DeviceInterface &vkd, VkDevice device, Allocator &alloc, uint32_t condValue,
140                     bool bindWithOffset, bool condWithOffset)
141         : m_buffer()
142         , m_allocation()
143         , m_condOffset(0ull)
144     {
145         // Create buffer with the desired size first.
146         const auto condSize         = static_cast<VkDeviceSize>(sizeof(condValue));
147         const auto condOffset       = (condWithOffset ? condSize : 0ull);
148         const auto bufferSize       = condSize + condOffset;
149         const auto bufferCreateInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT);
150         auto buffer                 = createBuffer(vkd, device, &bufferCreateInfo);
151 
152         // Allocate memory taking bindWithOffset into account.
153         const auto bufferMemReqs = getBufferMemoryRequirements(vkd, device, buffer.get());
154         const auto bindOffset    = (bindWithOffset ? de::roundUp(kBindOffset, bufferMemReqs.alignment) : 0ull);
155         const auto allocSize     = bufferMemReqs.size + bindOffset;
156 
157         const auto actualMemReqs =
158             makeMemoryRequirements(allocSize, bufferMemReqs.alignment, bufferMemReqs.memoryTypeBits);
159         auto allocation = alloc.allocate(actualMemReqs, MemoryRequirement::HostVisible);
160         vkd.bindBufferMemory(device, buffer.get(), allocation->getMemory(), bindOffset);
161 
162         // Fill buffer data.
163         const uint32_t fillValue = ((condValue != 0u) ? 0u : 1u);
164         uint8_t *hostPtr         = reinterpret_cast<uint8_t *>(allocation->getHostPtr());
165 
166         deMemset(hostPtr, 0, static_cast<size_t>(actualMemReqs.size));
167         deMemcpy(hostPtr + bindOffset, &fillValue, sizeof(fillValue));
168         deMemcpy(hostPtr + bindOffset + condOffset, &condValue, sizeof(condValue));
169 
170         m_buffer     = buffer;
171         m_allocation = allocation;
172         m_condOffset = condOffset;
173     }
174 
getCondOffset(void) const175     VkDeviceSize getCondOffset(void) const
176     {
177         return m_condOffset;
178     }
179 
getBuffer(void) const180     VkBuffer getBuffer(void) const
181     {
182         return m_buffer.get();
183     }
184 
185     // Cannot copy or assign this.
186     ConditionBuffer(const ConditionBuffer &)            = delete;
187     ConditionBuffer &operator=(const ConditionBuffer &) = delete;
188 
189 protected:
190     Move<VkBuffer> m_buffer;
191     de::MovePtr<Allocation> m_allocation;
192     VkDeviceSize m_condOffset;
193 };
194 
195 class ConditionalRenderingInstance : public vkt::TestInstance
196 {
197 public:
ConditionalRenderingInstance(Context & context,const TestParams & params)198     ConditionalRenderingInstance(Context &context, const TestParams &params)
199         : vkt::TestInstance(context)
200         , m_params(params)
201         , m_conditionBuffer()
202         , m_indirectDrawArgsBuffer()
203         , m_indirectDrawCountBuffer()
204     {
205     }
~ConditionalRenderingInstance(void)206     virtual ~ConditionalRenderingInstance(void)
207     {
208     }
209 
210     tcu::TestStatus iterate(void) override;
211 
212 protected:
213     // Creates the indirect buffers that are needed according to the test parameters.
214     void initIndirectBuffers(const DeviceInterface &vkd, const VkDevice device, Allocator &alloc);
215 
216     // Calls the appropriate drawing command depending on the test parameters.
217     void drawMeshTasks(const DeviceInterface &vkd, const VkCommandBuffer cmdBuffer) const;
218 
219     const TestParams m_params;
220     std::unique_ptr<ConditionBuffer> m_conditionBuffer;
221     std::unique_ptr<BufferWithMemory> m_indirectDrawArgsBuffer;
222     std::unique_ptr<BufferWithMemory> m_indirectDrawCountBuffer;
223 };
224 
225 // Makes an indirect buffer with the specified contents.
226 template <class T>
makeIndirectBuffer(const DeviceInterface & vkd,const VkDevice device,Allocator & alloc,const T & data)227 std::unique_ptr<BufferWithMemory> makeIndirectBuffer(const DeviceInterface &vkd, const VkDevice device,
228                                                      Allocator &alloc, const T &data)
229 {
230     const auto bufferSize       = static_cast<VkDeviceSize>(sizeof(data));
231     const auto bufferCreateInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
232 
233     std::unique_ptr<BufferWithMemory> buffer(
234         new BufferWithMemory(vkd, device, alloc, bufferCreateInfo, MemoryRequirement::HostVisible));
235 
236     auto &allocation = buffer->getAllocation();
237     void *dataPtr    = allocation.getHostPtr();
238 
239     deMemcpy(dataPtr, &data, sizeof(data));
240     flushAlloc(vkd, device, allocation);
241 
242     return buffer;
243 }
244 
initIndirectBuffers(const DeviceInterface & vkd,const VkDevice device,Allocator & alloc)245 void ConditionalRenderingInstance::initIndirectBuffers(const DeviceInterface &vkd, const VkDevice device,
246                                                        Allocator &alloc)
247 {
248     if (m_params.drawType != DrawType::DRAW)
249     {
250         const VkDrawMeshTasksIndirectCommandEXT drawArgs = {1u, 1u, 1u};
251         m_indirectDrawArgsBuffer                         = makeIndirectBuffer(vkd, device, alloc, drawArgs);
252     }
253 
254     if (m_params.drawType == DrawType::DRAW_INDIRECT_WITH_COUNT)
255     {
256         const uint32_t drawCount  = 1u;
257         m_indirectDrawCountBuffer = makeIndirectBuffer(vkd, device, alloc, drawCount);
258     }
259 }
260 
drawMeshTasks(const DeviceInterface & vkd,const VkCommandBuffer cmdBuffer) const261 void ConditionalRenderingInstance::drawMeshTasks(const DeviceInterface &vkd, const VkCommandBuffer cmdBuffer) const
262 {
263     const auto stride = static_cast<uint32_t>(sizeof(VkDrawMeshTasksIndirectCommandEXT));
264 
265     if (m_params.drawType == DrawType::DRAW)
266     {
267         vkd.cmdDrawMeshTasksEXT(cmdBuffer, 1u, 1u, 1u);
268     }
269     else if (m_params.drawType == DrawType::DRAW_INDIRECT)
270     {
271         vkd.cmdDrawMeshTasksIndirectEXT(cmdBuffer, m_indirectDrawArgsBuffer->get(), 0ull, 1u, stride);
272     }
273     else if (m_params.drawType == DrawType::DRAW_INDIRECT_WITH_COUNT)
274     {
275         vkd.cmdDrawMeshTasksIndirectCountEXT(cmdBuffer, m_indirectDrawArgsBuffer->get(), 0ull,
276                                              m_indirectDrawCountBuffer->get(), 0ull, 1u, stride);
277     }
278     else
279         DE_ASSERT(false);
280 }
281 
initPrograms(vk::SourceCollections & programCollection) const282 void ConditionalRenderingCase::initPrograms(vk::SourceCollections &programCollection) const
283 {
284     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
285 
286     if (m_params.useTask)
287     {
288         std::ostringstream task;
289         task << "#version 460\n"
290              << "#extension GL_EXT_mesh_shader : enable\n"
291              << "\n"
292              << "layout (local_size_x=1, local_size_y=1, local_size_z=1) in;\n"
293              << "\n"
294              << "void main (void) {\n"
295              << "    EmitMeshTasksEXT(1u, 1u, 1u);\n"
296              << "}\n";
297         programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
298     }
299 
300     std::ostringstream mesh;
301     mesh << "#version 460\n"
302          << "#extension GL_EXT_mesh_shader : enable\n"
303          << "\n"
304          << "layout (local_size_x=1, local_size_y=1, local_size_z=1) in;\n"
305          << "layout (triangles) out;\n"
306          << "layout (max_vertices=3, max_primitives=1) out;\n"
307          << "\n"
308          << "void main (void) {\n"
309          << "    SetMeshOutputsEXT(3u, 1u);\n"
310          << "\n"
311          << "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
312          << "\n"
313          << "    gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
314          << "    gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
315          << "    gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
316          << "}\n";
317     programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
318 
319     const auto outColor = getOutputColor();
320     std::ostringstream frag;
321     frag << "#version 460\n"
322          << "\n"
323          << "layout (location=0) out vec4 outColor;\n"
324          << "\n"
325          << "void main (void) {\n"
326          << "    outColor = vec4" << outColor << ";\n"
327          << "}\n";
328     programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
329 }
330 
createInstance(Context & context) const331 TestInstance *ConditionalRenderingCase::createInstance(Context &context) const
332 {
333     return new ConditionalRenderingInstance(context, m_params);
334 }
335 
checkSupport(Context & context) const336 void ConditionalRenderingCase::checkSupport(Context &context) const
337 {
338     checkTaskMeshShaderSupportEXT(context, m_params.useTask /*requireTask*/, true /*requireMesh*/);
339 
340     context.requireDeviceFunctionality("VK_EXT_conditional_rendering");
341 
342     if (m_params.drawType == DrawType::DRAW_INDIRECT_WITH_COUNT)
343         context.requireDeviceFunctionality("VK_KHR_draw_indirect_count");
344 
345     if (m_params.cmdBufferType == CmdBufferType::SECONDARY_WITH_INHERITANCE)
346     {
347         const auto &condRenderingFeatures = context.getConditionalRenderingFeaturesEXT();
348         if (!condRenderingFeatures.inheritedConditionalRendering)
349             TCU_THROW(NotSupportedError, "inheritedConditionalRendering not supported");
350     }
351 }
352 
iterate()353 tcu::TestStatus ConditionalRenderingInstance::iterate()
354 {
355     const auto &vkd       = m_context.getDeviceInterface();
356     const auto device     = m_context.getDevice();
357     auto &alloc           = m_context.getDefaultAllocator();
358     const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
359     const auto queue      = m_context.getUniversalQueue();
360 
361     const auto colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
362     const auto colorUsage  = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
363     const auto tcuFormat   = mapVkFormat(colorFormat);
364     const auto colorExtent = makeExtent3D(4u, 4u, 1u);
365     const tcu::IVec3 iExtent3D(static_cast<int>(colorExtent.width), static_cast<int>(colorExtent.height),
366                                static_cast<int>(colorExtent.depth));
367     const auto clearColor  = getClearColor();
368     const auto drawColor   = getOutputColor();
369     const auto bindPoint   = VK_PIPELINE_BIND_POINT_GRAPHICS;
370     const auto needsSecCmd = m_params.needsSecondaryCmdBuffer();
371 
372     // Create color attachment.
373     const VkImageCreateInfo colorAttCreateInfo = {
374         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
375         nullptr,                             // const void* pNext;
376         0u,                                  // VkImageCreateFlags flags;
377         VK_IMAGE_TYPE_2D,                    // VkImageType imageType;
378         colorFormat,                         // VkFormat format;
379         colorExtent,                         // VkExtent3D extent;
380         1u,                                  // uint32_t mipLevels;
381         1u,                                  // uint32_t arrayLayers;
382         VK_SAMPLE_COUNT_1_BIT,               // VkSampleCountFlagBits samples;
383         VK_IMAGE_TILING_OPTIMAL,             // VkImageTiling tiling;
384         colorUsage,                          // VkImageUsageFlags usage;
385         VK_SHARING_MODE_EXCLUSIVE,           // VkSharingMode sharingMode;
386         0u,                                  // uint32_t queueFamilyIndexCount;
387         nullptr,                             // const uint32_t* pQueueFamilyIndices;
388         VK_IMAGE_LAYOUT_UNDEFINED,           // VkImageLayout initialLayout;
389     };
390     ImageWithMemory colorAtt(vkd, device, alloc, colorAttCreateInfo, MemoryRequirement::Any);
391     const auto colorSRR     = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
392     const auto colorSRL     = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
393     const auto colorAttView = makeImageView(vkd, device, colorAtt.get(), VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorSRR);
394 
395     // Render pass and framebuffer.
396     const auto renderPass = makeRenderPass(vkd, device, colorFormat);
397     const auto framebuffer =
398         makeFramebuffer(vkd, device, renderPass.get(), colorAttView.get(), colorExtent.width, colorExtent.height);
399 
400     // Verification buffer.
401     const auto verifBufferSize =
402         static_cast<VkDeviceSize>(tcu::getPixelSize(tcuFormat) * iExtent3D.x() * iExtent3D.y() * iExtent3D.z());
403     const auto verifBufferCreateInfo = makeBufferCreateInfo(verifBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
404     BufferWithMemory verifBuffer(vkd, device, alloc, verifBufferCreateInfo, MemoryRequirement::HostVisible);
405     auto &verifBufferAlloc = verifBuffer.getAllocation();
406     void *verifBufferData  = verifBufferAlloc.getHostPtr();
407 
408     // Create the condition buffer.
409     m_conditionBuffer.reset(
410         new ConditionBuffer(vkd, device, alloc, m_params.condValue, m_params.bindWithOffset, m_params.condWithOffset));
411 
412     // Create the indirect buffers if needed.
413     initIndirectBuffers(vkd, device, alloc);
414 
415     // Pipeline.
416     const auto pipelineLayout = makePipelineLayout(vkd, device);
417     const auto &binaries      = m_context.getBinaryCollection();
418     const auto taskModule =
419         (binaries.contains("task") ? createShaderModule(vkd, device, binaries.get("task")) : Move<VkShaderModule>());
420     const auto meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
421     const auto fragModule = createShaderModule(vkd, device, binaries.get("frag"));
422 
423     const std::vector<VkViewport> viewports(1u, makeViewport(colorExtent));
424     const std::vector<VkRect2D> scissors(1u, makeRect2D(colorExtent));
425 
426     const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(), taskModule.get(), meshModule.get(),
427                                                fragModule.get(), renderPass.get(), viewports, scissors);
428 
429     // Command pool and command buffers.
430     const auto cmdPool          = makeCommandPool(vkd, device, queueIndex);
431     const auto primaryCmdBuffer = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
432     const auto secondaryCmdBuffer =
433         (needsSecCmd ? allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_SECONDARY) :
434                        Move<VkCommandBuffer>());
435     const auto primary   = primaryCmdBuffer.get();
436     const auto secondary = secondaryCmdBuffer.get();
437 
438     // Common conditional rendering begin info.
439     const auto conditionalRenderingFlags =
440         (m_params.inverted ? (VkConditionalRenderingFlagsEXT)VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT :
441                              static_cast<VkConditionalRenderingFlagsEXT>(0));
442     const VkConditionalRenderingBeginInfoEXT conditionalRenderingBegin = {
443         VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT, // VkStructureType sType;
444         nullptr,                                                // const void* pNext;
445         m_conditionBuffer->getBuffer(),                         // VkBuffer buffer;
446         m_conditionBuffer->getCondOffset(),                     // VkDeviceSize offset;
447         conditionalRenderingFlags,                              // VkConditionalRenderingFlagsEXT flags;
448     };
449 
450     // Inheritance info for the secondary command buffer.
451     const auto conditionalRenderingEnable =
452         ((m_params.cmdBufferType == CmdBufferType::SECONDARY_WITH_INHERITANCE) ? VK_TRUE : VK_FALSE);
453     const vk::VkCommandBufferInheritanceConditionalRenderingInfoEXT conditionalRenderingInheritanceInfo = {
454         VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT, // VkStructureType sType;
455         nullptr,                                                                     // const void* pNext;
456         conditionalRenderingEnable, // VkBool32 conditionalRenderingEnable;
457     };
458 
459     const VkCommandBufferInheritanceInfo inheritanceInfo = {
460         VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, // VkStructureType sType;
461         &conditionalRenderingInheritanceInfo,              // const void* pNext;
462         renderPass.get(),                                  // VkRenderPass renderPass;
463         0u,                                                // uint32_t subpass;
464         framebuffer.get(),                                 // VkFramebuffer framebuffer;
465         VK_FALSE,                                          // VkBool32 occlusionQueryEnable;
466         0u,                                                // VkQueryControlFlags queryFlags;
467         0u,                                                // VkQueryPipelineStatisticFlags pipelineStatistics;
468     };
469 
470     const VkCommandBufferUsageFlags cmdBufferUsageFlags =
471         (VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT);
472     const VkCommandBufferBeginInfo secondaryBeginInfo = {
473         VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
474         nullptr,                                     // const void* pNext;
475         cmdBufferUsageFlags,                         // VkCommandBufferUsageFlags flags;
476         &inheritanceInfo,                            // const VkCommandBufferInheritanceInfo* pInheritanceInfo;
477     };
478 
479     beginCommandBuffer(vkd, primary);
480 
481     if (m_params.cmdBufferType == CmdBufferType::PRIMARY)
482     {
483         // Do everything in the primary command buffer.
484         const auto cmdBuffer = primary;
485 
486         vkd.cmdBeginConditionalRenderingEXT(cmdBuffer, &conditionalRenderingBegin);
487         beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0u), clearColor);
488         vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipeline.get());
489         drawMeshTasks(vkd, cmdBuffer);
490         endRenderPass(vkd, cmdBuffer);
491         vkd.cmdEndConditionalRenderingEXT(cmdBuffer);
492     }
493     else if (m_params.cmdBufferType == CmdBufferType::SECONDARY)
494     {
495         // Do everything in the secondary command buffer.
496         // In addition, do the conditional rendering inside the render pass so it's a bit different from the primary case.
497         beginRenderPass(vkd, primary, renderPass.get(), framebuffer.get(), scissors.at(0u), clearColor,
498                         VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
499 
500         const auto cmdBuffer = secondaryCmdBuffer.get();
501 
502         vkd.beginCommandBuffer(secondary, &secondaryBeginInfo);
503         vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipeline.get());
504         vkd.cmdBeginConditionalRenderingEXT(cmdBuffer, &conditionalRenderingBegin);
505         drawMeshTasks(vkd, cmdBuffer);
506         vkd.cmdEndConditionalRenderingEXT(cmdBuffer);
507         endCommandBuffer(vkd, cmdBuffer);
508 
509         vkd.cmdExecuteCommands(primary, 1u, &cmdBuffer);
510         endRenderPass(vkd, primary);
511     }
512     else if (m_params.cmdBufferType == CmdBufferType::SECONDARY_WITH_INHERITANCE)
513     {
514         // Inherit everything in the secondary command buffer.
515         vkd.beginCommandBuffer(secondary, &secondaryBeginInfo);
516         vkd.cmdBindPipeline(secondary, bindPoint, pipeline.get());
517         drawMeshTasks(vkd, secondary);
518         endCommandBuffer(vkd, secondary);
519 
520         vkd.cmdBeginConditionalRenderingEXT(primary, &conditionalRenderingBegin);
521         beginRenderPass(vkd, primary, renderPass.get(), framebuffer.get(), scissors.at(0u), clearColor,
522                         VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
523         vkd.cmdExecuteCommands(primary, 1u, &secondary);
524         endRenderPass(vkd, primary);
525         vkd.cmdEndConditionalRenderingEXT(primary);
526     }
527     else
528         DE_ASSERT(false);
529 
530     // Transfer color attachment to the verification buffer.
531     const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
532     const auto copyRegion          = makeBufferImageCopy(colorExtent, colorSRL);
533     const auto preTranferBarrier   = makeImageMemoryBarrier(
534         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
535         VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorAtt.get(), colorSRR);
536 
537     cmdPipelineImageMemoryBarrier(vkd, primary, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
538                                   VK_PIPELINE_STAGE_TRANSFER_BIT, &preTranferBarrier);
539     vkd.cmdCopyImageToBuffer(primary, colorAtt.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, verifBuffer.get(), 1u,
540                              &copyRegion);
541     cmdPipelineMemoryBarrier(vkd, primary, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT,
542                              &postTransferBarrier);
543 
544     endCommandBuffer(vkd, primary);
545     submitCommandsAndWait(vkd, device, queue, primary);
546 
547     invalidateAlloc(vkd, device, verifBufferAlloc);
548 
549     const tcu::ConstPixelBufferAccess resultAccess(tcuFormat, iExtent3D, verifBufferData);
550     const bool expectDraw    = ((m_params.condValue != 0u) != m_params.inverted);
551     const auto expectedColor = (expectDraw ? drawColor : clearColor);
552     const tcu::Vec4 threshold(0.0f, 0.0f, 0.0f, 0.0f);
553 
554     auto &log = m_context.getTestContext().getLog();
555     if (!tcu::floatThresholdCompare(log, "Result", "", expectedColor, resultAccess, threshold,
556                                     tcu::COMPARE_LOG_ON_ERROR))
557         TCU_FAIL("Check log for details");
558 
559     return tcu::TestStatus::pass("Pass");
560 }
561 
562 } // namespace
563 
createMeshShaderConditionalRenderingTestsEXT(tcu::TestContext & testCtx)564 tcu::TestCaseGroup *createMeshShaderConditionalRenderingTestsEXT(tcu::TestContext &testCtx)
565 {
566     GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "conditional_rendering"));
567 
568     const struct
569     {
570         DrawType drawType;
571         const char *name;
572     } drawTypeCases[] = {
573         {DrawType::DRAW, "draw"},
574         {DrawType::DRAW_INDIRECT, "draw_indirect"},
575         {DrawType::DRAW_INDIRECT_WITH_COUNT, "draw_indirect_count"},
576     };
577 
578     const struct
579     {
580         CmdBufferType cmdBufferType;
581         const char *name;
582     } cmdBufferTypeCases[] = {
583         {CmdBufferType::PRIMARY, "primary_cmd_buffer"},
584         {CmdBufferType::SECONDARY, "secondary_cmd_buffer"},
585         {CmdBufferType::SECONDARY_WITH_INHERITANCE, "secondary_cmd_buffer_inheritance"},
586     };
587 
588     const struct
589     {
590         bool bindWithOffset;
591         const char *name;
592     } bindWithOffsetCases[] = {
593         {false, "bind_without_offset"},
594         {true, "bind_with_offset"},
595     };
596 
597     const struct
598     {
599         bool condWithOffset;
600         const char *name;
601     } condWithOffsetCases[] = {
602         {false, "cond_without_offset"},
603         {true, "cond_with_offset"},
604     };
605 
606     const struct
607     {
608         bool inverted;
609         const char *name;
610     } inversionCases[] = {
611         {false, "normal_cond"},
612         {true, "inverted_cond"},
613     };
614 
615     const struct
616     {
617         bool useTask;
618         const char *name;
619     } useTaskCases[] = {
620         {false, "mesh_only"},
621         {true, "mesh_and_task"},
622     };
623 
624     const auto condValues = getCondValues();
625 
626     for (const auto &drawTypeCase : drawTypeCases)
627     {
628         GroupPtr drawTypeGroup(new tcu::TestCaseGroup(testCtx, drawTypeCase.name));
629 
630         for (const auto &cmdBufferTypeCase : cmdBufferTypeCases)
631         {
632             GroupPtr cmdBufferTypeGroup(new tcu::TestCaseGroup(testCtx, cmdBufferTypeCase.name));
633 
634             for (const auto &bindWithOffsetCase : bindWithOffsetCases)
635             {
636                 GroupPtr bindWithOffsetGroup(new tcu::TestCaseGroup(testCtx, bindWithOffsetCase.name));
637 
638                 for (const auto &condWithOffsetCase : condWithOffsetCases)
639                 {
640                     GroupPtr condWithOffsetGroup(new tcu::TestCaseGroup(testCtx, condWithOffsetCase.name));
641 
642                     for (const auto &inversionCase : inversionCases)
643                     {
644                         GroupPtr inversionGroup(new tcu::TestCaseGroup(testCtx, inversionCase.name));
645 
646                         for (const auto &useTaskCase : useTaskCases)
647                         {
648                             GroupPtr useTaskGroup(new tcu::TestCaseGroup(testCtx, useTaskCase.name));
649 
650                             for (const auto &condValue : condValues)
651                             {
652                                 const auto testName     = "value_" + paddedHex(condValue);
653                                 const TestParams params = {
654                                     drawTypeCase.drawType,             // DrawType drawType;
655                                     cmdBufferTypeCase.cmdBufferType,   // CmdBufferType cmdBufferType;
656                                     bindWithOffsetCase.bindWithOffset, // bool bindWithOffset;
657                                     condWithOffsetCase.condWithOffset, // bool condWithOffset;
658                                     condValue,                         // uint32_t condValue;
659                                     inversionCase.inverted,            // bool inverted;
660                                     useTaskCase.useTask,               // bool useTask;
661                                 };
662                                 useTaskGroup->addChild(new ConditionalRenderingCase(testCtx, testName, params));
663                             }
664 
665                             inversionGroup->addChild(useTaskGroup.release());
666                         }
667 
668                         condWithOffsetGroup->addChild(inversionGroup.release());
669                     }
670 
671                     bindWithOffsetGroup->addChild(condWithOffsetGroup.release());
672                 }
673 
674                 cmdBufferTypeGroup->addChild(bindWithOffsetGroup.release());
675             }
676 
677             drawTypeGroup->addChild(cmdBufferTypeGroup.release());
678         }
679 
680         mainGroup->addChild(drawTypeGroup.release());
681     }
682 
683     return mainGroup.release();
684 }
685 
686 } // namespace MeshShader
687 } // namespace vkt
688