1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2021 The Khronos Group Inc.
6  * Copyright (c) 2021 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 Mesh Shader Builtin Tests for VK_EXT_mesh_shader
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktMeshShaderBuiltinTestsEXT.hpp"
26 #include "vktMeshShaderUtil.hpp"
27 #include "vktTestCase.hpp"
28 
29 #include "vkTypeUtil.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkObjUtil.hpp"
32 #include "vkBuilderUtil.hpp"
33 #include "vkImageWithMemory.hpp"
34 #include "vkBufferWithMemory.hpp"
35 #include "vkCmdUtil.hpp"
36 #include "vkBarrierUtil.hpp"
37 
38 #include "tcuTexture.hpp"
39 #include "tcuTestLog.hpp"
40 
41 #include <vector>
42 #include <algorithm>
43 #include <sstream>
44 #include <map>
45 #include <utility>
46 #include <sstream>
47 
48 namespace vkt
49 {
50 namespace MeshShader
51 {
52 
53 namespace
54 {
55 
56 // Wraps a tcu::IVec2 with a custom operator< that uses the X and Y components in component order so it can be used as a map key.
57 // Can be converted to and from a tcu::IVec2 automatically.
58 class CoordKey
59 {
60 public:
CoordKey(const tcu::IVec2 & coords)61     CoordKey(const tcu::IVec2 &coords) : m_coords(coords)
62     {
63     }
64 
operator tcu::IVec2() const65     operator tcu::IVec2() const
66     {
67         return m_coords;
68     }
69 
operator <(const CoordKey & other) const70     bool operator<(const CoordKey &other) const
71     {
72         const auto &a = this->m_coords;
73         const auto &b = other.m_coords;
74 
75         for (int i = 0; i < tcu::IVec2::SIZE; ++i)
76         {
77             if (a[i] < b[i])
78                 return true;
79             if (a[i] > b[i])
80                 return false;
81         }
82 
83         return false;
84     }
85 
86 private:
87     const tcu::IVec2 m_coords;
88 };
89 
90 using namespace vk;
91 
92 using GroupPtr            = de::MovePtr<tcu::TestCaseGroup>;
93 using DrawCommandVec      = std::vector<VkDrawMeshTasksIndirectCommandEXT>;
94 using ImageWithMemoryPtr  = de::MovePtr<ImageWithMemory>;
95 using BufferWithMemoryPtr = de::MovePtr<BufferWithMemory>;
96 using ViewportVec         = std::vector<VkViewport>;
97 using ColorVec            = std::vector<tcu::Vec4>;
98 using PixelMap            = std::map<CoordKey, tcu::Vec4>; // Coordinates to color.
99 
getClearColor()100 tcu::Vec4 getClearColor()
101 {
102     return tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
103 }
104 
getDefaultExtent()105 VkExtent2D getDefaultExtent()
106 {
107     return makeExtent2D(8u, 8u);
108 }
109 
getLinearExtent()110 VkExtent2D getLinearExtent()
111 {
112     return makeExtent2D(8u, 1u);
113 }
114 
115 struct JobSize
116 {
117     uint32_t numTasks;
118     uint32_t localSize;
119 };
120 
getLargeJobSize()121 JobSize getLargeJobSize()
122 {
123     return JobSize{8u, 8u};
124 }
125 
126 // Single draw command with the given number of tasks, 1 by default.
getDefaultDrawCommands(uint32_t taskCount=1u)127 DrawCommandVec getDefaultDrawCommands(uint32_t taskCount = 1u)
128 {
129     return DrawCommandVec(1u, makeDrawMeshTasksIndirectCommandEXT(taskCount, 1u, 1u));
130 }
131 
132 // Basic fragment shader that draws fragments in blue.
getBasicFragShader()133 std::string getBasicFragShader()
134 {
135     return "#version 460\n"
136            "layout (location=0) out vec4 outColor;\n"
137            "void main ()\n"
138            "{\n"
139            "    outColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
140            "}\n";
141 }
142 
143 struct IterationParams
144 {
145     VkExtent2D colorExtent;
146     uint32_t numLayers;
147     bool multiview;
148     bool indirect;
149     tcu::Maybe<FragmentSize> fragmentSize;
150     DrawCommandVec drawArgs;
151     ViewportVec viewports; // If empty, a single default viewport is used.
152 };
153 
154 class MeshShaderBuiltinInstance : public vkt::TestInstance
155 {
156 public:
MeshShaderBuiltinInstance(Context & context,const IterationParams & params)157     MeshShaderBuiltinInstance(Context &context, const IterationParams &params)
158         : vkt::TestInstance(context)
159         , m_params(params)
160     {
161     }
~MeshShaderBuiltinInstance(void)162     virtual ~MeshShaderBuiltinInstance(void)
163     {
164     }
165 
166     tcu::TestStatus iterate() override;
167     virtual void verifyResults(const tcu::ConstPixelBufferAccess &result) = 0;
168 
169 protected:
170     IterationParams m_params;
171 };
172 
createCustomRenderPass(const DeviceInterface & vkd,VkDevice device,VkFormat format,bool multiview,uint32_t numLayers)173 Move<VkRenderPass> createCustomRenderPass(const DeviceInterface &vkd, VkDevice device, VkFormat format, bool multiview,
174                                           uint32_t numLayers)
175 {
176     DE_ASSERT(numLayers > 0u);
177     const uint32_t numSubpasses = (multiview ? numLayers : 1u);
178 
179     const VkAttachmentDescription colorAttachmentDescription = {
180         0u,                                       // VkAttachmentDescriptionFlags    flags
181         format,                                   // VkFormat                        format
182         VK_SAMPLE_COUNT_1_BIT,                    // VkSampleCountFlagBits           samples
183         VK_ATTACHMENT_LOAD_OP_CLEAR,              // VkAttachmentLoadOp              loadOp
184         VK_ATTACHMENT_STORE_OP_STORE,             // VkAttachmentStoreOp             storeOp
185         VK_ATTACHMENT_LOAD_OP_DONT_CARE,          // VkAttachmentLoadOp              stencilLoadOp
186         VK_ATTACHMENT_STORE_OP_DONT_CARE,         // VkAttachmentStoreOp             stencilStoreOp
187         VK_IMAGE_LAYOUT_UNDEFINED,                // VkImageLayout                   initialLayout
188         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout                   finalLayout
189     };
190 
191     const VkAttachmentReference colorAttachmentRef =
192         makeAttachmentReference(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
193 
194     const VkSubpassDescription subpassDescription = {
195         0u,                              // VkSubpassDescriptionFlags       flags
196         VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint             pipelineBindPoint
197         0u,                              // uint32_t                        inputAttachmentCount
198         nullptr,                         // const VkAttachmentReference*    pInputAttachments
199         1u,                              // uint32_t                        colorAttachmentCount
200         &colorAttachmentRef,             // const VkAttachmentReference*    pColorAttachments
201         nullptr,                         // const VkAttachmentReference*    pResolveAttachments
202         nullptr,                         // const VkAttachmentReference*    pDepthStencilAttachment
203         0u,                              // uint32_t                        preserveAttachmentCount
204         nullptr                          // const uint32_t*                 pPreserveAttachments
205     };
206 
207     std::vector<VkSubpassDescription> subpassDescriptions;
208 
209     subpassDescriptions.reserve(numSubpasses);
210     for (uint32_t i = 0; i < numSubpasses; ++i)
211         subpassDescriptions.push_back(subpassDescription);
212 
213     std::vector<VkSubpassDependency> dependencies;
214 
215     for (uint32_t subpassIdx = 1u; subpassIdx < numSubpasses; ++subpassIdx)
216     {
217         const uint32_t prev                = subpassIdx - 1u;
218         const VkSubpassDependency colorDep = {
219             prev,                                          // uint32_t srcSubpass;
220             subpassIdx,                                    // uint32_t dstSubpass;
221             VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask;
222             VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags dstStageMask;
223             VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,          // VkAccessFlags srcAccessMask;
224             (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
225              VK_ACCESS_COLOR_ATTACHMENT_READ_BIT), // VkAccessFlags dstAccessMask;
226             VK_DEPENDENCY_BY_REGION_BIT,           // VkDependencyFlags dependencyFlags;
227         };
228         dependencies.push_back(colorDep);
229     }
230 
231     using MultiviewInfoPtr = de::MovePtr<VkRenderPassMultiviewCreateInfo>;
232 
233     MultiviewInfoPtr multiviewCreateInfo;
234     std::vector<uint32_t> viewMasks;
235 
236     if (multiview)
237     {
238         multiviewCreateInfo  = MultiviewInfoPtr(new VkRenderPassMultiviewCreateInfo);
239         *multiviewCreateInfo = initVulkanStructure();
240 
241         viewMasks.resize(subpassDescriptions.size());
242         for (uint32_t subpassIdx = 0u; subpassIdx < static_cast<uint32_t>(viewMasks.size()); ++subpassIdx)
243             viewMasks[subpassIdx] = (1u << subpassIdx);
244 
245         multiviewCreateInfo->subpassCount = static_cast<uint32_t>(viewMasks.size());
246         multiviewCreateInfo->pViewMasks   = de::dataOrNull(viewMasks);
247     }
248 
249     const VkRenderPassCreateInfo renderPassInfo = {
250         VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,         // VkStructureType                   sType
251         multiviewCreateInfo.get(),                         // const void*                       pNext
252         0u,                                                // VkRenderPassCreateFlags           flags
253         1u,                                                // uint32_t                          attachmentCount
254         &colorAttachmentDescription,                       // const VkAttachmentDescription*    pAttachments
255         static_cast<uint32_t>(subpassDescriptions.size()), // uint32_t                          subpassCount
256         de::dataOrNull(subpassDescriptions),               // const VkSubpassDescription*       pSubpasses
257         static_cast<uint32_t>(dependencies.size()),        // uint32_t                          dependencyCount
258         de::dataOrNull(dependencies),                      // const VkSubpassDependency*        pDependencies
259     };
260 
261     return createRenderPass(vkd, device, &renderPassInfo);
262 }
263 
iterate()264 tcu::TestStatus MeshShaderBuiltinInstance::iterate()
265 {
266     const auto &vkd       = m_context.getDeviceInterface();
267     const auto device     = m_context.getDevice();
268     auto &alloc           = m_context.getDefaultAllocator();
269     const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
270     const auto queue      = m_context.getUniversalQueue();
271     const auto &binaries  = m_context.getBinaryCollection();
272 
273     const auto useTask         = binaries.contains("task");
274     const auto useFrag         = binaries.contains("frag");
275     const auto extent          = makeExtent3D(m_params.colorExtent.width, m_params.colorExtent.height, 1u);
276     const auto iExtent3D       = tcu::IVec3(static_cast<int>(extent.width), static_cast<int>(extent.height),
277                                             static_cast<int>(m_params.numLayers));
278     const auto format          = VK_FORMAT_R8G8B8A8_UNORM;
279     const auto tcuFormat       = mapVkFormat(format);
280     const auto colorUsage      = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
281     const auto viewType        = ((m_params.numLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D);
282     const auto colorSRR        = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers);
283     const auto colorSRL        = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers);
284     const auto numPasses       = (m_params.multiview ? m_params.numLayers : 1u);
285     const tcu::Vec4 clearColor = getClearColor();
286 
287     ImageWithMemoryPtr colorBuffer;
288     Move<VkImageView> colorBufferView;
289     {
290         const VkImageCreateInfo colorBufferInfo = {
291             VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
292             nullptr,                             // const void* pNext;
293             0u,                                  // VkImageCreateFlags flags;
294             VK_IMAGE_TYPE_2D,                    // VkImageType imageType;
295             format,                              // VkFormat format;
296             extent,                              // VkExtent3D extent;
297             1u,                                  // uint32_t mipLevels;
298             m_params.numLayers,                  // uint32_t arrayLayers;
299             VK_SAMPLE_COUNT_1_BIT,               // VkSampleCountFlagBits samples;
300             VK_IMAGE_TILING_OPTIMAL,             // VkImageTiling tiling;
301             colorUsage,                          // VkImageUsageFlags usage;
302             VK_SHARING_MODE_EXCLUSIVE,           // VkSharingMode sharingMode;
303             0u,                                  // uint32_t queueFamilyIndexCount;
304             nullptr,                             // const uint32_t* pQueueFamilyIndices;
305             VK_IMAGE_LAYOUT_UNDEFINED,           // VkImageLayout initialLayout;
306         };
307         colorBuffer =
308             ImageWithMemoryPtr(new ImageWithMemory(vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any));
309         colorBufferView = makeImageView(vkd, device, colorBuffer->get(), viewType, format, colorSRR);
310     }
311 
312     // Empty descriptor set layout.
313     DescriptorSetLayoutBuilder layoutBuilder;
314     const auto setLayout = layoutBuilder.build(vkd, device);
315 
316     // Pipeline layout.
317     const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
318 
319     // Render pass and framebuffer.
320     const auto renderPass  = createCustomRenderPass(vkd, device, format, m_params.multiview, m_params.numLayers);
321     const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorBufferView.get(), extent.width,
322                                              extent.height, (m_params.multiview ? 1u : m_params.numLayers));
323 
324     // Pipeline.
325     Move<VkShaderModule> taskModule;
326     Move<VkShaderModule> meshModule;
327     Move<VkShaderModule> fragModule;
328 
329     if (useTask)
330         taskModule = createShaderModule(vkd, device, binaries.get("task"));
331     if (useFrag)
332         fragModule = createShaderModule(vkd, device, binaries.get("frag"));
333     meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
334 
335     std::vector<VkViewport> viewports;
336     std::vector<VkRect2D> scissors;
337     if (m_params.viewports.empty())
338     {
339         // Default ones.
340         viewports.push_back(makeViewport(extent));
341         scissors.push_back(makeRect2D(extent));
342     }
343     else
344     {
345         // The desired viewports and the same number of default scissors.
346         viewports.reserve(m_params.viewports.size());
347         std::copy(begin(m_params.viewports), end(m_params.viewports), std::back_inserter(viewports));
348         scissors.resize(viewports.size(), makeRect2D(extent));
349     }
350 
351     using ShadingRateInfoPtr = de::MovePtr<VkPipelineFragmentShadingRateStateCreateInfoKHR>;
352     ShadingRateInfoPtr pNext;
353     if (static_cast<bool>(m_params.fragmentSize))
354     {
355         pNext  = ShadingRateInfoPtr(new VkPipelineFragmentShadingRateStateCreateInfoKHR);
356         *pNext = initVulkanStructure();
357 
358         pNext->fragmentSize   = getShadingRateSize(m_params.fragmentSize.get());
359         pNext->combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR;
360         pNext->combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
361     }
362 
363     // Pipelines.
364     std::vector<Move<VkPipeline>> pipelines;
365     pipelines.reserve(numPasses);
366     for (uint32_t subpassIdx = 0u; subpassIdx < numPasses; ++subpassIdx)
367     {
368         pipelines.emplace_back(makeGraphicsPipeline(
369             vkd, device, pipelineLayout.get(), taskModule.get(), meshModule.get(), fragModule.get(), renderPass.get(),
370             viewports, scissors, subpassIdx, nullptr, nullptr, nullptr, nullptr, nullptr, 0u, pNext.get()));
371     }
372 
373     // Command pool and buffer.
374     const auto cmdPool      = makeCommandPool(vkd, device, queueIndex);
375     const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
376     const auto cmdBuffer    = cmdBufferPtr.get();
377 
378     // Indirect buffer if needed.
379     BufferWithMemoryPtr indirectBuffer;
380 
381     DE_ASSERT(!m_params.drawArgs.empty());
382     if (m_params.indirect)
383     {
384         // Indirect draws.
385         const auto indirectBufferSize  = static_cast<VkDeviceSize>(de::dataSize(m_params.drawArgs));
386         const auto indirectBufferUsage = (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
387         const auto indirectBufferInfo  = makeBufferCreateInfo(indirectBufferSize, indirectBufferUsage);
388         indirectBuffer                 = BufferWithMemoryPtr(
389             new BufferWithMemory(vkd, device, alloc, indirectBufferInfo, MemoryRequirement::HostVisible));
390         auto &indirectBufferAlloc = indirectBuffer->getAllocation();
391         void *indirectBufferData  = indirectBufferAlloc.getHostPtr();
392 
393         deMemcpy(indirectBufferData, m_params.drawArgs.data(), static_cast<size_t>(indirectBufferSize));
394         flushAlloc(vkd, device, indirectBufferAlloc);
395     }
396 
397     // Submit commands.
398     beginCommandBuffer(vkd, cmdBuffer);
399     beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor);
400 
401     for (uint32_t subpassIdx = 0u; subpassIdx < numPasses; ++subpassIdx)
402     {
403         if (subpassIdx > 0u)
404             vkd.cmdNextSubpass(cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
405 
406         vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[subpassIdx].get());
407 
408         if (!m_params.indirect)
409         {
410             for (const auto &command : m_params.drawArgs)
411                 vkd.cmdDrawMeshTasksEXT(cmdBuffer, command.groupCountX, command.groupCountY, command.groupCountZ);
412         }
413         else
414         {
415             const auto numDraws = static_cast<uint32_t>(m_params.drawArgs.size());
416             const auto stride   = static_cast<uint32_t>(sizeof(decltype(m_params.drawArgs)::value_type));
417             vkd.cmdDrawMeshTasksIndirectEXT(cmdBuffer, indirectBuffer->get(), 0ull, numDraws, stride);
418         }
419     }
420 
421     endRenderPass(vkd, cmdBuffer);
422 
423     // Output buffer to extract the color buffer contents.
424     BufferWithMemoryPtr outBuffer;
425     void *outBufferData = nullptr;
426     {
427         const auto layerSize      = static_cast<VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) *
428                                                          extent.width * extent.height);
429         const auto outBufferSize  = layerSize * m_params.numLayers;
430         const auto outBufferUsage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
431         const auto outBufferInfo  = makeBufferCreateInfo(outBufferSize, outBufferUsage);
432 
433         outBuffer = BufferWithMemoryPtr(
434             new BufferWithMemory(vkd, device, alloc, outBufferInfo, MemoryRequirement::HostVisible));
435         outBufferData = outBuffer->getAllocation().getHostPtr();
436     }
437 
438     // Transition image layout.
439     const auto preTransferBarrier = makeImageMemoryBarrier(
440         (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), VK_ACCESS_TRANSFER_READ_BIT,
441         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer->get(), colorSRR);
442 
443     vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
444                            0u, nullptr, 0u, nullptr, 1u, &preTransferBarrier);
445 
446     // Copy image to output buffer.
447     const std::vector<VkBufferImageCopy> regions(1u, makeBufferImageCopy(extent, colorSRL));
448     vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outBuffer->get(),
449                              static_cast<uint32_t>(regions.size()), de::dataOrNull(regions));
450 
451     // Transfer to host barrier.
452     const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
453     vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u,
454                            &postTransferBarrier, 0u, nullptr, 0u, nullptr);
455 
456     endCommandBuffer(vkd, cmdBuffer);
457     submitCommandsAndWait(vkd, device, queue, cmdBuffer);
458 
459     // Invalidate alloc and verify result.
460     {
461         auto &outBufferAlloc = outBuffer->getAllocation();
462         invalidateAlloc(vkd, device, outBufferAlloc);
463 
464         tcu::ConstPixelBufferAccess result(tcuFormat, iExtent3D, outBufferData);
465         verifyResults(result);
466     }
467 
468     return tcu::TestStatus::pass("Pass");
469 }
470 
471 // Abstract case that implements the generic checkSupport method.
472 class MeshShaderBuiltinCase : public vkt::TestCase
473 {
474 public:
MeshShaderBuiltinCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)475     MeshShaderBuiltinCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
476         : vkt::TestCase(testCtx, name)
477         , m_taskNeeded(taskNeeded)
478     {
479     }
~MeshShaderBuiltinCase(void)480     virtual ~MeshShaderBuiltinCase(void)
481     {
482     }
483 
484     void checkSupport(Context &context) const override;
485 
486 protected:
487     const bool m_taskNeeded;
488 };
489 
checkSupport(Context & context) const490 void MeshShaderBuiltinCase::checkSupport(Context &context) const
491 {
492     checkTaskMeshShaderSupportEXT(context, m_taskNeeded, true);
493 }
494 
495 // Instance that verifies color layers.
496 class FullScreenColorInstance : public MeshShaderBuiltinInstance
497 {
498 public:
FullScreenColorInstance(Context & context,const IterationParams & params,const ColorVec & expectedColors)499     FullScreenColorInstance(Context &context, const IterationParams &params, const ColorVec &expectedColors)
500         : MeshShaderBuiltinInstance(context, params)
501         , m_expectedColors(expectedColors)
502     {
503     }
~FullScreenColorInstance(void)504     virtual ~FullScreenColorInstance(void)
505     {
506     }
507 
508     void verifyResults(const tcu::ConstPixelBufferAccess &result) override;
509 
510 protected:
511     const ColorVec m_expectedColors;
512 };
513 
verifyResults(const tcu::ConstPixelBufferAccess & result)514 void FullScreenColorInstance::verifyResults(const tcu::ConstPixelBufferAccess &result)
515 {
516     auto &log         = m_context.getTestContext().getLog();
517     bool fail         = false;
518     const auto width  = result.getWidth();
519     const auto height = result.getHeight();
520     const auto depth  = result.getDepth();
521 
522     for (int z = 0; z < depth; ++z)
523     {
524         const auto &expected = m_expectedColors.at(z);
525 
526         for (int y = 0; y < height; ++y)
527             for (int x = 0; x < width; ++x)
528             {
529                 const auto resultColor = result.getPixel(x, y, z);
530                 if (resultColor != expected)
531                 {
532                     std::ostringstream msg;
533                     msg << "Pixel (" << x << ", " << y << ", " << z << ") failed: expected " << expected
534                         << " and found " << resultColor;
535                     log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
536                     fail = true;
537                 }
538             }
539     }
540 
541     if (fail)
542     {
543         log << tcu::TestLog::Image("Result", "", result);
544         TCU_FAIL("Check log for details");
545     }
546 }
547 
548 // Instance that verifies single-layer framebuffers divided into 4 quadrants.
549 class QuadrantsInstance : public MeshShaderBuiltinInstance
550 {
551 public:
QuadrantsInstance(Context & context,const IterationParams & params,const tcu::Vec4 topLeft,const tcu::Vec4 topRight,const tcu::Vec4 bottomLeft,const tcu::Vec4 bottomRight)552     QuadrantsInstance(Context &context, const IterationParams &params, const tcu::Vec4 topLeft,
553                       const tcu::Vec4 topRight, const tcu::Vec4 bottomLeft, const tcu::Vec4 bottomRight)
554         : MeshShaderBuiltinInstance(context, params)
555         , m_topLeft(topLeft)
556         , m_topRight(topRight)
557         , m_bottomLeft(bottomLeft)
558         , m_bottomRight(bottomRight)
559     {
560     }
~QuadrantsInstance(void)561     virtual ~QuadrantsInstance(void)
562     {
563     }
564 
565     void verifyResults(const tcu::ConstPixelBufferAccess &result) override;
566 
567 protected:
568     const tcu::Vec4 m_topLeft;
569     const tcu::Vec4 m_topRight;
570     const tcu::Vec4 m_bottomLeft;
571     const tcu::Vec4 m_bottomRight;
572 };
573 
verifyResults(const tcu::ConstPixelBufferAccess & result)574 void QuadrantsInstance::verifyResults(const tcu::ConstPixelBufferAccess &result)
575 {
576     auto &log         = m_context.getTestContext().getLog();
577     bool fail         = false;
578     const auto width  = result.getWidth();
579     const auto height = result.getHeight();
580     const auto depth  = result.getDepth();
581 
582     DE_ASSERT(depth == 1);
583     DE_ASSERT(width > 0 && width % 2 == 0);
584     DE_ASSERT(height > 0 && height % 2 == 0);
585     DE_UNREF(depth); // For release builds.
586 
587     const auto halfWidth  = width / 2;
588     const auto halfHeight = height / 2;
589     tcu::Vec4 expected;
590 
591     for (int y = 0; y < height; ++y)
592         for (int x = 0; x < width; ++x)
593         {
594             // Choose the right quadrant
595             if (y < halfHeight)
596                 expected = ((x < halfWidth) ? m_topLeft : m_topRight);
597             else
598                 expected = ((x < halfWidth) ? m_bottomLeft : m_bottomRight);
599 
600             const auto resultColor = result.getPixel(x, y);
601             if (resultColor != expected)
602             {
603                 std::ostringstream msg;
604                 msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
605                 log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
606                 fail = true;
607             }
608         }
609 
610     if (fail)
611     {
612         log << tcu::TestLog::Image("Result", "", result);
613         TCU_FAIL("Check log for details");
614     }
615 }
616 
617 // Instance that verifies single-layer framebuffers with specific pixels set to some color.
618 struct PixelVerifierParams
619 {
620     const tcu::Vec4 background;
621     const PixelMap pixelMap;
622 };
623 
624 class PixelsInstance : public MeshShaderBuiltinInstance
625 {
626 public:
PixelsInstance(Context & context,const IterationParams & params,const PixelVerifierParams & pixelParams)627     PixelsInstance(Context &context, const IterationParams &params, const PixelVerifierParams &pixelParams)
628         : MeshShaderBuiltinInstance(context, params)
629         , m_pixelParams(pixelParams)
630     {
631     }
~PixelsInstance(void)632     virtual ~PixelsInstance(void)
633     {
634     }
635 
636     void verifyResults(const tcu::ConstPixelBufferAccess &result) override;
637 
638 protected:
639     const PixelVerifierParams m_pixelParams;
640 };
641 
verifyResults(const tcu::ConstPixelBufferAccess & result)642 void PixelsInstance::verifyResults(const tcu::ConstPixelBufferAccess &result)
643 {
644     auto &log         = m_context.getTestContext().getLog();
645     bool fail         = false;
646     const auto width  = result.getWidth();
647     const auto height = result.getHeight();
648     const auto depth  = result.getDepth();
649 
650     DE_ASSERT(depth == 1);
651     DE_UNREF(depth); // For release builds.
652 
653     for (int y = 0; y < height; ++y)
654         for (int x = 0; x < width; ++x)
655         {
656             const tcu::IVec2 coords(x, y);
657             const auto iter        = m_pixelParams.pixelMap.find(coords);
658             const auto expected    = ((iter == m_pixelParams.pixelMap.end()) ? m_pixelParams.background : iter->second);
659             const auto resultColor = result.getPixel(x, y);
660 
661             if (resultColor != expected)
662             {
663                 std::ostringstream msg;
664                 msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
665                 log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
666                 fail = true;
667             }
668         }
669 
670     if (fail)
671     {
672         log << tcu::TestLog::Image("Result", "", result);
673         TCU_FAIL("Check log for details");
674     }
675 }
676 
677 // Primitive ID case.
678 class PrimitiveIdCase : public MeshShaderBuiltinCase
679 {
680 public:
PrimitiveIdCase(tcu::TestContext & testCtx,const std::string & name,bool glslFrag)681     PrimitiveIdCase(tcu::TestContext &testCtx, const std::string &name, bool glslFrag)
682         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
683         , m_glslFrag(glslFrag)
684     {
685     }
~PrimitiveIdCase(void)686     virtual ~PrimitiveIdCase(void)
687     {
688     }
689 
690     void initPrograms(vk::SourceCollections &programCollection) const override;
691     void checkSupport(Context &context) const override;
692     TestInstance *createInstance(Context &context) const override;
693 
694 protected:
695     // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
696     const bool m_glslFrag;
697 };
698 
initPrograms(vk::SourceCollections & programCollection) const699 void PrimitiveIdCase::initPrograms(vk::SourceCollections &programCollection) const
700 {
701     const auto buildOptions    = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
702     const auto spvBuildOptions = getMinMeshEXTSpvBuildOptions(programCollection.usedVulkanVersion);
703 
704     // Mesh shader.
705     {
706         std::ostringstream mesh;
707         mesh << "#version 460\n"
708              << "#extension GL_EXT_mesh_shader : enable\n"
709              << "\n"
710              << "layout (local_size_x=1) in;\n"
711              << "layout (triangles) out;\n"
712              << "layout (max_vertices=3, max_primitives=1) out;\n"
713              << "\n"
714              << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
715              << "   int gl_PrimitiveID;\n"
716              << "} gl_MeshPrimitivesEXT[];\n"
717              << "\n"
718              << "void main ()\n"
719              << "{\n"
720              << "    SetMeshOutputsEXT(3u, 1u);\n"
721              << "\n"
722              << "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
723              << "\n"
724              << "    gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
725              << "    gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
726              << "    gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
727              << "\n"
728              // Sets an arbitrary primitive id.
729              << "    gl_MeshPrimitivesEXT[0].gl_PrimitiveID = 1629198956;\n"
730              << "}\n";
731         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
732     }
733 
734     // Frag shader.
735     if (m_glslFrag)
736     {
737         std::ostringstream frag;
738         frag << "#version 460\n"
739              << "#extension GL_EXT_mesh_shader : enable\n"
740              << "\n"
741              << "layout (location=0) out vec4 outColor;\n"
742              << "\n"
743              << "void main ()\n"
744              << "{\n"
745              // Checks the primitive id matches.
746              << "    outColor = ((gl_PrimitiveID == 1629198956) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, "
747                 "1.0));\n"
748              << "}\n";
749         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
750     }
751     else
752     {
753         // This is the same shader as above, but OpCapability Geometry has been replaced by OpCapability MeshShadingEXT in order to
754         // access gl_PrimitiveID. This also needs the SPV_EXT_mesh_shader extension.
755         std::ostringstream frag;
756         frag << "; Version: 1.0\n"
757              << "; Generator: Khronos Glslang Reference Front End; 10\n"
758              << "; Bound: 24\n"
759              << "; Schema: 0\n"
760              << "      OpCapability Shader\n"
761 
762              // Manual change in these lines.
763              //<< "      OpCapability Geometry\n"
764              << "      OpCapability MeshShadingEXT\n"
765              << "      OpExtension \"SPV_EXT_mesh_shader\"\n"
766 
767              << " %1 = OpExtInstImport \"GLSL.std.450\"\n"
768              << "      OpMemoryModel Logical GLSL450\n"
769              << "      OpEntryPoint Fragment %4 \"main\" %9 %12\n"
770              << "      OpExecutionMode %4 OriginUpperLeft\n"
771              << "      OpDecorate %9 Location 0\n"
772              << "      OpDecorate %12 Flat\n"
773              << "      OpDecorate %12 BuiltIn PrimitiveId\n"
774              << " %2 = OpTypeVoid\n"
775              << " %3 = OpTypeFunction %2\n"
776              << " %6 = OpTypeFloat 32\n"
777              << " %7 = OpTypeVector %6 4\n"
778              << " %8 = OpTypePointer Output %7\n"
779              << " %9 = OpVariable %8 Output\n"
780              << "%10 = OpTypeInt 32 1\n"
781              << "%11 = OpTypePointer Input %10\n"
782              << "%12 = OpVariable %11 Input\n"
783              << "%14 = OpConstant %10 1629198956\n"
784              << "%15 = OpTypeBool\n"
785              << "%17 = OpConstant %6 0\n"
786              << "%18 = OpConstant %6 1\n"
787              << "%19 = OpConstantComposite %7 %17 %17 %18 %18\n"
788              << "%20 = OpConstantComposite %7 %17 %17 %17 %18\n"
789              << "%21 = OpTypeVector %15 4\n"
790              << " %4 = OpFunction %2 None %3\n"
791              << " %5 = OpLabel\n"
792              << "%13 = OpLoad %10 %12\n"
793              << "%16 = OpIEqual %15 %13 %14\n"
794              << "%22 = OpCompositeConstruct %21 %16 %16 %16 %16\n"
795              << "%23 = OpSelect %7 %22 %19 %20\n"
796              << "      OpStore %9 %23\n"
797              << "      OpReturn\n"
798              << "      OpFunctionEnd\n";
799         programCollection.spirvAsmSources.add("frag") << frag.str() << spvBuildOptions;
800     }
801 }
802 
checkSupport(Context & context) const803 void PrimitiveIdCase::checkSupport(Context &context) const
804 {
805     MeshShaderBuiltinCase::checkSupport(context);
806 
807     // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
808     if (m_glslFrag)
809         context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
810 }
811 
createInstance(Context & context) const812 TestInstance *PrimitiveIdCase::createInstance(Context &context) const
813 {
814     const ColorVec expectedColors(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
815     const IterationParams iterationParams = {
816         getDefaultExtent(),       // VkExtent2D colorExtent;
817         1u,                       // uint32_t numLayers;
818         false,                    // bool multiview;
819         false,                    // bool indirect;
820         tcu::Nothing,             // tcu::Maybe<FragmentSize> fragmentSize;
821         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
822         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
823     };
824     return new FullScreenColorInstance(context, iterationParams, expectedColors);
825 }
826 
827 // Layer builtin case.
828 class LayerCase : public MeshShaderBuiltinCase
829 {
830 public:
LayerCase(tcu::TestContext & testCtx,const std::string & name,bool writeVal,bool shareVertices)831     LayerCase(tcu::TestContext &testCtx, const std::string &name, bool writeVal, bool shareVertices)
832         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
833         , m_shareVertices(shareVertices)
834         , m_writeVal(writeVal)
835     {
836     }
~LayerCase(void)837     virtual ~LayerCase(void)
838     {
839     }
840 
841     void initPrograms(vk::SourceCollections &programCollection) const override;
842     void checkSupport(Context &context) const override;
843     TestInstance *createInstance(Context &context) const override;
844 
845     static constexpr uint32_t kNumLayers = 4u;
846 
847 protected:
848     const bool m_shareVertices;
849     const bool m_writeVal;
850 };
851 
initPrograms(vk::SourceCollections & programCollection) const852 void LayerCase::initPrograms(vk::SourceCollections &programCollection) const
853 {
854     const auto buildOptions  = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
855     const auto localSize     = (m_shareVertices ? kNumLayers : 1u);
856     const auto numPrimitives = (m_shareVertices ? kNumLayers : 1u);
857     const auto layerNumber   = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
858 
859     // One layer per local invocation or work group (shared vertices or not, respectively).
860     {
861         std::ostringstream mesh;
862         mesh << "#version 460\n"
863              << "#extension GL_EXT_mesh_shader : enable\n"
864              << "\n"
865              << "layout (local_size_x=" << localSize << ") in;\n"
866              << "layout (triangles) out;\n"
867              << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
868              << "\n"
869              << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
870              << "   int gl_Layer;\n"
871              << "} gl_MeshPrimitivesEXT[];\n"
872              << "\n"
873              << "void main ()\n"
874              << "{\n"
875              << "    SetMeshOutputsEXT(3u, " << numPrimitives << ");\n"
876              << "\n"
877              << "    if (gl_LocalInvocationIndex == 0u)\n"
878              << "    {\n"
879              << "        gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
880              << "        gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
881              << "        gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
882              << "    }\n"
883              << "\n"
884              << "    gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);\n";
885 
886         if (m_writeVal)
887             mesh << "    gl_MeshPrimitivesEXT[gl_LocalInvocationIndex].gl_Layer = int(" << layerNumber << ");\n";
888 
889         mesh << "}\n";
890 
891         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
892     }
893 
894     // Fragment shader chooses one color per layer.
895     {
896         std::ostringstream frag;
897         frag << "#version 460\n"
898              << "#extension GL_EXT_mesh_shader : enable\n"
899              << "\n"
900              << "layout (location=0) out vec4 outColor;\n"
901              << "\n"
902              << "vec4 colors[" << kNumLayers << "] = vec4[](\n"
903              << "    vec4(0.0, 0.0, 1.0, 1.0),\n"
904              << "    vec4(1.0, 0.0, 1.0, 1.0),\n"
905              << "    vec4(0.0, 1.0, 1.0, 1.0),\n"
906              << "    vec4(1.0, 1.0, 0.0, 1.0)\n"
907              << ");\n"
908              << "\n"
909              << "void main ()\n"
910              << "{\n"
911              << "    outColor = colors[gl_Layer];\n"
912              << "}\n";
913         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
914     }
915 }
916 
checkSupport(Context & context) const917 void LayerCase::checkSupport(Context &context) const
918 {
919     MeshShaderBuiltinCase::checkSupport(context);
920 
921     if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
922         context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
923     else
924     {
925         const auto &features = context.getDeviceVulkan12Features();
926         if (!features.shaderOutputLayer)
927             TCU_THROW(NotSupportedError, "shaderOutputLayer feature not supported");
928     }
929 }
930 
createInstance(Context & context) const931 TestInstance *LayerCase::createInstance(Context &context) const
932 {
933     ColorVec expectedColors;
934 
935     const auto usedLayers    = (m_writeVal ? kNumLayers : 1u);
936     const auto numWorkGroups = (m_shareVertices ? 1u : kNumLayers);
937 
938     expectedColors.reserve(usedLayers);
939     expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0));
940 
941     if (m_writeVal)
942     {
943         expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0));
944         expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0));
945         expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0));
946     }
947 
948     const IterationParams iterationParams = {
949         getDefaultExtent(),                    // VkExtent2D colorExtent;
950         usedLayers,                            // uint32_t numLayers;
951         false,                                 // bool multiview;
952         false,                                 // bool indirect;
953         tcu::Nothing,                          // tcu::Maybe<FragmentSize> fragmentSize;
954         getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs;
955         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
956     };
957     return new FullScreenColorInstance(context, iterationParams, expectedColors);
958 }
959 
960 // ViewportIndex builtin case.
961 class ViewportIndexCase : public MeshShaderBuiltinCase
962 {
963 public:
ViewportIndexCase(tcu::TestContext & testCtx,const std::string & name,bool writeVal,bool shareVertices)964     ViewportIndexCase(tcu::TestContext &testCtx, const std::string &name, bool writeVal, bool shareVertices)
965         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
966         , m_shareVertices(shareVertices)
967         , m_writeVal(writeVal)
968     {
969     }
~ViewportIndexCase(void)970     virtual ~ViewportIndexCase(void)
971     {
972     }
973 
974     void initPrograms(vk::SourceCollections &programCollection) const override;
975     void checkSupport(Context &context) const override;
976     TestInstance *createInstance(Context &context) const override;
977 
978     static constexpr uint32_t kQuadrants = 4u;
979 
980 protected:
981     const bool m_shareVertices;
982     const bool m_writeVal;
983 };
984 
initPrograms(vk::SourceCollections & programCollection) const985 void ViewportIndexCase::initPrograms(vk::SourceCollections &programCollection) const
986 {
987     const auto buildOptions  = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
988     const auto localSize     = (m_shareVertices ? kQuadrants : 1u);
989     const auto numPrimitives = (m_shareVertices ? kQuadrants : 1u);
990     const auto viewportIndex = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
991 
992     // One viewport per local invocation or work group (sharing vertices or not, respectively).
993     {
994         std::ostringstream mesh;
995         mesh << "#version 460\n"
996              << "#extension GL_EXT_mesh_shader : enable\n"
997              << "\n"
998              << "layout (local_size_x=" << localSize << ") in;\n"
999              << "layout (triangles) out;\n"
1000              << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
1001              << "\n"
1002              << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
1003              << "   int gl_ViewportIndex;\n"
1004              << "} gl_MeshPrimitivesEXT[];\n"
1005              << "\n"
1006              << "void main ()\n"
1007              << "{\n"
1008              << "    SetMeshOutputsEXT(3u, " << numPrimitives << ");\n"
1009              << "\n"
1010              << "    if (gl_LocalInvocationIndex == 0u)\n"
1011              << "    {\n"
1012              << "        gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1013              << "        gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
1014              << "        gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
1015              << "    }\n"
1016              << "\n"
1017              << "    gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);\n";
1018 
1019         if (m_writeVal)
1020             mesh << "    gl_MeshPrimitivesEXT[gl_LocalInvocationIndex].gl_ViewportIndex = int(" << viewportIndex
1021                  << ");\n";
1022 
1023         mesh << "}\n";
1024 
1025         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1026     }
1027 
1028     // Fragment shader chooses one color per viewport.
1029     {
1030         std::ostringstream frag;
1031         frag << "#version 460\n"
1032              << "#extension GL_EXT_mesh_shader : enable\n"
1033              << "\n"
1034              << "layout (location=0) out vec4 outColor;\n"
1035              << "\n"
1036              << "vec4 colors[" << kQuadrants << "] = vec4[](\n"
1037              << "    vec4(0.0, 0.0, 1.0, 1.0),\n"
1038              << "    vec4(1.0, 0.0, 1.0, 1.0),\n"
1039              << "    vec4(0.0, 1.0, 1.0, 1.0),\n"
1040              << "    vec4(1.0, 1.0, 0.0, 1.0)\n"
1041              << ");\n"
1042              << "\n"
1043              << "void main ()\n"
1044              << "{\n"
1045              << "    outColor = colors[gl_ViewportIndex];\n"
1046              << "}\n";
1047         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
1048     }
1049 }
1050 
checkSupport(Context & context) const1051 void ViewportIndexCase::checkSupport(Context &context) const
1052 {
1053     MeshShaderBuiltinCase::checkSupport(context);
1054     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
1055 
1056     if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
1057         context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
1058     else
1059     {
1060         const auto &features = context.getDeviceVulkan12Features();
1061         if (!features.shaderOutputViewportIndex)
1062             TCU_THROW(NotSupportedError, "shaderOutputViewportIndex feature not supported");
1063     }
1064 }
1065 
createInstance(Context & context) const1066 TestInstance *ViewportIndexCase::createInstance(Context &context) const
1067 {
1068     const auto extent = getDefaultExtent();
1069 
1070     DE_ASSERT(extent.width > 0u && extent.width % 2u == 0u);
1071     DE_ASSERT(extent.height > 0u && extent.height % 2u == 0u);
1072 
1073     const auto halfWidth  = static_cast<float>(extent.width) / 2.0f;
1074     const auto halfHeight = static_cast<float>(extent.height) / 2.0f;
1075 
1076     const auto topLeft     = tcu::Vec4(0.0, 0.0, 1.0, 1.0);
1077     const auto topRight    = (m_writeVal ? tcu::Vec4(1.0, 0.0, 1.0, 1.0) : getClearColor());
1078     const auto bottomLeft  = (m_writeVal ? tcu::Vec4(0.0, 1.0, 1.0, 1.0) : getClearColor());
1079     const auto bottomRight = (m_writeVal ? tcu::Vec4(1.0, 1.0, 0.0, 1.0) : getClearColor());
1080 
1081     ViewportVec viewports;
1082     viewports.reserve(kQuadrants);
1083     viewports.emplace_back(makeViewport(0.0f, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f));
1084     viewports.emplace_back(makeViewport(halfWidth, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f));
1085     viewports.emplace_back(makeViewport(0.0f, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f));
1086     viewports.emplace_back(makeViewport(halfWidth, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f));
1087 
1088     const auto numWorkGroups              = (m_shareVertices ? 1u : kQuadrants);
1089     const IterationParams iterationParams = {
1090         getDefaultExtent(),                    // VkExtent2D colorExtent;
1091         1u,                                    // uint32_t numLayers;
1092         false,                                 // bool multiview;
1093         false,                                 // bool indirect;
1094         tcu::Nothing,                          // tcu::Maybe<FragmentSize> fragmentSize;
1095         getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs;
1096         std::move(viewports),                  // ViewportVec viewports;
1097     };
1098     return new QuadrantsInstance(context, iterationParams, topLeft, topRight, bottomLeft, bottomRight);
1099 }
1100 
1101 // Position builtin case.
1102 class PositionCase : public MeshShaderBuiltinCase
1103 {
1104 public:
PositionCase(tcu::TestContext & testCtx,const std::string & name)1105     PositionCase(tcu::TestContext &testCtx, const std::string &name)
1106         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1107     {
1108     }
~PositionCase(void)1109     virtual ~PositionCase(void)
1110     {
1111     }
1112 
1113     void initPrograms(vk::SourceCollections &programCollection) const override;
1114     TestInstance *createInstance(Context &context) const override;
1115 };
1116 
initPrograms(vk::SourceCollections & programCollection) const1117 void PositionCase::initPrograms(vk::SourceCollections &programCollection) const
1118 {
1119     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1120 
1121     // Mesh shader: emit single triangle around the center of the top left pixel.
1122     {
1123         const auto extent  = getDefaultExtent();
1124         const auto fWidth  = static_cast<float>(extent.width);
1125         const auto fHeight = static_cast<float>(extent.height);
1126 
1127         const auto pxWidth  = 2.0f / fWidth;
1128         const auto pxHeight = 2.0f / fHeight;
1129 
1130         const auto halfXPix = pxWidth / 2.0f;
1131         const auto halfYPix = pxHeight / 2.0f;
1132 
1133         // Center of top left pixel.
1134         const auto x = -1.0f + halfXPix;
1135         const auto y = -1.0f + halfYPix;
1136 
1137         std::ostringstream mesh;
1138         mesh << "#version 460\n"
1139              << "#extension GL_EXT_mesh_shader : enable\n"
1140              << "\n"
1141              << "layout (local_size_x=1) in;\n"
1142              << "layout (triangles) out;\n"
1143              << "layout (max_vertices=3, max_primitives=1) out;\n"
1144              << "\n"
1145              << "void main ()\n"
1146              << "{\n"
1147              << "    SetMeshOutputsEXT(3u, 1u);\n"
1148              << "\n"
1149              << "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
1150              << "\n"
1151              << "    gl_MeshVerticesEXT[0].gl_Position = vec4(" << (x - halfXPix) << ", " << (y + halfYPix)
1152              << ", 0.0, 1.0);\n"
1153              << "    gl_MeshVerticesEXT[1].gl_Position = vec4(" << (x + halfXPix) << ", " << (y + halfYPix)
1154              << ", 0.0, 1.0);\n"
1155              << "    gl_MeshVerticesEXT[2].gl_Position = vec4(" << x << ", " << (y - halfYPix) << ", 0.0, 1.0);\n"
1156              << "}\n";
1157         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1158     }
1159 
1160     // Basic fragment shader.
1161     {
1162         const auto frag = getBasicFragShader();
1163         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1164     }
1165 }
1166 
createInstance(Context & context) const1167 TestInstance *PositionCase::createInstance(Context &context) const
1168 {
1169     const IterationParams iterationParams = {
1170         getDefaultExtent(),       // VkExtent2D colorExtent;
1171         1u,                       // uint32_t numLayers;
1172         false,                    // bool multiview;
1173         false,                    // bool indirect;
1174         tcu::Nothing,             // tcu::Maybe<FragmentSize> fragmentSize;
1175         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1176         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1177     };
1178 
1179     // Must match the shader.
1180     PixelMap pixelMap;
1181     pixelMap[tcu::IVec2(0, 0)] = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
1182 
1183     const PixelVerifierParams verifierParams = {
1184         getClearColor(),     // const tcu::Vec4 background;
1185         std::move(pixelMap), // const PixelMap pixelMap;
1186     };
1187     return new PixelsInstance(context, iterationParams, verifierParams);
1188 }
1189 
1190 // PointSize builtin case.
1191 class PointSizeCase : public MeshShaderBuiltinCase
1192 {
1193 public:
PointSizeCase(tcu::TestContext & testCtx,const std::string & name)1194     PointSizeCase(tcu::TestContext &testCtx, const std::string &name)
1195         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1196     {
1197     }
~PointSizeCase(void)1198     virtual ~PointSizeCase(void)
1199     {
1200     }
1201 
1202     void initPrograms(vk::SourceCollections &programCollection) const override;
1203     TestInstance *createInstance(Context &context) const override;
1204     void checkSupport(Context &context) const override;
1205 
1206     static constexpr float kPointSize = 4.0f;
1207 };
1208 
initPrograms(vk::SourceCollections & programCollection) const1209 void PointSizeCase::initPrograms(vk::SourceCollections &programCollection) const
1210 {
1211     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1212 
1213     // Mesh shader: large point covering the top left quadrant.
1214     {
1215         std::ostringstream mesh;
1216         mesh << "#version 460\n"
1217              << "#extension GL_EXT_mesh_shader : enable\n"
1218              << "\n"
1219              << "layout (local_size_x=1) in;\n"
1220              << "layout (points) out;\n"
1221              << "layout (max_vertices=1, max_primitives=1) out;\n"
1222              << "\n"
1223              << "void main ()\n"
1224              << "{\n"
1225              << "    SetMeshOutputsEXT(1u, 1u);\n"
1226              << "\n"
1227              << "    gl_PrimitivePointIndicesEXT[0] = 0u;\n"
1228              << "\n"
1229              << "    gl_MeshVerticesEXT[0].gl_Position = vec4(-0.5, -0.5, 0.0, 1.0);\n"
1230              << "    gl_MeshVerticesEXT[0].gl_PointSize = " << kPointSize << ";\n"
1231              << "}\n";
1232         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1233     }
1234 
1235     // Basic fragment shader.
1236     {
1237         const auto frag = getBasicFragShader();
1238         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1239     }
1240 }
1241 
createInstance(Context & context) const1242 TestInstance *PointSizeCase::createInstance(Context &context) const
1243 {
1244     const IterationParams iterationParams = {
1245         getDefaultExtent(),       // VkExtent2D colorExtent;
1246         1u,                       // uint32_t numLayers;
1247         false,                    // bool multiview;
1248         false,                    // bool indirect;
1249         tcu::Nothing,             // tcu::Maybe<FragmentSize> fragmentSize;
1250         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1251         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1252     };
1253 
1254     // Must match the shader.
1255     const tcu::Vec4 black = getClearColor();
1256     const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1257 
1258     return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1259 }
1260 
checkSupport(Context & context) const1261 void PointSizeCase::checkSupport(Context &context) const
1262 {
1263     MeshShaderBuiltinCase::checkSupport(context);
1264     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_LARGE_POINTS);
1265 
1266     const auto &properties = context.getDeviceProperties();
1267     if (kPointSize < properties.limits.pointSizeRange[0] || kPointSize > properties.limits.pointSizeRange[1])
1268         TCU_THROW(NotSupportedError, "Required point size outside point size range");
1269 }
1270 
1271 // ClipDistance builtin case.
1272 class ClipDistanceCase : public MeshShaderBuiltinCase
1273 {
1274 public:
ClipDistanceCase(tcu::TestContext & testCtx,const std::string & name)1275     ClipDistanceCase(tcu::TestContext &testCtx, const std::string &name)
1276         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1277     {
1278     }
~ClipDistanceCase(void)1279     virtual ~ClipDistanceCase(void)
1280     {
1281     }
1282 
1283     void initPrograms(vk::SourceCollections &programCollection) const override;
1284     TestInstance *createInstance(Context &context) const override;
1285     void checkSupport(Context &context) const override;
1286 };
1287 
initPrograms(vk::SourceCollections & programCollection) const1288 void ClipDistanceCase::initPrograms(vk::SourceCollections &programCollection) const
1289 {
1290     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1291 
1292     // Mesh shader: full-screen quad using different clip distances.
1293     {
1294         std::ostringstream mesh;
1295         mesh << "#version 460\n"
1296              << "#extension GL_EXT_mesh_shader : enable\n"
1297              << "\n"
1298              << "layout (local_size_x=1) in;\n"
1299              << "layout (triangles) out;\n"
1300              << "layout (max_vertices=4, max_primitives=2) out;\n"
1301              << "\n"
1302              << "out gl_MeshPerVertexEXT {\n"
1303              << "    vec4  gl_Position;\n"
1304              << "    float gl_ClipDistance[2];\n"
1305              << "} gl_MeshVerticesEXT[];\n"
1306              << "\n"
1307              << "void main ()\n"
1308              << "{\n"
1309              << "    SetMeshOutputsEXT(4u, 2u);\n"
1310              << "\n"
1311              << "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
1312              << "    gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 3u, 2u);\n"
1313              << "\n"
1314              << "    gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1315              << "    gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  1.0, 0.0, 1.0);\n"
1316              << "    gl_MeshVerticesEXT[2].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1317              << "    gl_MeshVerticesEXT[3].gl_Position = vec4( 1.0,  1.0, 0.0, 1.0);\n"
1318              << "\n"
1319              // The first clip plane keeps the left half of the frame buffer.
1320              << "    gl_MeshVerticesEXT[0].gl_ClipDistance[0] =  1.0;\n"
1321              << "    gl_MeshVerticesEXT[1].gl_ClipDistance[0] =  1.0;\n"
1322              << "    gl_MeshVerticesEXT[2].gl_ClipDistance[0] = -1.0;\n"
1323              << "    gl_MeshVerticesEXT[3].gl_ClipDistance[0] = -1.0;\n"
1324              << "\n"
1325              // The second clip plane keeps the top half of the frame buffer.
1326              << "    gl_MeshVerticesEXT[0].gl_ClipDistance[1] =  1.0;\n"
1327              << "    gl_MeshVerticesEXT[1].gl_ClipDistance[1] = -1.0;\n"
1328              << "    gl_MeshVerticesEXT[2].gl_ClipDistance[1] =  1.0;\n"
1329              << "    gl_MeshVerticesEXT[3].gl_ClipDistance[1] = -1.0;\n"
1330              << "}\n";
1331         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1332     }
1333 
1334     // Fragment shader chooses a constant color.
1335     {
1336         std::ostringstream frag;
1337         frag << "#version 460\n"
1338              << "#extension GL_EXT_mesh_shader : enable\n"
1339              << "\n"
1340              << "layout (location=0) out vec4 outColor;\n"
1341              << "\n"
1342              << "void main ()\n"
1343              << "{\n"
1344              // White color should not actually be used, as those fragments are supposed to be discarded.
1345              << "    outColor = ((gl_ClipDistance[0] >= 0.0 && gl_ClipDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : "
1346                 "vec4(1.0, 1.0, 1.0, 1.0));\n"
1347              << "}\n";
1348         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
1349     }
1350 }
1351 
createInstance(Context & context) const1352 TestInstance *ClipDistanceCase::createInstance(Context &context) const
1353 {
1354     const IterationParams iterationParams = {
1355         getDefaultExtent(),       // VkExtent2D colorExtent;
1356         1u,                       // uint32_t numLayers;
1357         false,                    // bool multiview;
1358         false,                    // bool indirect;
1359         tcu::Nothing,             // tcu::Maybe<FragmentSize> fragmentSize;
1360         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1361         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1362     };
1363 
1364     // Must match the shader.
1365     const tcu::Vec4 black = getClearColor();
1366     const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1367 
1368     return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1369 }
1370 
checkSupport(Context & context) const1371 void ClipDistanceCase::checkSupport(Context &context) const
1372 {
1373     MeshShaderBuiltinCase::checkSupport(context);
1374     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CLIP_DISTANCE);
1375 }
1376 
1377 // CullDistance builtin case.
1378 class CullDistanceCase : public MeshShaderBuiltinCase
1379 {
1380 public:
CullDistanceCase(tcu::TestContext & testCtx,const std::string & name)1381     CullDistanceCase(tcu::TestContext &testCtx, const std::string &name)
1382         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1383     {
1384     }
~CullDistanceCase(void)1385     virtual ~CullDistanceCase(void)
1386     {
1387     }
1388 
1389     void initPrograms(vk::SourceCollections &programCollection) const override;
1390     TestInstance *createInstance(Context &context) const override;
1391     void checkSupport(Context &context) const override;
1392 };
1393 
initPrograms(vk::SourceCollections & programCollection) const1394 void CullDistanceCase::initPrograms(vk::SourceCollections &programCollection) const
1395 {
1396     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1397 
1398     // Mesh shader: two quads covering the whole screen, one on top of the other.
1399     // Use cull distances to discard the bottom quad.
1400     // Use cull distances to paint the top one in two colors: blue on the left, white on the right.
1401     {
1402         std::ostringstream mesh;
1403         mesh << "#version 460\n"
1404              << "#extension GL_EXT_mesh_shader : enable\n"
1405              << "\n"
1406              << "layout (local_size_x=1) in;\n"
1407              << "layout (triangles) out;\n"
1408              << "layout (max_vertices=6, max_primitives=4) out;\n"
1409              << "\n"
1410              << "out gl_MeshPerVertexEXT {\n"
1411              << "    vec4  gl_Position;\n"
1412              << "    float gl_CullDistance[2];\n"
1413              << "} gl_MeshVerticesEXT[];\n"
1414              << "\n"
1415              << "void main ()\n"
1416              << "{\n"
1417              << "    SetMeshOutputsEXT(6u, 4u);\n"
1418              << "\n"
1419              << "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 3u);\n"
1420              << "    gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 4u, 3u);\n"
1421              << "    gl_PrimitiveTriangleIndicesEXT[2] = uvec3(1u, 2u, 4u);\n"
1422              << "    gl_PrimitiveTriangleIndicesEXT[3] = uvec3(2u, 5u, 4u);\n"
1423              << "\n"
1424              << "    gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1425              << "    gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  0.0, 0.0, 1.0);\n"
1426              << "    gl_MeshVerticesEXT[2].gl_Position = vec4(-1.0,  1.0, 0.0, 1.0);\n"
1427              << "    gl_MeshVerticesEXT[3].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1428              << "    gl_MeshVerticesEXT[4].gl_Position = vec4( 1.0,  0.0, 0.0, 1.0);\n"
1429              << "    gl_MeshVerticesEXT[5].gl_Position = vec4( 1.0,  1.0, 0.0, 1.0);\n"
1430              << "\n"
1431              // The first cull plane discards the bottom quad
1432              << "    gl_MeshVerticesEXT[0].gl_CullDistance[0] =  1.0;\n"
1433              << "    gl_MeshVerticesEXT[1].gl_CullDistance[0] = -1.0;\n"
1434              << "    gl_MeshVerticesEXT[2].gl_CullDistance[0] = -2.0;\n"
1435              << "    gl_MeshVerticesEXT[3].gl_CullDistance[0] =  1.0;\n"
1436              << "    gl_MeshVerticesEXT[4].gl_CullDistance[0] = -1.0;\n"
1437              << "    gl_MeshVerticesEXT[5].gl_CullDistance[0] = -2.0;\n"
1438              << "\n"
1439              // The second cull plane helps paint left and right different.
1440              << "    gl_MeshVerticesEXT[0].gl_CullDistance[1] =  1.0;\n"
1441              << "    gl_MeshVerticesEXT[1].gl_CullDistance[1] =  1.0;\n"
1442              << "    gl_MeshVerticesEXT[2].gl_CullDistance[1] =  1.0;\n"
1443              << "    gl_MeshVerticesEXT[3].gl_CullDistance[1] = -1.0;\n"
1444              << "    gl_MeshVerticesEXT[4].gl_CullDistance[1] = -1.0;\n"
1445              << "    gl_MeshVerticesEXT[5].gl_CullDistance[1] = -1.0;\n"
1446              << "}\n";
1447         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1448     }
1449 
1450     // Fragment shader chooses color based on the second cull distance.
1451     {
1452         std::ostringstream frag;
1453         frag << "#version 460\n"
1454              << "#extension GL_EXT_mesh_shader : enable\n"
1455              << "\n"
1456              << "layout (location=0) out vec4 outColor;\n"
1457              << "\n"
1458              << "void main ()\n"
1459              << "{\n"
1460              << "    outColor = ((gl_CullDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(1.0, 1.0, 1.0, 1.0));\n"
1461              << "}\n";
1462         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
1463     }
1464 }
1465 
createInstance(Context & context) const1466 TestInstance *CullDistanceCase::createInstance(Context &context) const
1467 {
1468     const IterationParams iterationParams = {
1469         getDefaultExtent(),       // VkExtent2D colorExtent;
1470         1u,                       // uint32_t numLayers;
1471         false,                    // bool multiview;
1472         false,                    // bool indirect;
1473         tcu::Nothing,             // tcu::Maybe<FragmentSize> fragmentSize;
1474         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1475         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1476     };
1477 
1478     // Must match the shader.
1479     const tcu::Vec4 black = getClearColor();
1480     const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1481     const tcu::Vec4 white(1.0f, 1.0f, 1.0f, 1.0f);
1482 
1483     return new QuadrantsInstance(context, iterationParams, blue, white, black, black);
1484 }
1485 
checkSupport(Context & context) const1486 void CullDistanceCase::checkSupport(Context &context) const
1487 {
1488     MeshShaderBuiltinCase::checkSupport(context);
1489     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CULL_DISTANCE);
1490 }
1491 
1492 // Generates statements to draw a triangle around the given pixel number, knowing the framebuffer width (len).
1493 // Supposes the height of the framebuffer is 1.
triangleForPixel(const std::string & pixel,const std::string & len,const std::string & primitiveIndex)1494 std::string triangleForPixel(const std::string &pixel, const std::string &len, const std::string &primitiveIndex)
1495 {
1496     std::ostringstream statements;
1497     statements << "    const float imgWidth = float(" << len << ");\n"
1498                << "    const float pixWidth = (2.0 / imgWidth);\n"
1499                << "    const float halfPix  = (pixWidth / 2.0);\n"
1500                << "    const float xCenter  = (((float(" << pixel << ") + 0.5) / imgWidth) * 2.0 - 1.0);\n"
1501                << "    const float xLeft    = (xCenter - halfPix);\n"
1502                << "    const float xRight   = (xCenter + halfPix);\n"
1503                << "    const uint  vindex   = (" << primitiveIndex << " * 3u);\n"
1504                << "    const uvec3 indices  = uvec3(vindex + 0, vindex + 1, vindex + 2);\n"
1505                << "\n"
1506                << "    gl_PrimitiveTriangleIndicesEXT[" << primitiveIndex << "] = indices;\n"
1507                << "\n"
1508                << "    gl_MeshVerticesEXT[indices.x].gl_Position = vec4(xLeft,    0.5, 0.0, 1.0);\n"
1509                << "    gl_MeshVerticesEXT[indices.y].gl_Position = vec4(xRight,   0.5, 0.0, 1.0);\n"
1510                << "    gl_MeshVerticesEXT[indices.z].gl_Position = vec4(xCenter, -0.5, 0.0, 1.0);\n";
1511     return statements.str();
1512 }
1513 
1514 // WorkGroupID builtin case.
1515 class WorkGroupIdCase : public MeshShaderBuiltinCase
1516 {
1517 public:
WorkGroupIdCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)1518     WorkGroupIdCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
1519         : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1520         , m_extent(getLinearExtent())
1521     {
1522     }
~WorkGroupIdCase(void)1523     virtual ~WorkGroupIdCase(void)
1524     {
1525     }
1526 
1527     void initPrograms(vk::SourceCollections &programCollection) const override;
1528     TestInstance *createInstance(Context &context) const override;
1529 
1530 protected:
1531     const VkExtent2D m_extent;
1532 };
1533 
initPrograms(vk::SourceCollections & programCollection) const1534 void WorkGroupIdCase::initPrograms(vk::SourceCollections &programCollection) const
1535 {
1536     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1537 
1538     const std::string taskDataDecl = "struct TaskData {\n"
1539                                      "    uint id;\n"
1540                                      "    uint size;\n"
1541                                      "};\n"
1542                                      "taskPayloadSharedEXT TaskData td;\n";
1543 
1544     // Mesh shader: each work group fills one pixel.
1545     {
1546         const std::string pixel = (m_taskNeeded ? "td.id" : "gl_WorkGroupID.x");
1547         const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1548 
1549         std::ostringstream mesh;
1550         mesh << "#version 460\n"
1551              << "#extension GL_EXT_mesh_shader : enable\n"
1552              << "\n"
1553              << "layout (local_size_x=1) in;\n"
1554              << "layout (triangles) out;\n"
1555              << "layout (max_vertices=3, max_primitives=1) out;\n"
1556              << "\n"
1557              << (m_taskNeeded ? taskDataDecl : "") << "\n"
1558              << "void main ()\n"
1559              << "{\n"
1560              << "    SetMeshOutputsEXT(3u, 1u);\n"
1561              << "\n"
1562              << triangleForPixel(pixel, len, "0") << "}\n";
1563         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1564     }
1565 
1566     if (m_taskNeeded)
1567     {
1568         std::ostringstream task;
1569         task << "#version 460\n"
1570              << "#extension GL_EXT_mesh_shader : enable\n"
1571              << "\n"
1572              << "layout (local_size_x=1) in;\n"
1573              << "\n"
1574              << taskDataDecl << "\n"
1575              << "void main ()\n"
1576              << "{\n"
1577              << "    td.id          = gl_WorkGroupID.x;\n"
1578              << "    td.size        = " << m_extent.width << ";\n"
1579              << "    EmitMeshTasksEXT(1u, 1u, 1u);\n"
1580              << "}\n";
1581         programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1582     }
1583 
1584     // Basic fragment shader.
1585     {
1586         const auto frag = getBasicFragShader();
1587         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1588     }
1589 }
1590 
createInstance(Context & context) const1591 TestInstance *WorkGroupIdCase::createInstance(Context &context) const
1592 {
1593     // Must match the shader.
1594     const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1595     const IterationParams iterationParams = {
1596         m_extent,                               // VkExtent2D colorExtent;
1597         1u,                                     // uint32_t numLayers;
1598         false,                                  // bool multiview;
1599         false,                                  // bool indirect;
1600         tcu::Nothing,                           // tcu::Maybe<FragmentSize> fragmentSize;
1601         getDefaultDrawCommands(m_extent.width), // DrawCommandVec drawArgs;
1602         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1603     };
1604     return new FullScreenColorInstance(context, iterationParams, expectedColors);
1605 }
1606 
1607 // Variable to use.
1608 enum class LocalInvocation
1609 {
1610     ID = 0,
1611     INDEX
1612 };
1613 
1614 // LocalInvocationId and LocalInvocationIndex builtin cases. These are also used to test WorkGroupSize.
1615 class LocalInvocationCase : public MeshShaderBuiltinCase
1616 {
1617 public:
LocalInvocationCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded,LocalInvocation variable)1618     LocalInvocationCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded, LocalInvocation variable)
1619         : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1620         , m_extent(getLinearExtent())
1621         , m_variable(variable)
1622     {
1623     }
~LocalInvocationCase(void)1624     virtual ~LocalInvocationCase(void)
1625     {
1626     }
1627 
1628     void initPrograms(vk::SourceCollections &programCollection) const override;
1629     TestInstance *createInstance(Context &context) const override;
1630 
1631 protected:
1632     const VkExtent2D m_extent;
1633     const LocalInvocation m_variable;
1634 };
1635 
initPrograms(vk::SourceCollections & programCollection) const1636 void LocalInvocationCase::initPrograms(vk::SourceCollections &programCollection) const
1637 {
1638     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1639 
1640     // Invocation index to use.
1641     const std::string localIndex =
1642         ((m_variable == LocalInvocation::ID) ? "gl_LocalInvocationID.x" : "gl_LocalInvocationIndex");
1643 
1644     // Task data.
1645     std::ostringstream taskDataDecl;
1646     taskDataDecl << "struct TaskData {\n"
1647                  // indexNumber[x] == x
1648                  << "    uint indexNumber[" << m_extent.width << "];\n"
1649                  << "    uint size;\n"
1650                  << "};\n"
1651                  << "taskPayloadSharedEXT TaskData td;\n";
1652     const auto taskDataDeclStr = taskDataDecl.str();
1653 
1654     // Mesh shader: each work group fills one pixel.
1655     {
1656         const std::string pixel          = (m_taskNeeded ? "td.indexNumber[gl_WorkGroupID.x]" : localIndex);
1657         const std::string len            = (m_taskNeeded ? "td.size" : "gl_WorkGroupSize.x");
1658         const auto localSize             = (m_taskNeeded ? 1u : m_extent.width);
1659         const auto maxVert               = localSize * 3u;
1660         const std::string primitiveIndex = (m_taskNeeded ? "0" : localIndex);
1661 
1662         std::ostringstream mesh;
1663         mesh << "#version 460\n"
1664              << "#extension GL_EXT_mesh_shader : enable\n"
1665              << "\n"
1666              << "layout (local_size_x=" << localSize << ") in;\n"
1667              << "layout (triangles) out;\n"
1668              << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1669              << "\n"
1670              << (m_taskNeeded ? taskDataDeclStr : "") << "\n"
1671              << "void main ()\n"
1672              << "{\n"
1673              << "    SetMeshOutputsEXT(" << maxVert << ", " << localSize << ");\n"
1674              << "\n"
1675              << triangleForPixel(pixel, len, primitiveIndex) << "}\n";
1676         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1677     }
1678 
1679     if (m_taskNeeded)
1680     {
1681         std::ostringstream task;
1682         task << "#version 460\n"
1683              << "#extension GL_EXT_mesh_shader : enable\n"
1684              << "\n"
1685              << "layout (local_size_x=" << m_extent.width << ") in;\n"
1686              << "\n"
1687              << taskDataDeclStr << "\n"
1688              << "void main ()\n"
1689              << "{\n"
1690              << "    td.indexNumber[" << localIndex << "] = " << localIndex << ";\n"
1691              << "    td.size = gl_WorkGroupSize.x;\n"
1692              << "    EmitMeshTasksEXT(" << m_extent.width << ", 1u, 1u);\n"
1693              << "}\n";
1694         programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1695     }
1696 
1697     // Basic fragment shader.
1698     {
1699         const auto frag = getBasicFragShader();
1700         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1701     }
1702 }
1703 
createInstance(Context & context) const1704 TestInstance *LocalInvocationCase::createInstance(Context &context) const
1705 {
1706     // Must match the shader.
1707     const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1708     const IterationParams iterationParams = {
1709         m_extent,                 // VkExtent2D colorExtent;
1710         1u,                       // uint32_t numLayers;
1711         false,                    // bool multiview;
1712         false,                    // bool indirect;
1713         tcu::Nothing,             // tcu::Maybe<FragmentSize> fragmentSize;
1714         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1715         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1716     };
1717     return new FullScreenColorInstance(context, iterationParams, expectedColors);
1718 }
1719 
1720 // NumWorkgroups case.
toGLSL(const tcu::UVec3 & v)1721 std::string toGLSL(const tcu::UVec3 &v)
1722 {
1723     return "uvec3(" + std::to_string(v.x()) + ", " + std::to_string(v.y()) + ", " + std::to_string(v.z()) + ")";
1724 }
1725 
1726 class NumWorkgroupsCase : public MeshShaderBuiltinCase
1727 {
1728 public:
NumWorkgroupsCase(tcu::TestContext & testCtx,const std::string & name,const tcu::Maybe<tcu::UVec3> & taskGroups,const tcu::UVec3 & meshGroups)1729     NumWorkgroupsCase(tcu::TestContext &testCtx, const std::string &name, const tcu::Maybe<tcu::UVec3> &taskGroups,
1730                       const tcu::UVec3 &meshGroups)
1731         : MeshShaderBuiltinCase(testCtx, name, static_cast<bool>(taskGroups))
1732         , m_taskGroups(taskGroups)
1733         , m_meshGroups(meshGroups)
1734     {
1735     }
~NumWorkgroupsCase(void)1736     virtual ~NumWorkgroupsCase(void)
1737     {
1738     }
1739 
1740     void initPrograms(vk::SourceCollections &programCollection) const override;
1741     TestInstance *createInstance(Context &context) const override;
1742 
1743 protected:
1744     VkExtent2D imageExtent() const;
1745     tcu::UVec3 drawArgs() const;
1746 
1747     const tcu::Maybe<tcu::UVec3> m_taskGroups;
1748     const tcu::UVec3 m_meshGroups;
1749 };
1750 
imageExtent() const1751 VkExtent2D NumWorkgroupsCase::imageExtent() const
1752 {
1753     uint32_t taskMultiplier = 1u;
1754 
1755     if (m_taskNeeded)
1756     {
1757         const auto &tg = m_taskGroups.get();
1758         taskMultiplier = tg.x() * tg.y() * tg.z();
1759     }
1760 
1761     const uint32_t meshFactor = m_meshGroups.x() * m_meshGroups.y() * m_meshGroups.z();
1762     const uint32_t width      = meshFactor * taskMultiplier;
1763 
1764     return makeExtent2D(width, 1u);
1765 }
1766 
drawArgs() const1767 tcu::UVec3 NumWorkgroupsCase::drawArgs() const
1768 {
1769     if (m_taskNeeded)
1770         return m_taskGroups.get();
1771     return m_meshGroups;
1772 }
1773 
initPrograms(vk::SourceCollections & programCollection) const1774 void NumWorkgroupsCase::initPrograms(vk::SourceCollections &programCollection) const
1775 {
1776     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1777 
1778     // Task data.
1779     std::ostringstream taskDataDecl;
1780 
1781     if (m_taskNeeded)
1782     {
1783         taskDataDecl << "struct TaskData {\n"
1784                      << "    uvec3 parentId;\n"
1785                      << "    uvec3 parentSize;\n"
1786                      << "};\n"
1787                      << "taskPayloadSharedEXT TaskData td;\n";
1788     }
1789 
1790     const auto taskDataDeclStr = taskDataDecl.str();
1791     const auto extent          = imageExtent();
1792     const auto &width          = extent.width;
1793     DE_ASSERT(extent.height == 1u);
1794 
1795     // Mesh shader: each work group fills one pixel.
1796     {
1797         const std::string parentId     = (m_taskNeeded ? "td.parentId" : "uvec3(0, 0, 0)");
1798         const std::string parentSize   = (m_taskNeeded ? "td.parentSize" : "uvec3(1, 1, 1)");
1799         const std::string parentOffset = "(" + parentSize + ".x * " + parentSize + ".y * " + parentId + ".z + " +
1800                                          parentId + ".y * " + parentSize + ".x + " + parentId + ".x)";
1801         const std::string meshGroupsPerTask = std::to_string(m_meshGroups.x() * m_meshGroups.y() * m_meshGroups.z());
1802         const std::string meshGroupIndex    = "(gl_NumWorkGroups.x * gl_NumWorkGroups.y * gl_WorkGroupID.z + "
1803                                               "gl_WorkGroupID.y * gl_NumWorkGroups.x + gl_WorkGroupID.x)";
1804         const std::string pixel = "((" + parentOffset + " * " + meshGroupsPerTask + ") + " + meshGroupIndex + ")";
1805         const std::string len   = std::to_string(width);
1806 
1807         std::ostringstream mesh;
1808         mesh << "#version 460\n"
1809              << "#extension GL_EXT_mesh_shader : enable\n"
1810              << "\n"
1811              << "layout (local_size_x=1) in;\n"
1812              << "layout (triangles) out;\n"
1813              << "layout (max_vertices=3, max_primitives=1) out;\n"
1814              << "\n"
1815              << taskDataDeclStr << "\n"
1816              << "void main ()\n"
1817              << "{\n"
1818              << "    uint numVertices = 3u;\n"
1819              << "    uint numPrimitives = 1u;\n"
1820              << "    if (gl_NumWorkGroups != " << toGLSL(m_meshGroups) << ") {\n"
1821              << "        numVertices = 0u;\n"
1822              << "        numPrimitives = 0u;\n"
1823              << "    }\n"
1824              << "    SetMeshOutputsEXT(numVertices, numPrimitives);\n"
1825              << "    if (numPrimitives == 0u) {\n"
1826              << "        return;\n"
1827              << "    }\n"
1828              << "\n"
1829              << triangleForPixel(pixel, len, "0") << "}\n";
1830         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1831     }
1832 
1833     if (m_taskNeeded)
1834     {
1835         std::ostringstream task;
1836         task << "#version 460\n"
1837              << "#extension GL_EXT_mesh_shader : enable\n"
1838              << "\n"
1839              << "layout (local_size_x=1) in;\n"
1840              << "\n"
1841              << taskDataDeclStr << "\n"
1842              << "void main ()\n"
1843              << "{\n"
1844              << "    uvec3 meshGroups = " << toGLSL(m_meshGroups) << ";\n"
1845              << "    if (gl_NumWorkGroups != " << toGLSL(m_taskGroups.get()) << ") {\n"
1846              << "        meshGroups = uvec3(0, 0, 0);\n"
1847              << "    }\n"
1848              << "    td.parentSize = gl_NumWorkGroups;\n"
1849              << "    td.parentId   = gl_WorkGroupID;\n"
1850              << "    EmitMeshTasksEXT(meshGroups.x, meshGroups.y, meshGroups.z);\n"
1851              << "}\n";
1852         programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1853     }
1854 
1855     // Basic fragment shader.
1856     {
1857         const auto frag = getBasicFragShader();
1858         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1859     }
1860 }
1861 
createInstance(Context & context) const1862 TestInstance *NumWorkgroupsCase::createInstance(Context &context) const
1863 {
1864     // Must match the shader.
1865     const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1866     const auto extent      = imageExtent();
1867     const auto drawCmdArgs = drawArgs();
1868     const DrawCommandVec drawCommands(
1869         1u, makeDrawMeshTasksIndirectCommandEXT(drawCmdArgs.x(), drawCmdArgs.y(), drawCmdArgs.z()));
1870     const IterationParams iterationParams = {
1871         extent,       // VkExtent2D colorExtent;
1872         1u,           // uint32_t numLayers;
1873         false,        // bool multiview;
1874         false,        // bool indirect;
1875         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1876         drawCommands, // DrawCommandVec drawArgs;
1877         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1878     };
1879     return new FullScreenColorInstance(context, iterationParams, expectedColors);
1880 }
1881 
1882 // GlobalInvocationId builtin case.
1883 class GlobalInvocationIdCase : public MeshShaderBuiltinCase
1884 {
1885 public:
GlobalInvocationIdCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)1886     GlobalInvocationIdCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
1887         : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1888         , m_jobSize(getLargeJobSize())
1889         , m_extent{m_jobSize.numTasks * m_jobSize.localSize, 1u}
1890     {
1891     }
~GlobalInvocationIdCase(void)1892     virtual ~GlobalInvocationIdCase(void)
1893     {
1894     }
1895 
1896     void initPrograms(vk::SourceCollections &programCollection) const override;
1897     TestInstance *createInstance(Context &context) const override;
1898 
1899 protected:
1900     const JobSize m_jobSize;
1901     const VkExtent2D m_extent;
1902 };
1903 
initPrograms(vk::SourceCollections & programCollection) const1904 void GlobalInvocationIdCase::initPrograms(vk::SourceCollections &programCollection) const
1905 {
1906     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1907     const auto &localSize   = m_jobSize.localSize;
1908 
1909     // Task data.
1910     std::ostringstream taskDataDecl;
1911     taskDataDecl << "struct TaskData {\n"
1912                  << "    uint pixelId[" << localSize << "];\n"
1913                  << "    uint size;\n"
1914                  << "};\n"
1915                  << "taskPayloadSharedEXT TaskData td;\n";
1916     const auto taskDataDeclStr = taskDataDecl.str();
1917 
1918     // Mesh shader: each work group fills one pixel.
1919     {
1920         const std::string pixel = (m_taskNeeded ? "td.pixelId[gl_LocalInvocationIndex]" : "gl_GlobalInvocationID.x");
1921         const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1922         const std::string primitiveIndex = "gl_LocalInvocationIndex";
1923         const auto maxVert               = localSize * 3u;
1924 
1925         std::ostringstream mesh;
1926         mesh << "#version 460\n"
1927              << "#extension GL_EXT_mesh_shader : enable\n"
1928              << "\n"
1929              << "layout (local_size_x=" << localSize << ") in;\n"
1930              << "layout (triangles) out;\n"
1931              << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1932              << "\n"
1933              << (m_taskNeeded ? taskDataDeclStr : "") << "\n"
1934              << "void main ()\n"
1935              << "{\n"
1936              << "    SetMeshOutputsEXT(" << maxVert << ", " << localSize << ");\n"
1937              << "\n"
1938              << triangleForPixel(pixel, len, primitiveIndex) << "}\n";
1939         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1940     }
1941 
1942     if (m_taskNeeded)
1943     {
1944         std::ostringstream task;
1945         task << "#version 460\n"
1946              << "#extension GL_EXT_mesh_shader : enable\n"
1947              << "\n"
1948              << "layout (local_size_x=" << localSize << ") in;\n"
1949              << "\n"
1950              << taskDataDeclStr << "\n"
1951              << "void main ()\n"
1952              << "{\n"
1953              << "    td.pixelId[gl_LocalInvocationIndex] = gl_GlobalInvocationID.x;\n"
1954              << "    td.size = " << m_extent.width << ";\n"
1955              << "    EmitMeshTasksEXT(1u, 1u, 1u);\n"
1956              << "}\n";
1957         programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1958     }
1959 
1960     // Basic fragment shader.
1961     {
1962         const auto frag = getBasicFragShader();
1963         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1964     }
1965 }
1966 
createInstance(Context & context) const1967 TestInstance *GlobalInvocationIdCase::createInstance(Context &context) const
1968 {
1969     // Must match the shader.
1970     const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1971     const IterationParams iterationParams = {
1972         m_extent,                                   // VkExtent2D colorExtent;
1973         1u,                                         // uint32_t numLayers;
1974         false,                                      // bool multiview;
1975         false,                                      // bool indirect;
1976         tcu::Nothing,                               // tcu::Maybe<FragmentSize> fragmentSize;
1977         getDefaultDrawCommands(m_jobSize.numTasks), // DrawCommandVec drawArgs;
1978         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1979     };
1980     return new FullScreenColorInstance(context, iterationParams, expectedColors);
1981 }
1982 
1983 // DrawIndex builtin case.
1984 class DrawIndexCase : public MeshShaderBuiltinCase
1985 {
1986 public:
DrawIndexCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)1987     DrawIndexCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
1988         : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1989         , m_extent(getLinearExtent())
1990     {
1991     }
~DrawIndexCase(void)1992     virtual ~DrawIndexCase(void)
1993     {
1994     }
1995 
1996     void initPrograms(vk::SourceCollections &programCollection) const override;
1997     TestInstance *createInstance(Context &context) const override;
1998 
1999 protected:
2000     const VkExtent2D m_extent;
2001 };
2002 
initPrograms(vk::SourceCollections & programCollection) const2003 void DrawIndexCase::initPrograms(vk::SourceCollections &programCollection) const
2004 {
2005     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2006 
2007     const std::string taskDataDecl = "struct TaskData {\n"
2008                                      "    uint id;\n"
2009                                      "    uint size;\n"
2010                                      "};\n"
2011                                      "taskPayloadSharedEXT TaskData td;\n";
2012 
2013     const auto drawIndex = "uint(gl_DrawID)";
2014 
2015     // Mesh shader: each work group fills one pixel.
2016     {
2017         const std::string pixel = (m_taskNeeded ? "td.id" : drawIndex);
2018         const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
2019 
2020         std::ostringstream mesh;
2021         mesh << "#version 460\n"
2022              << "#extension GL_EXT_mesh_shader : enable\n"
2023              << "\n"
2024              << "layout (local_size_x=1) in;\n"
2025              << "layout (triangles) out;\n"
2026              << "layout (max_vertices=3, max_primitives=1) out;\n"
2027              << "\n"
2028              << (m_taskNeeded ? taskDataDecl : "") << "\n"
2029              << "void main ()\n"
2030              << "{\n"
2031              << "    SetMeshOutputsEXT(3u, 1u);\n"
2032              << "\n"
2033              << triangleForPixel(pixel, len, "0") << "}\n";
2034         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2035     }
2036 
2037     if (m_taskNeeded)
2038     {
2039         std::ostringstream task;
2040         task << "#version 460\n"
2041              << "#extension GL_EXT_mesh_shader : enable\n"
2042              << "\n"
2043              << "layout (local_size_x=1) in;\n"
2044              << "\n"
2045              << taskDataDecl << "\n"
2046              << "void main ()\n"
2047              << "{\n"
2048              << "    td.id          = " << drawIndex << ";\n"
2049              << "    td.size        = " << m_extent.width << ";\n"
2050              << "    EmitMeshTasksEXT(1u, 1u, 1u);\n"
2051              << "}\n";
2052         programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
2053     }
2054 
2055     // Basic fragment shader.
2056     {
2057         const auto frag = getBasicFragShader();
2058         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
2059     }
2060 }
2061 
createInstance(Context & context) const2062 TestInstance *DrawIndexCase::createInstance(Context &context) const
2063 {
2064     // Must match the shader.
2065     const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
2066     const DrawCommandVec commands(m_extent.width, makeDrawMeshTasksIndirectCommandEXT(1u, 1u, 1u));
2067     const IterationParams iterationParams = {
2068         m_extent,     // VkExtent2D colorExtent;
2069         1u,           // uint32_t numLayers;
2070         false,        // bool multiview;
2071         true,         // bool indirect;
2072         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
2073         commands,     // DrawCommandVec drawArgs;
2074         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
2075     };
2076     return new FullScreenColorInstance(context, iterationParams, expectedColors);
2077 }
2078 
2079 // ViewIndex builtin case.
2080 class ViewIndexCase : public MeshShaderBuiltinCase
2081 {
2082 public:
ViewIndexCase(tcu::TestContext & testCtx,const std::string & name)2083     ViewIndexCase(tcu::TestContext &testCtx, const std::string &name)
2084         : MeshShaderBuiltinCase(testCtx, name, false)
2085         , m_extent(getDefaultExtent())
2086     {
2087     }
~ViewIndexCase(void)2088     virtual ~ViewIndexCase(void)
2089     {
2090     }
2091 
2092     void checkSupport(Context &context) const override;
2093     void initPrograms(vk::SourceCollections &programCollection) const override;
2094     TestInstance *createInstance(Context &context) const override;
2095 
2096     static constexpr uint32_t kNumLayers = 4u;
2097 
2098 protected:
2099     const VkExtent2D m_extent;
2100 };
2101 
checkSupport(Context & context) const2102 void ViewIndexCase::checkSupport(Context &context) const
2103 {
2104     MeshShaderBuiltinCase::checkSupport(context);
2105 
2106     const auto &multiviewFeatures = context.getMultiviewFeatures();
2107     if (!multiviewFeatures.multiview)
2108         TCU_THROW(NotSupportedError, "Multiview not supported");
2109 
2110     const auto &meshFeatures = context.getMeshShaderFeaturesEXT();
2111     if (!meshFeatures.multiviewMeshShader)
2112         TCU_THROW(NotSupportedError, "Multiview not supported for mesh shaders");
2113 
2114     const auto &meshProperties = context.getMeshShaderPropertiesEXT();
2115     if (kNumLayers > meshProperties.maxMeshMultiviewViewCount)
2116     {
2117         std::ostringstream msg;
2118         msg << "maxMeshMultiviewViewCount too low: " << meshProperties.maxMeshMultiviewViewCount
2119             << " and the test needs " << kNumLayers;
2120         TCU_THROW(NotSupportedError, msg.str());
2121     }
2122 }
2123 
initPrograms(vk::SourceCollections & programCollection) const2124 void ViewIndexCase::initPrograms(vk::SourceCollections &programCollection) const
2125 {
2126     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2127 
2128     DE_ASSERT(!m_taskNeeded);
2129 
2130     // Mesh shader: choose output color depending on the view index.
2131     {
2132         std::ostringstream mesh;
2133         mesh << "#version 460\n"
2134              << "#extension GL_EXT_mesh_shader : enable\n"
2135              << "#extension GL_EXT_multiview : enable\n"
2136              << "\n"
2137              << "layout (local_size_x=1) in;\n"
2138              << "layout (triangles) out;\n"
2139              << "layout (max_vertices=3, max_primitives=1) out;\n"
2140              << "\n"
2141              << "vec4 colors[" << kNumLayers << "] = vec4[](\n"
2142              << "    vec4(0.0, 0.0, 1.0, 1.0),\n"
2143              << "    vec4(1.0, 0.0, 1.0, 1.0),\n"
2144              << "    vec4(0.0, 1.0, 1.0, 1.0),\n"
2145              << "    vec4(1.0, 1.0, 0.0, 1.0)\n"
2146              << ");\n"
2147              << "\n"
2148              << "layout (location=0) perprimitiveEXT out vec4 primitiveColor[];\n"
2149              << "\n"
2150              << "void main ()\n"
2151              << "{\n"
2152              << "    SetMeshOutputsEXT(3u, 1u);\n"
2153              << "\n"
2154              << "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
2155              << "    primitiveColor[0] = colors[gl_ViewIndex];\n"
2156              << "\n"
2157              << "    gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
2158              << "    gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
2159              << "    gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
2160              << "}\n";
2161         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2162     }
2163 
2164     // Fragment shader writes its output using the primitive color from the mesh shader.
2165     {
2166         std::ostringstream frag;
2167         frag << "#version 460\n"
2168              << "#extension GL_EXT_mesh_shader : enable\n"
2169              << "#extension GL_EXT_multiview : enable\n"
2170              << "\n"
2171              << "layout (location=0) perprimitiveEXT in vec4 primitiveColor;\n"
2172              << "layout (location=0) out vec4 outColor;\n"
2173              << "\n"
2174              << "void main ()\n"
2175              << "{\n"
2176              << "    outColor = primitiveColor;\n"
2177              << "}\n";
2178         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
2179     }
2180 }
2181 
createInstance(Context & context) const2182 TestInstance *ViewIndexCase::createInstance(Context &context) const
2183 {
2184     // Must match the shader.
2185     ColorVec expectedColors;
2186 
2187     expectedColors.reserve(kNumLayers);
2188     expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0));
2189     expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0));
2190     expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0));
2191     expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0));
2192 
2193     const IterationParams iterationParams = {
2194         getDefaultExtent(),       // VkExtent2D colorExtent;
2195         kNumLayers,               // uint32_t numLayers;
2196         true,                     // bool multiview;
2197         false,                    // bool indirect;
2198         tcu::Nothing,             // tcu::Maybe<FragmentSize> fragmentSize;
2199         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
2200         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
2201     };
2202     return new FullScreenColorInstance(context, iterationParams, expectedColors);
2203 }
2204 
2205 // Primitive Shading Rate case.
2206 class PrimitiveShadingRateCase : public MeshShaderBuiltinCase
2207 {
2208 public:
PrimitiveShadingRateCase(tcu::TestContext & testCtx,const std::string & name,FragmentSize topSize,FragmentSize bottomSize)2209     PrimitiveShadingRateCase(tcu::TestContext &testCtx, const std::string &name, FragmentSize topSize,
2210                              FragmentSize bottomSize)
2211         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
2212         , m_topSize(topSize)
2213         , m_bottomSize(bottomSize)
2214     {
2215     }
~PrimitiveShadingRateCase(void)2216     virtual ~PrimitiveShadingRateCase(void)
2217     {
2218     }
2219 
2220     void initPrograms(vk::SourceCollections &programCollection) const override;
2221     void checkSupport(Context &context) const override;
2222     TestInstance *createInstance(Context &context) const override;
2223 
2224 protected:
2225     const FragmentSize m_topSize;
2226     const FragmentSize m_bottomSize;
2227 };
2228 
initPrograms(vk::SourceCollections & programCollection) const2229 void PrimitiveShadingRateCase::initPrograms(vk::SourceCollections &programCollection) const
2230 {
2231     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2232 
2233     // Shading rate masks to use.
2234     const auto topMask    = getGLSLShadingRateMask(m_topSize);
2235     const auto bottomMask = getGLSLShadingRateMask(m_bottomSize);
2236 
2237     // Mesh shader.
2238     {
2239         std::ostringstream mesh;
2240         mesh << "#version 460\n"
2241              << "#extension GL_EXT_mesh_shader : enable\n"
2242              << "#extension GL_EXT_fragment_shading_rate : enable\n"
2243              << "\n"
2244              << "layout (local_size_x=1) in;\n"
2245              << "layout (triangles) out;\n"
2246              << "layout (max_vertices=6, max_primitives=4) out;\n"
2247              << "\n"
2248              << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
2249              << "   int gl_PrimitiveShadingRateEXT;\n"
2250              << "} gl_MeshPrimitivesEXT[];\n"
2251              << "\n"
2252              << "void main ()\n"
2253              << "{\n"
2254              << "    SetMeshOutputsEXT(6u, 4u);\n"
2255              << "\n"
2256              << "    const vec4 topLeft  = vec4(-1.0, -1.0, 0.0, 1.0);\n"
2257              << "    const vec4 midLeft  = vec4(-1.0,  0.0, 0.0, 1.0);\n"
2258              << "    const vec4 botLeft  = vec4(-1.0,  1.0, 0.0, 1.0);\n"
2259              << "\n"
2260              << "    const vec4 topRight = vec4( 1.0, -1.0, 0.0, 1.0);\n"
2261              << "    const vec4 midRight = vec4( 1.0,  0.0, 0.0, 1.0);\n"
2262              << "    const vec4 botRight = vec4( 1.0,  1.0, 0.0, 1.0);\n"
2263              << "\n"
2264              << "    gl_MeshVerticesEXT[0].gl_Position = topLeft;\n"
2265              << "    gl_MeshVerticesEXT[1].gl_Position = midLeft;\n"
2266              << "    gl_MeshVerticesEXT[2].gl_Position = botLeft;\n"
2267              << "\n"
2268              << "    gl_MeshVerticesEXT[3].gl_Position = topRight;\n"
2269              << "    gl_MeshVerticesEXT[4].gl_Position = midRight;\n"
2270              << "    gl_MeshVerticesEXT[5].gl_Position = botRight;\n"
2271              << "\n"
2272              << "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 3u);\n"
2273              << "    gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 4u, 3u);\n"
2274              << "    gl_PrimitiveTriangleIndicesEXT[2] = uvec3(1u, 2u, 4u);\n"
2275              << "    gl_PrimitiveTriangleIndicesEXT[3] = uvec3(2u, 5u, 4u);\n"
2276              << "\n"
2277              << "    gl_MeshPrimitivesEXT[0].gl_PrimitiveShadingRateEXT = " << topMask << ";\n"
2278              << "    gl_MeshPrimitivesEXT[1].gl_PrimitiveShadingRateEXT = " << topMask << ";\n"
2279              << "    gl_MeshPrimitivesEXT[2].gl_PrimitiveShadingRateEXT = " << bottomMask << ";\n"
2280              << "    gl_MeshPrimitivesEXT[3].gl_PrimitiveShadingRateEXT = " << bottomMask << ";\n"
2281              << "}\n";
2282         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2283     }
2284 
2285     // Frag shader.
2286     {
2287         const auto extent     = getDefaultExtent();
2288         const auto halfHeight = static_cast<float>(extent.height) / 2.0f;
2289 
2290         std::ostringstream frag;
2291         frag << "#version 460\n"
2292              << "#extension GL_EXT_mesh_shader : enable\n"
2293              << "#extension GL_EXT_fragment_shading_rate : enable\n"
2294              << "\n"
2295              << "layout (location=0) out vec4 outColor;\n"
2296              << "\n"
2297              << "void main ()\n"
2298              << "{\n"
2299              // Checks the shading rate matches.
2300              << "    const int expectedRate = ((gl_FragCoord.y < " << halfHeight << ")? " << topMask << " : "
2301              << bottomMask << ");\n"
2302              << "    outColor = ((gl_ShadingRateEXT == expectedRate) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, "
2303                 "1.0));\n"
2304              << "}\n";
2305         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
2306     }
2307 }
2308 
checkSupport(Context & context) const2309 void PrimitiveShadingRateCase::checkSupport(Context &context) const
2310 {
2311     MeshShaderBuiltinCase::checkSupport(context);
2312 
2313     context.requireDeviceFunctionality("VK_KHR_fragment_shading_rate");
2314 
2315     const auto &meshShaderFeatures = context.getMeshShaderFeaturesEXT();
2316     if (!meshShaderFeatures.primitiveFragmentShadingRateMeshShader)
2317         TCU_THROW(NotSupportedError, "Primitive fragment shading rate not supported in mesh shaders");
2318 }
2319 
createInstance(Context & context) const2320 TestInstance *PrimitiveShadingRateCase::createInstance(Context &context) const
2321 {
2322     const ColorVec expectedColors(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
2323     FragmentSizeVector fsInUse{m_topSize, m_bottomSize};
2324     const IterationParams iterationParams = {
2325         getDefaultExtent(),                                             // VkExtent2D colorExtent;
2326         1u,                                                             // uint32_t numLayers;
2327         false,                                                          // bool multiview;
2328         false,                                                          // bool indirect;
2329         tcu::just(getBadShadingRateSize(begin(fsInUse), end(fsInUse))), // tcu::Maybe<FragmentSize> fragmentSize;
2330         getDefaultDrawCommands(),                                       // DrawCommandVec drawArgs;
2331         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
2332     };
2333     return new FullScreenColorInstance(context, iterationParams, expectedColors);
2334 }
2335 
2336 // Cull Primitives case.
2337 class CullPrimitivesCase : public MeshShaderBuiltinCase
2338 {
2339 public:
CullPrimitivesCase(tcu::TestContext & testCtx,const std::string & name)2340     CullPrimitivesCase(tcu::TestContext &testCtx, const std::string &name)
2341         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
2342     {
2343     }
~CullPrimitivesCase(void)2344     virtual ~CullPrimitivesCase(void)
2345     {
2346     }
2347 
2348     void initPrograms(vk::SourceCollections &programCollection) const override;
2349     TestInstance *createInstance(Context &context) const override;
2350 };
2351 
initPrograms(vk::SourceCollections & programCollection) const2352 void CullPrimitivesCase::initPrograms(vk::SourceCollections &programCollection) const
2353 {
2354     const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2355 
2356     // Mesh shader.
2357     {
2358         std::ostringstream mesh;
2359         mesh << "#version 460\n"
2360              << "#extension GL_EXT_mesh_shader : enable\n"
2361              << "\n"
2362              << "layout (local_size_x=1) in;\n"
2363              << "layout (triangles) out;\n"
2364              << "layout (max_vertices=6, max_primitives=4) out;\n"
2365              << "\n"
2366              << "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
2367              << "   bool gl_CullPrimitiveEXT;\n"
2368              << "} gl_MeshPrimitivesEXT[];\n"
2369              << "\n"
2370              << "void main ()\n"
2371              << "{\n"
2372              << "    SetMeshOutputsEXT(6u, 4u);\n"
2373              << "\n"
2374              << "    const vec4 topLeft  = vec4(-1.0, -1.0, 0.0, 1.0);\n"
2375              << "    const vec4 midLeft  = vec4(-1.0,  0.0, 0.0, 1.0);\n"
2376              << "    const vec4 botLeft  = vec4(-1.0,  1.0, 0.0, 1.0);\n"
2377              << "\n"
2378              << "    const vec4 topRight = vec4( 1.0, -1.0, 0.0, 1.0);\n"
2379              << "    const vec4 midRight = vec4( 1.0,  0.0, 0.0, 1.0);\n"
2380              << "    const vec4 botRight = vec4( 1.0,  1.0, 0.0, 1.0);\n"
2381              << "\n"
2382              << "    gl_MeshVerticesEXT[0].gl_Position = topLeft;\n"
2383              << "    gl_MeshVerticesEXT[1].gl_Position = midLeft;\n"
2384              << "    gl_MeshVerticesEXT[2].gl_Position = botLeft;\n"
2385              << "\n"
2386              << "    gl_MeshVerticesEXT[3].gl_Position = topRight;\n"
2387              << "    gl_MeshVerticesEXT[4].gl_Position = midRight;\n"
2388              << "    gl_MeshVerticesEXT[5].gl_Position = botRight;\n"
2389              << "\n"
2390              << "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 3u);\n"
2391              << "    gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 4u, 3u);\n"
2392              << "    gl_PrimitiveTriangleIndicesEXT[2] = uvec3(1u, 2u, 4u);\n"
2393              << "    gl_PrimitiveTriangleIndicesEXT[3] = uvec3(2u, 5u, 4u);\n"
2394              << "\n"
2395              << "    gl_MeshPrimitivesEXT[0].gl_CullPrimitiveEXT = false;\n"
2396              << "    gl_MeshPrimitivesEXT[1].gl_CullPrimitiveEXT = false;\n"
2397              << "    gl_MeshPrimitivesEXT[2].gl_CullPrimitiveEXT = true;\n"
2398              << "    gl_MeshPrimitivesEXT[3].gl_CullPrimitiveEXT = true;\n"
2399              << "}\n";
2400         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2401     }
2402 
2403     // Frag shader.
2404     programCollection.glslSources.add("frag") << glu::FragmentSource(getBasicFragShader());
2405 }
2406 
createInstance(Context & context) const2407 TestInstance *CullPrimitivesCase::createInstance(Context &context) const
2408 {
2409     const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
2410     const tcu::Vec4 black = getClearColor();
2411 
2412     const IterationParams iterationParams = {
2413         getDefaultExtent(),       // VkExtent2D colorExtent;
2414         1u,                       // uint32_t numLayers;
2415         false,                    // bool multiview;
2416         false,                    // bool indirect;
2417         tcu::Nothing,             // tcu::Maybe<FragmentSize> fragmentSize;
2418         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
2419         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
2420     };
2421     return new QuadrantsInstance(context, iterationParams, blue, blue, black, black);
2422 }
2423 
2424 } // namespace
2425 
createMeshShaderBuiltinTestsEXT(tcu::TestContext & testCtx)2426 tcu::TestCaseGroup *createMeshShaderBuiltinTestsEXT(tcu::TestContext &testCtx)
2427 {
2428     GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "builtin"));
2429 
2430     mainGroup->addChild(new PositionCase(testCtx, "position"));
2431     mainGroup->addChild(new PointSizeCase(testCtx, "point_size"));
2432     mainGroup->addChild(new ClipDistanceCase(testCtx, "clip_distance"));
2433     mainGroup->addChild(new CullDistanceCase(testCtx, "cull_distance"));
2434     mainGroup->addChild(new PrimitiveIdCase(testCtx, "primitive_id_glsl", true /*glslFrag*/));
2435     mainGroup->addChild(new PrimitiveIdCase(testCtx, "primitive_id_spirv", false /*glslFrag*/));
2436     mainGroup->addChild(new LayerCase(testCtx, "layer", true /*writeval*/, false /*shareVertices*/));
2437     mainGroup->addChild(new LayerCase(testCtx, "layer_shared", true /*writeval*/, true /*shareVertices*/));
2438     mainGroup->addChild(new LayerCase(testCtx, "layer_no_write", false /*writeval*/, false /*shareVertices*/));
2439     mainGroup->addChild(new ViewportIndexCase(testCtx, "viewport_index", true /*writeVal*/, false /*shareVertices*/));
2440     mainGroup->addChild(
2441         new ViewportIndexCase(testCtx, "viewport_index_shared", true /*writeVal*/, true /*shareVertices*/));
2442     mainGroup->addChild(
2443         new ViewportIndexCase(testCtx, "viewport_index_no_write", false /*writeVal*/, false /*shareVertices*/));
2444     mainGroup->addChild(new WorkGroupIdCase(testCtx, "work_group_id_in_mesh", false /*taskNeeded*/));
2445     mainGroup->addChild(new WorkGroupIdCase(testCtx, "work_group_id_in_task", true /*taskNeeded*/));
2446     mainGroup->addChild(new NumWorkgroupsCase(testCtx, "num_work_groups_mesh", tcu::Nothing, tcu::UVec3(5u, 6u, 7u)));
2447     mainGroup->addChild(new NumWorkgroupsCase(testCtx, "num_work_groups_task_and_mesh",
2448                                               tcu::just(tcu::UVec3(2u, 3u, 4u)), tcu::UVec3(3u, 4u, 2u)));
2449     mainGroup->addChild(
2450         new LocalInvocationCase(testCtx, "local_invocation_id_in_mesh", false /*taskNeeded*/, LocalInvocation::ID));
2451     mainGroup->addChild(
2452         new LocalInvocationCase(testCtx, "local_invocation_id_in_task", true /*taskNeeded*/, LocalInvocation::ID));
2453     mainGroup->addChild(new LocalInvocationCase(testCtx, "local_invocation_index_in_task", true /*taskNeeded*/,
2454                                                 LocalInvocation::INDEX));
2455     mainGroup->addChild(new LocalInvocationCase(testCtx, "local_invocation_index_in_mesh", false /*taskNeeded*/,
2456                                                 LocalInvocation::INDEX));
2457     mainGroup->addChild(new GlobalInvocationIdCase(testCtx, "global_invocation_id_in_mesh", false /*taskNeeded*/));
2458     mainGroup->addChild(new GlobalInvocationIdCase(testCtx, "global_invocation_id_in_task", true /*taskNeeded*/));
2459     mainGroup->addChild(new DrawIndexCase(testCtx, "draw_index_in_mesh", false /*taskNeeded*/));
2460     mainGroup->addChild(new DrawIndexCase(testCtx, "draw_index_in_task", true /*taskNeeded*/));
2461     mainGroup->addChild(new ViewIndexCase(testCtx, "view_index"));
2462     mainGroup->addChild(new CullPrimitivesCase(testCtx, "cull_primitives"));
2463 
2464     // Primitive shading rate tests.
2465     {
2466         const auto sizeCount = static_cast<int>(FragmentSize::SIZE_COUNT);
2467 
2468         for (int i = 0; i < sizeCount; ++i)
2469             for (int j = 0; j < sizeCount; ++j)
2470             {
2471                 const auto topSize    = static_cast<FragmentSize>(i);
2472                 const auto bottomSize = static_cast<FragmentSize>(j);
2473 
2474                 const auto topExtent    = getShadingRateSize(topSize);
2475                 const auto bottomExtent = getShadingRateSize(bottomSize);
2476 
2477                 const auto testName = "primitive_shading_rate_" + std::to_string(topExtent.width) + "x" +
2478                                       std::to_string(topExtent.height) + "_" + std::to_string(bottomExtent.width) +
2479                                       "x" + std::to_string(bottomExtent.height);
2480 
2481                 mainGroup->addChild(new PrimitiveShadingRateCase(testCtx, testName, topSize, bottomSize));
2482             }
2483     }
2484 
2485     return mainGroup.release();
2486 }
2487 
2488 } // namespace MeshShader
2489 } // namespace vkt
2490