xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/mesh_shader/vktMeshShaderBuiltinTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktMeshShaderBuiltinTests.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<VkDrawMeshTasksIndirectCommandNV>;
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 
getDefaultExtent()100 VkExtent2D getDefaultExtent()
101 {
102     return makeExtent2D(8u, 8u);
103 }
104 
getLinearExtent()105 VkExtent2D getLinearExtent()
106 {
107     return makeExtent2D(8u, 1u);
108 }
109 
110 struct JobSize
111 {
112     uint32_t numTasks;
113     uint32_t localSize;
114 };
115 
getLargeJobSize()116 JobSize getLargeJobSize()
117 {
118     return JobSize{8u, 8u};
119 }
120 
121 // Single draw command with the given number of tasks, 1 by default.
getDefaultDrawCommands(uint32_t taskCount=1u)122 DrawCommandVec getDefaultDrawCommands(uint32_t taskCount = 1u)
123 {
124     return DrawCommandVec(1u, makeDrawMeshTasksIndirectCommandNV(taskCount, 0u));
125 }
126 
127 // Basic fragment shader that draws fragments in blue.
getBasicFragShader()128 std::string getBasicFragShader()
129 {
130     return "#version 460\n"
131            "#extension GL_NV_mesh_shader : enable\n"
132            "\n"
133            "layout (location=0) out vec4 outColor;\n"
134            "\n"
135            "void main ()\n"
136            "{\n"
137            "    outColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
138            "}\n";
139 }
140 
141 struct IterationParams
142 {
143     VkExtent2D colorExtent;
144     uint32_t numLayers;
145     DrawCommandVec drawArgs;
146     bool indirect;
147     ViewportVec viewports; // If empty, a single default viewport is used.
148     tcu::Maybe<FragmentSize> fragmentSize;
149 };
150 
151 class MeshShaderBuiltinInstance : public vkt::TestInstance
152 {
153 public:
MeshShaderBuiltinInstance(Context & context,const IterationParams & params)154     MeshShaderBuiltinInstance(Context &context, const IterationParams &params)
155         : vkt::TestInstance(context)
156         , m_params(params)
157     {
158     }
~MeshShaderBuiltinInstance(void)159     virtual ~MeshShaderBuiltinInstance(void)
160     {
161     }
162 
163     tcu::TestStatus iterate() override;
164     virtual void verifyResults(const tcu::ConstPixelBufferAccess &result) = 0;
165 
166 protected:
167     IterationParams m_params;
168 };
169 
iterate()170 tcu::TestStatus MeshShaderBuiltinInstance::iterate()
171 {
172     const auto &vkd       = m_context.getDeviceInterface();
173     const auto device     = m_context.getDevice();
174     auto &alloc           = m_context.getDefaultAllocator();
175     const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
176     const auto queue      = m_context.getUniversalQueue();
177     const auto &binaries  = m_context.getBinaryCollection();
178 
179     const auto useTask    = binaries.contains("task");
180     const auto useFrag    = binaries.contains("frag");
181     const auto extent     = makeExtent3D(m_params.colorExtent.width, m_params.colorExtent.height, 1u);
182     const auto iExtent3D  = tcu::IVec3(static_cast<int>(extent.width), static_cast<int>(extent.height),
183                                        static_cast<int>(m_params.numLayers));
184     const auto format     = VK_FORMAT_R8G8B8A8_UNORM;
185     const auto tcuFormat  = mapVkFormat(format);
186     const auto colorUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
187     const auto viewType   = ((m_params.numLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D);
188     const auto colorSRR   = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers);
189     const auto colorSRL   = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers);
190     const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
191 
192     ImageWithMemoryPtr colorBuffer;
193     Move<VkImageView> colorBufferView;
194     {
195         const VkImageCreateInfo colorBufferInfo = {
196             VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
197             nullptr,                             // const void* pNext;
198             0u,                                  // VkImageCreateFlags flags;
199             VK_IMAGE_TYPE_2D,                    // VkImageType imageType;
200             format,                              // VkFormat format;
201             extent,                              // VkExtent3D extent;
202             1u,                                  // uint32_t mipLevels;
203             m_params.numLayers,                  // uint32_t arrayLayers;
204             VK_SAMPLE_COUNT_1_BIT,               // VkSampleCountFlagBits samples;
205             VK_IMAGE_TILING_OPTIMAL,             // VkImageTiling tiling;
206             colorUsage,                          // VkImageUsageFlags usage;
207             VK_SHARING_MODE_EXCLUSIVE,           // VkSharingMode sharingMode;
208             0u,                                  // uint32_t queueFamilyIndexCount;
209             nullptr,                             // const uint32_t* pQueueFamilyIndices;
210             VK_IMAGE_LAYOUT_UNDEFINED,           // VkImageLayout initialLayout;
211         };
212         colorBuffer =
213             ImageWithMemoryPtr(new ImageWithMemory(vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any));
214         colorBufferView = makeImageView(vkd, device, colorBuffer->get(), viewType, format, colorSRR);
215     }
216 
217     // Empty descriptor set layout.
218     DescriptorSetLayoutBuilder layoutBuilder;
219     const auto setLayout = layoutBuilder.build(vkd, device);
220 
221     // Pipeline layout.
222     const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
223 
224     // Render pass and framebuffer.
225     const auto renderPass  = makeRenderPass(vkd, device, format);
226     const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorBufferView.get(), extent.width,
227                                              extent.height, m_params.numLayers);
228 
229     // Pipeline.
230     Move<VkShaderModule> taskModule;
231     Move<VkShaderModule> meshModule;
232     Move<VkShaderModule> fragModule;
233 
234     if (useTask)
235         taskModule = createShaderModule(vkd, device, binaries.get("task"));
236     if (useFrag)
237         fragModule = createShaderModule(vkd, device, binaries.get("frag"));
238     meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
239 
240     std::vector<VkViewport> viewports;
241     std::vector<VkRect2D> scissors;
242     if (m_params.viewports.empty())
243     {
244         // Default ones.
245         viewports.push_back(makeViewport(extent));
246         scissors.push_back(makeRect2D(extent));
247     }
248     else
249     {
250         // The desired viewports and the same number of default scissors.
251         viewports.reserve(m_params.viewports.size());
252         std::copy(begin(m_params.viewports), end(m_params.viewports), std::back_inserter(viewports));
253         scissors.resize(viewports.size(), makeRect2D(extent));
254     }
255 
256     using ShadingRateInfoPtr = de::MovePtr<VkPipelineFragmentShadingRateStateCreateInfoKHR>;
257     ShadingRateInfoPtr pNext;
258     if (static_cast<bool>(m_params.fragmentSize))
259     {
260         pNext  = ShadingRateInfoPtr(new VkPipelineFragmentShadingRateStateCreateInfoKHR);
261         *pNext = initVulkanStructure();
262 
263         pNext->fragmentSize   = getShadingRateSize(m_params.fragmentSize.get());
264         pNext->combinerOps[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR;
265         pNext->combinerOps[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
266     }
267 
268     const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(), taskModule.get(), meshModule.get(),
269                                                fragModule.get(), renderPass.get(), viewports, scissors, 0u, nullptr,
270                                                nullptr, nullptr, nullptr, nullptr, 0u, pNext.get());
271 
272     // Command pool and buffer.
273     const auto cmdPool      = makeCommandPool(vkd, device, queueIndex);
274     const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
275     const auto cmdBuffer    = cmdBufferPtr.get();
276 
277     // Indirect buffer if needed.
278     BufferWithMemoryPtr indirectBuffer;
279 
280     DE_ASSERT(!m_params.drawArgs.empty());
281     if (m_params.indirect)
282     {
283         // Indirect draws.
284         const auto indirectBufferSize  = static_cast<VkDeviceSize>(de::dataSize(m_params.drawArgs));
285         const auto indirectBufferUsage = (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
286         const auto indirectBufferInfo  = makeBufferCreateInfo(indirectBufferSize, indirectBufferUsage);
287         indirectBuffer                 = BufferWithMemoryPtr(
288             new BufferWithMemory(vkd, device, alloc, indirectBufferInfo, MemoryRequirement::HostVisible));
289         auto &indirectBufferAlloc = indirectBuffer->getAllocation();
290         void *indirectBufferData  = indirectBufferAlloc.getHostPtr();
291 
292         deMemcpy(indirectBufferData, m_params.drawArgs.data(), static_cast<size_t>(indirectBufferSize));
293         flushAlloc(vkd, device, indirectBufferAlloc);
294     }
295 
296     // Submit commands.
297     beginCommandBuffer(vkd, cmdBuffer);
298     beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor);
299     vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
300 
301     if (!m_params.indirect)
302     {
303         for (const auto &command : m_params.drawArgs)
304             vkd.cmdDrawMeshTasksNV(cmdBuffer, command.taskCount, command.firstTask);
305     }
306     else
307     {
308         const auto numDraws = static_cast<uint32_t>(m_params.drawArgs.size());
309         const auto stride   = static_cast<uint32_t>(sizeof(decltype(m_params.drawArgs)::value_type));
310         vkd.cmdDrawMeshTasksIndirectNV(cmdBuffer, indirectBuffer->get(), 0ull, numDraws, stride);
311     }
312 
313     endRenderPass(vkd, cmdBuffer);
314 
315     // Output buffer to extract the color buffer contents.
316     BufferWithMemoryPtr outBuffer;
317     void *outBufferData = nullptr;
318     {
319         const auto layerSize      = static_cast<VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) *
320                                                          extent.width * extent.height);
321         const auto outBufferSize  = layerSize * m_params.numLayers;
322         const auto outBufferUsage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
323         const auto outBufferInfo  = makeBufferCreateInfo(outBufferSize, outBufferUsage);
324 
325         outBuffer = BufferWithMemoryPtr(
326             new BufferWithMemory(vkd, device, alloc, outBufferInfo, MemoryRequirement::HostVisible));
327         outBufferData = outBuffer->getAllocation().getHostPtr();
328     }
329 
330     // Transition image layout.
331     const auto preTransferBarrier = makeImageMemoryBarrier(
332         (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), VK_ACCESS_TRANSFER_READ_BIT,
333         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer->get(), colorSRR);
334 
335     vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
336                            0u, nullptr, 0u, nullptr, 1u, &preTransferBarrier);
337 
338     // Copy image to output buffer.
339     const std::vector<VkBufferImageCopy> regions(1u, makeBufferImageCopy(extent, colorSRL));
340     vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outBuffer->get(),
341                              static_cast<uint32_t>(regions.size()), de::dataOrNull(regions));
342 
343     // Transfer to host barrier.
344     const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
345     vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u,
346                            &postTransferBarrier, 0u, nullptr, 0u, nullptr);
347 
348     endCommandBuffer(vkd, cmdBuffer);
349     submitCommandsAndWait(vkd, device, queue, cmdBuffer);
350 
351     // Invalidate alloc and verify result.
352     {
353         auto &outBufferAlloc = outBuffer->getAllocation();
354         invalidateAlloc(vkd, device, outBufferAlloc);
355 
356         tcu::ConstPixelBufferAccess result(tcuFormat, iExtent3D, outBufferData);
357         verifyResults(result);
358     }
359 
360     return tcu::TestStatus::pass("Pass");
361 }
362 
363 // Abstract case that implements the generic checkSupport method.
364 class MeshShaderBuiltinCase : public vkt::TestCase
365 {
366 public:
MeshShaderBuiltinCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)367     MeshShaderBuiltinCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
368         : vkt::TestCase(testCtx, name)
369         , m_taskNeeded(taskNeeded)
370     {
371     }
~MeshShaderBuiltinCase(void)372     virtual ~MeshShaderBuiltinCase(void)
373     {
374     }
375 
376     void checkSupport(Context &context) const override;
377 
378 protected:
379     const bool m_taskNeeded;
380 };
381 
checkSupport(Context & context) const382 void MeshShaderBuiltinCase::checkSupport(Context &context) const
383 {
384     checkTaskMeshShaderSupportNV(context, m_taskNeeded, true);
385 }
386 
387 // Instance that verifies color layers.
388 class FullScreenColorInstance : public MeshShaderBuiltinInstance
389 {
390 public:
FullScreenColorInstance(Context & context,const IterationParams & params,const ColorVec & expectedColors)391     FullScreenColorInstance(Context &context, const IterationParams &params, const ColorVec &expectedColors)
392         : MeshShaderBuiltinInstance(context, params)
393         , m_expectedColors(expectedColors)
394     {
395     }
~FullScreenColorInstance(void)396     virtual ~FullScreenColorInstance(void)
397     {
398     }
399 
400     void verifyResults(const tcu::ConstPixelBufferAccess &result) override;
401 
402 protected:
403     const ColorVec m_expectedColors;
404 };
405 
verifyResults(const tcu::ConstPixelBufferAccess & result)406 void FullScreenColorInstance::verifyResults(const tcu::ConstPixelBufferAccess &result)
407 {
408     auto &log         = m_context.getTestContext().getLog();
409     bool fail         = false;
410     const auto width  = result.getWidth();
411     const auto height = result.getHeight();
412     const auto depth  = result.getDepth();
413 
414     for (int z = 0; z < depth; ++z)
415     {
416         const auto &expected = m_expectedColors.at(z);
417 
418         for (int y = 0; y < height; ++y)
419             for (int x = 0; x < width; ++x)
420             {
421                 const auto resultColor = result.getPixel(x, y, z);
422                 if (resultColor != expected)
423                 {
424                     std::ostringstream msg;
425                     msg << "Pixel (" << x << ", " << y << ", " << z << ") failed: expected " << expected
426                         << " and found " << resultColor;
427                     log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
428                     fail = true;
429                 }
430             }
431     }
432 
433     if (fail)
434     {
435         log << tcu::TestLog::Image("Result", "", result);
436         TCU_FAIL("Check log for details");
437     }
438 }
439 
440 // Instance that verifies single-layer framebuffers divided into 4 quadrants.
441 class QuadrantsInstance : public MeshShaderBuiltinInstance
442 {
443 public:
QuadrantsInstance(Context & context,const IterationParams & params,const tcu::Vec4 topLeft,const tcu::Vec4 topRight,const tcu::Vec4 bottomLeft,const tcu::Vec4 bottomRight)444     QuadrantsInstance(Context &context, const IterationParams &params, const tcu::Vec4 topLeft,
445                       const tcu::Vec4 topRight, const tcu::Vec4 bottomLeft, const tcu::Vec4 bottomRight)
446         : MeshShaderBuiltinInstance(context, params)
447         , m_topLeft(topLeft)
448         , m_topRight(topRight)
449         , m_bottomLeft(bottomLeft)
450         , m_bottomRight(bottomRight)
451     {
452     }
~QuadrantsInstance(void)453     virtual ~QuadrantsInstance(void)
454     {
455     }
456 
457     void verifyResults(const tcu::ConstPixelBufferAccess &result) override;
458 
459 protected:
460     const tcu::Vec4 m_topLeft;
461     const tcu::Vec4 m_topRight;
462     const tcu::Vec4 m_bottomLeft;
463     const tcu::Vec4 m_bottomRight;
464 };
465 
verifyResults(const tcu::ConstPixelBufferAccess & result)466 void QuadrantsInstance::verifyResults(const tcu::ConstPixelBufferAccess &result)
467 {
468     const auto width  = result.getWidth();
469     const auto height = result.getHeight();
470     const auto depth  = result.getDepth();
471 
472     DE_ASSERT(depth == 1);
473     DE_ASSERT(width > 0 && width % 2 == 0);
474     DE_ASSERT(height > 0 && height % 2 == 0);
475     DE_UNREF(depth); // For release builds.
476 
477     const auto halfWidth  = width / 2;
478     const auto halfHeight = height / 2;
479     tcu::Vec4 expected;
480 
481     for (int y = 0; y < height; ++y)
482         for (int x = 0; x < width; ++x)
483         {
484             // Choose the right quadrant
485             if (y < halfHeight)
486                 expected = ((x < halfWidth) ? m_topLeft : m_topRight);
487             else
488                 expected = ((x < halfWidth) ? m_bottomLeft : m_bottomRight);
489 
490             const auto resultColor = result.getPixel(x, y);
491             if (resultColor != expected)
492             {
493                 std::ostringstream msg;
494                 msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
495                 TCU_FAIL(msg.str());
496             }
497         }
498 }
499 
500 // Instance that verifies single-layer framebuffers with specific pixels set to some color.
501 struct PixelVerifierParams
502 {
503     const tcu::Vec4 background;
504     const PixelMap pixelMap;
505 };
506 
507 class PixelsInstance : public MeshShaderBuiltinInstance
508 {
509 public:
PixelsInstance(Context & context,const IterationParams & params,const PixelVerifierParams & pixelParams)510     PixelsInstance(Context &context, const IterationParams &params, const PixelVerifierParams &pixelParams)
511         : MeshShaderBuiltinInstance(context, params)
512         , m_pixelParams(pixelParams)
513     {
514     }
~PixelsInstance(void)515     virtual ~PixelsInstance(void)
516     {
517     }
518 
519     void verifyResults(const tcu::ConstPixelBufferAccess &result) override;
520 
521 protected:
522     const PixelVerifierParams m_pixelParams;
523 };
524 
verifyResults(const tcu::ConstPixelBufferAccess & result)525 void PixelsInstance::verifyResults(const tcu::ConstPixelBufferAccess &result)
526 {
527     const auto width  = result.getWidth();
528     const auto height = result.getHeight();
529     const auto depth  = result.getDepth();
530 
531     DE_ASSERT(depth == 1);
532     DE_UNREF(depth); // For release builds.
533 
534     for (int y = 0; y < height; ++y)
535         for (int x = 0; x < width; ++x)
536         {
537             const tcu::IVec2 coords(x, y);
538             const auto iter        = m_pixelParams.pixelMap.find(coords);
539             const auto expected    = ((iter == m_pixelParams.pixelMap.end()) ? m_pixelParams.background : iter->second);
540             const auto resultColor = result.getPixel(x, y);
541 
542             if (resultColor != expected)
543             {
544                 std::ostringstream msg;
545                 msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
546                 TCU_FAIL(msg.str());
547             }
548         }
549 }
550 
551 // Primitive ID cases.
552 class PrimitiveIdCase : public MeshShaderBuiltinCase
553 {
554 public:
PrimitiveIdCase(tcu::TestContext & testCtx,const std::string & name,bool glslFrag)555     PrimitiveIdCase(tcu::TestContext &testCtx, const std::string &name, bool glslFrag)
556         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
557         , m_glslFrag(glslFrag)
558     {
559     }
~PrimitiveIdCase(void)560     virtual ~PrimitiveIdCase(void)
561     {
562     }
563 
564     void initPrograms(vk::SourceCollections &programCollection) const override;
565     void checkSupport(Context &context) const override;
566     TestInstance *createInstance(Context &context) const override;
567 
568 protected:
569     // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
570     const bool m_glslFrag;
571 };
572 
initPrograms(vk::SourceCollections & programCollection) const573 void PrimitiveIdCase::initPrograms(vk::SourceCollections &programCollection) const
574 {
575     // Mesh shader.
576     {
577         std::ostringstream mesh;
578         mesh << "#version 460\n"
579              << "#extension GL_NV_mesh_shader : enable\n"
580              << "\n"
581              << "layout (local_size_x=1) in;\n"
582              << "layout (triangles) out;\n"
583              << "layout (max_vertices=3, max_primitives=1) out;\n"
584              << "\n"
585              << "perprimitiveNV out gl_MeshPerPrimitiveNV {\n"
586              << "   int gl_PrimitiveID;\n"
587              << "} gl_MeshPrimitivesNV[];\n"
588              << "\n"
589              << "void main ()\n"
590              << "{\n"
591              << "    gl_PrimitiveCountNV = 1u;\n"
592              << "\n"
593              << "    gl_PrimitiveIndicesNV[0] = 0;\n"
594              << "    gl_PrimitiveIndicesNV[1] = 1;\n"
595              << "    gl_PrimitiveIndicesNV[2] = 2;\n"
596              << "\n"
597              << "    gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
598              << "    gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
599              << "    gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
600              << "\n"
601              // Sets an arbitrary primitive id.
602              << "    gl_MeshPrimitivesNV[0].gl_PrimitiveID = 1629198956;\n"
603              << "}\n";
604         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
605     }
606 
607     // Frag shader.
608     if (m_glslFrag)
609     {
610         std::ostringstream frag;
611         frag << "#version 460\n"
612              << "#extension GL_NV_mesh_shader : enable\n"
613              << "\n"
614              << "layout (location=0) out vec4 outColor;\n"
615              << "\n"
616              << "void main ()\n"
617              << "{\n"
618              // Checks the primitive id matches.
619              << "    outColor = ((gl_PrimitiveID == 1629198956) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, "
620                 "1.0));\n"
621              << "}\n";
622         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
623     }
624     else
625     {
626         // This is the same shader as above, but OpCapability Geometry has been replaced by OpCapability MeshShadingNV in order to
627         // access gl_PrimitiveID. This also needs the SPV_NV_mesh_shader extension.
628         std::ostringstream frag;
629         frag << "; Version: 1.0\n"
630              << "; Generator: Khronos Glslang Reference Front End; 10\n"
631              << "; Bound: 24\n"
632              << "; Schema: 0\n"
633              << "      OpCapability Shader\n"
634 
635              // Manual change in these lines.
636              //<< "      OpCapability Geometry\n"
637              << "      OpCapability MeshShadingNV\n"
638              << "      OpExtension \"SPV_NV_mesh_shader\"\n"
639 
640              << " %1 = OpExtInstImport \"GLSL.std.450\"\n"
641              << "      OpMemoryModel Logical GLSL450\n"
642              << "      OpEntryPoint Fragment %4 \"main\" %9 %12\n"
643              << "      OpExecutionMode %4 OriginUpperLeft\n"
644              << "      OpDecorate %9 Location 0\n"
645              << "      OpDecorate %12 Flat\n"
646              << "      OpDecorate %12 BuiltIn PrimitiveId\n"
647              << " %2 = OpTypeVoid\n"
648              << " %3 = OpTypeFunction %2\n"
649              << " %6 = OpTypeFloat 32\n"
650              << " %7 = OpTypeVector %6 4\n"
651              << " %8 = OpTypePointer Output %7\n"
652              << " %9 = OpVariable %8 Output\n"
653              << "%10 = OpTypeInt 32 1\n"
654              << "%11 = OpTypePointer Input %10\n"
655              << "%12 = OpVariable %11 Input\n"
656              << "%14 = OpConstant %10 1629198956\n"
657              << "%15 = OpTypeBool\n"
658              << "%17 = OpConstant %6 0\n"
659              << "%18 = OpConstant %6 1\n"
660              << "%19 = OpConstantComposite %7 %17 %17 %18 %18\n"
661              << "%20 = OpConstantComposite %7 %17 %17 %17 %18\n"
662              << "%21 = OpTypeVector %15 4\n"
663              << " %4 = OpFunction %2 None %3\n"
664              << " %5 = OpLabel\n"
665              << "%13 = OpLoad %10 %12\n"
666              << "%16 = OpIEqual %15 %13 %14\n"
667              << "%22 = OpCompositeConstruct %21 %16 %16 %16 %16\n"
668              << "%23 = OpSelect %7 %22 %19 %20\n"
669              << "      OpStore %9 %23\n"
670              << "      OpReturn\n"
671              << "      OpFunctionEnd\n";
672         programCollection.spirvAsmSources.add("frag") << frag.str();
673     }
674 }
675 
checkSupport(Context & context) const676 void PrimitiveIdCase::checkSupport(Context &context) const
677 {
678     MeshShaderBuiltinCase::checkSupport(context);
679 
680     // Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
681     if (m_glslFrag)
682         context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
683 }
684 
createInstance(Context & context) const685 TestInstance *PrimitiveIdCase::createInstance(Context &context) const
686 {
687     const ColorVec expectedColors(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
688     const IterationParams iterationParams = {
689         getDefaultExtent(),       // VkExtent2D colorExtent;
690         1u,                       // uint32_t numLayers;
691         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
692         false,                    // bool indirect;
693         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
694         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
695     };
696     return new FullScreenColorInstance(context, iterationParams, expectedColors);
697 }
698 
699 // Layer builtin case.
700 class LayerCase : public MeshShaderBuiltinCase
701 {
702 public:
LayerCase(tcu::TestContext & testCtx,const std::string & name,bool shareVertices)703     LayerCase(tcu::TestContext &testCtx, const std::string &name, bool shareVertices)
704         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
705         , m_shareVertices(shareVertices)
706     {
707     }
~LayerCase(void)708     virtual ~LayerCase(void)
709     {
710     }
711 
712     void initPrograms(vk::SourceCollections &programCollection) const override;
713     void checkSupport(Context &context) const override;
714     TestInstance *createInstance(Context &context) const override;
715 
716     static constexpr uint32_t kNumLayers = 4u;
717 
718 protected:
719     const bool m_shareVertices;
720 };
721 
initPrograms(vk::SourceCollections & programCollection) const722 void LayerCase::initPrograms(vk::SourceCollections &programCollection) const
723 {
724     const auto localSize     = (m_shareVertices ? kNumLayers : 1u);
725     const auto numPrimitives = (m_shareVertices ? kNumLayers : 1u);
726     const auto layerNumber   = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
727 
728     // One layer per local invocation or work group (shared vertices or not, respectively).
729     {
730         std::ostringstream mesh;
731         mesh << "#version 460\n"
732              << "#extension GL_NV_mesh_shader : enable\n"
733              << "\n"
734              << "layout (local_size_x=" << localSize << ") in;\n"
735              << "layout (triangles) out;\n"
736              << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
737              << "\n"
738              << "perprimitiveNV out gl_MeshPerPrimitiveNV {\n"
739              << "   int gl_Layer;\n"
740              << "} gl_MeshPrimitivesNV[];\n"
741              << "\n"
742              << "void main ()\n"
743              << "{\n"
744              << "    gl_PrimitiveCountNV = " << numPrimitives << ";\n"
745              << "\n"
746              << "    if (gl_LocalInvocationIndex == 0u)\n"
747              << "    {\n"
748              << "        gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
749              << "        gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
750              << "        gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
751              << "    }\n"
752              << "\n"
753              << "    const uint baseIndex = gl_LocalInvocationIndex * 3u;\n"
754              << "    gl_PrimitiveIndicesNV[baseIndex + 0] = 0;\n"
755              << "    gl_PrimitiveIndicesNV[baseIndex + 1] = 1;\n"
756              << "    gl_PrimitiveIndicesNV[baseIndex + 2] = 2;\n"
757              << "\n"
758              << "    gl_MeshPrimitivesNV[gl_LocalInvocationIndex].gl_Layer = int(" << layerNumber << ");\n"
759              << "}\n";
760         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
761     }
762 
763     // Fragment shader chooses one color per layer.
764     {
765         std::ostringstream frag;
766         frag << "#version 460\n"
767              << "#extension GL_NV_mesh_shader : enable\n"
768              << "\n"
769              << "layout (location=0) out vec4 outColor;\n"
770              << "\n"
771              << "vec4 colors[" << kNumLayers << "] = vec4[](\n"
772              << "    vec4(0.0, 0.0, 1.0, 1.0),\n"
773              << "    vec4(1.0, 0.0, 1.0, 1.0),\n"
774              << "    vec4(0.0, 1.0, 1.0, 1.0),\n"
775              << "    vec4(1.0, 1.0, 0.0, 1.0)\n"
776              << ");\n"
777              << "\n"
778              << "void main ()\n"
779              << "{\n"
780              << "    outColor = colors[gl_Layer];\n"
781              << "}\n";
782         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
783     }
784 }
785 
checkSupport(Context & context) const786 void LayerCase::checkSupport(Context &context) const
787 {
788     MeshShaderBuiltinCase::checkSupport(context);
789 
790     if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
791         context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
792     else
793     {
794         const auto &features = context.getDeviceVulkan12Features();
795         if (!features.shaderOutputLayer)
796             TCU_THROW(NotSupportedError, "shaderOutputLayer feature not supported");
797     }
798 }
799 
createInstance(Context & context) const800 TestInstance *LayerCase::createInstance(Context &context) const
801 {
802     ColorVec expectedColors;
803 
804     expectedColors.reserve(kNumLayers);
805     expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0));
806     expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0));
807     expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0));
808     expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0));
809 
810     const auto numWorkGroups              = (m_shareVertices ? 1u : kNumLayers);
811     const IterationParams iterationParams = {
812         getDefaultExtent(),                    // VkExtent2D colorExtent;
813         kNumLayers,                            // uint32_t numLayers;
814         getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs;
815         false,                                 // bool indirect;
816         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
817         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
818     };
819     return new FullScreenColorInstance(context, iterationParams, expectedColors);
820 }
821 
822 // ViewportIndex builtin case.
823 class ViewportIndexCase : public MeshShaderBuiltinCase
824 {
825 public:
ViewportIndexCase(tcu::TestContext & testCtx,const std::string & name,bool shareVertices)826     ViewportIndexCase(tcu::TestContext &testCtx, const std::string &name, bool shareVertices)
827         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
828         , m_shareVertices(shareVertices)
829     {
830     }
~ViewportIndexCase(void)831     virtual ~ViewportIndexCase(void)
832     {
833     }
834 
835     void initPrograms(vk::SourceCollections &programCollection) const override;
836     void checkSupport(Context &context) const override;
837     TestInstance *createInstance(Context &context) const override;
838 
839     static constexpr uint32_t kQuadrants = 4u;
840 
841 protected:
842     const bool m_shareVertices;
843 };
844 
initPrograms(vk::SourceCollections & programCollection) const845 void ViewportIndexCase::initPrograms(vk::SourceCollections &programCollection) const
846 {
847     const auto localSize     = (m_shareVertices ? kQuadrants : 1u);
848     const auto numPrimitives = (m_shareVertices ? kQuadrants : 1u);
849     const auto viewportIndex = (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
850 
851     // One viewport per local invocation or work group (sharing vertices or not, respectively).
852     {
853         std::ostringstream mesh;
854         mesh << "#version 460\n"
855              << "#extension GL_NV_mesh_shader : enable\n"
856              << "\n"
857              << "layout (local_size_x=" << localSize << ") in;\n"
858              << "layout (triangles) out;\n"
859              << "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
860              << "\n"
861              << "perprimitiveNV out gl_MeshPerPrimitiveNV {\n"
862              << "   int gl_ViewportIndex;\n"
863              << "} gl_MeshPrimitivesNV[];\n"
864              << "\n"
865              << "void main ()\n"
866              << "{\n"
867              << "    gl_PrimitiveCountNV = " << numPrimitives << ";\n"
868              << "\n"
869              << "    if (gl_LocalInvocationIndex == 0u)\n"
870              << "    {\n"
871              << "        gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
872              << "        gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
873              << "        gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
874              << "    }\n"
875              << "\n"
876              << "    const uint baseIndex = gl_LocalInvocationIndex * 3u;\n"
877              << "    gl_PrimitiveIndicesNV[baseIndex + 0] = 0;\n"
878              << "    gl_PrimitiveIndicesNV[baseIndex + 1] = 1;\n"
879              << "    gl_PrimitiveIndicesNV[baseIndex + 2] = 2;\n"
880              << "\n"
881              << "    gl_MeshPrimitivesNV[gl_LocalInvocationIndex].gl_ViewportIndex = int(" << viewportIndex << ");\n"
882              << "}\n";
883         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
884     }
885 
886     // Fragment shader chooses one color per viewport.
887     {
888         std::ostringstream frag;
889         frag << "#version 460\n"
890              << "#extension GL_NV_mesh_shader : enable\n"
891              << "\n"
892              << "layout (location=0) out vec4 outColor;\n"
893              << "\n"
894              << "vec4 colors[" << kQuadrants << "] = vec4[](\n"
895              << "    vec4(0.0, 0.0, 1.0, 1.0),\n"
896              << "    vec4(1.0, 0.0, 1.0, 1.0),\n"
897              << "    vec4(0.0, 1.0, 1.0, 1.0),\n"
898              << "    vec4(1.0, 1.0, 0.0, 1.0)\n"
899              << ");\n"
900              << "\n"
901              << "void main ()\n"
902              << "{\n"
903              << "    outColor = colors[gl_ViewportIndex];\n"
904              << "}\n";
905         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
906     }
907 }
908 
checkSupport(Context & context) const909 void ViewportIndexCase::checkSupport(Context &context) const
910 {
911     MeshShaderBuiltinCase::checkSupport(context);
912     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
913 
914     if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
915         context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
916     else
917     {
918         const auto &features = context.getDeviceVulkan12Features();
919         if (!features.shaderOutputViewportIndex)
920             TCU_THROW(NotSupportedError, "shaderOutputViewportIndex feature not supported");
921     }
922 }
923 
createInstance(Context & context) const924 TestInstance *ViewportIndexCase::createInstance(Context &context) const
925 {
926     const auto extent = getDefaultExtent();
927 
928     DE_ASSERT(extent.width > 0u && extent.width % 2u == 0u);
929     DE_ASSERT(extent.height > 0u && extent.height % 2u == 0u);
930 
931     const auto halfWidth  = static_cast<float>(extent.width) / 2.0f;
932     const auto halfHeight = static_cast<float>(extent.height) / 2.0f;
933 
934     const auto topLeft     = tcu::Vec4(0.0, 0.0, 1.0, 1.0);
935     const auto topRight    = tcu::Vec4(1.0, 0.0, 1.0, 1.0);
936     const auto bottomLeft  = tcu::Vec4(0.0, 1.0, 1.0, 1.0);
937     const auto bottomRight = tcu::Vec4(1.0, 1.0, 0.0, 1.0);
938 
939     ViewportVec viewports;
940     viewports.reserve(kQuadrants);
941     viewports.emplace_back(makeViewport(0.0f, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f));
942     viewports.emplace_back(makeViewport(halfWidth, 0.0f, halfWidth, halfHeight, 0.0f, 1.0f));
943     viewports.emplace_back(makeViewport(0.0f, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f));
944     viewports.emplace_back(makeViewport(halfWidth, halfHeight, halfWidth, halfHeight, 0.0f, 1.0f));
945 
946     const auto numWorkGroups              = (m_shareVertices ? 1u : kQuadrants);
947     const IterationParams iterationParams = {
948         getDefaultExtent(),                    // VkExtent2D colorExtent;
949         1u,                                    // uint32_t numLayers;
950         getDefaultDrawCommands(numWorkGroups), // DrawCommandVec drawArgs;
951         false,                                 // bool indirect;
952         std::move(viewports),                  // ViewportVec viewports;
953         tcu::Nothing,                          // tcu::Maybe<FragmentSize> fragmentSize;
954     };
955     return new QuadrantsInstance(context, iterationParams, topLeft, topRight, bottomLeft, bottomRight);
956 }
957 
958 // Position builtin case.
959 class PositionCase : public MeshShaderBuiltinCase
960 {
961 public:
PositionCase(tcu::TestContext & testCtx,const std::string & name)962     PositionCase(tcu::TestContext &testCtx, const std::string &name)
963         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
964     {
965     }
~PositionCase(void)966     virtual ~PositionCase(void)
967     {
968     }
969 
970     void initPrograms(vk::SourceCollections &programCollection) const override;
971     TestInstance *createInstance(Context &context) const override;
972 };
973 
initPrograms(vk::SourceCollections & programCollection) const974 void PositionCase::initPrograms(vk::SourceCollections &programCollection) const
975 {
976     // Mesh shader: emit single triangle around the center of the top left pixel.
977     {
978         const auto extent  = getDefaultExtent();
979         const auto fWidth  = static_cast<float>(extent.width);
980         const auto fHeight = static_cast<float>(extent.height);
981 
982         const auto pxWidth  = 2.0f / fWidth;
983         const auto pxHeight = 2.0f / fHeight;
984 
985         const auto halfXPix = pxWidth / 2.0f;
986         const auto halfYPix = pxHeight / 2.0f;
987 
988         // Center of top left pixel.
989         const auto x = -1.0f + halfXPix;
990         const auto y = -1.0f + halfYPix;
991 
992         std::ostringstream mesh;
993         mesh << "#version 460\n"
994              << "#extension GL_NV_mesh_shader : enable\n"
995              << "\n"
996              << "layout (local_size_x=1) in;\n"
997              << "layout (triangles) out;\n"
998              << "layout (max_vertices=3, max_primitives=1) out;\n"
999              << "\n"
1000              << "void main ()\n"
1001              << "{\n"
1002              << "    gl_PrimitiveCountNV = 1u;\n"
1003              << "\n"
1004              << "    gl_PrimitiveIndicesNV[0] = 0;\n"
1005              << "    gl_PrimitiveIndicesNV[1] = 1;\n"
1006              << "    gl_PrimitiveIndicesNV[2] = 2;\n"
1007              << "\n"
1008              << "    gl_MeshVerticesNV[0].gl_Position = vec4(" << (x - halfXPix) << ", " << (y + halfYPix)
1009              << ", 0.0, 1.0);\n"
1010              << "    gl_MeshVerticesNV[1].gl_Position = vec4(" << (x + halfXPix) << ", " << (y + halfYPix)
1011              << ", 0.0, 1.0);\n"
1012              << "    gl_MeshVerticesNV[2].gl_Position = vec4(" << x << ", " << (y - halfYPix) << ", 0.0, 1.0);\n"
1013              << "}\n";
1014         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1015     }
1016 
1017     // Basic fragment shader.
1018     {
1019         const auto frag = getBasicFragShader();
1020         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1021     }
1022 }
1023 
createInstance(Context & context) const1024 TestInstance *PositionCase::createInstance(Context &context) const
1025 {
1026     const IterationParams iterationParams = {
1027         getDefaultExtent(),       // VkExtent2D colorExtent;
1028         1u,                       // uint32_t numLayers;
1029         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1030         false,                    // bool indirect;
1031         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1032         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1033     };
1034 
1035     // Must match the shader.
1036     PixelMap pixelMap;
1037     pixelMap[tcu::IVec2(0, 0)] = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
1038 
1039     const PixelVerifierParams verifierParams = {
1040         tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), // const tcu::Vec4 background;
1041         std::move(pixelMap),               // const PixelMap pixelMap;
1042     };
1043     return new PixelsInstance(context, iterationParams, verifierParams);
1044 }
1045 
1046 // PointSize builtin case.
1047 class PointSizeCase : public MeshShaderBuiltinCase
1048 {
1049 public:
PointSizeCase(tcu::TestContext & testCtx,const std::string & name)1050     PointSizeCase(tcu::TestContext &testCtx, const std::string &name)
1051         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1052     {
1053     }
~PointSizeCase(void)1054     virtual ~PointSizeCase(void)
1055     {
1056     }
1057 
1058     void initPrograms(vk::SourceCollections &programCollection) const override;
1059     TestInstance *createInstance(Context &context) const override;
1060     void checkSupport(Context &context) const override;
1061 
1062     static constexpr float kPointSize = 4.0f;
1063 };
1064 
initPrograms(vk::SourceCollections & programCollection) const1065 void PointSizeCase::initPrograms(vk::SourceCollections &programCollection) const
1066 {
1067     // Mesh shader: large point covering the top left quadrant.
1068     {
1069         std::ostringstream mesh;
1070         mesh << "#version 460\n"
1071              << "#extension GL_NV_mesh_shader : enable\n"
1072              << "\n"
1073              << "layout (local_size_x=1) in;\n"
1074              << "layout (points) out;\n"
1075              << "layout (max_vertices=1, max_primitives=1) out;\n"
1076              << "\n"
1077              << "void main ()\n"
1078              << "{\n"
1079              << "    gl_PrimitiveCountNV = 1u;\n"
1080              << "    gl_PrimitiveIndicesNV[0] = 0;\n"
1081              << "    gl_MeshVerticesNV[0].gl_Position = vec4(-0.5, -0.5, 0.0, 1.0);\n"
1082              << "    gl_MeshVerticesNV[0].gl_PointSize = " << kPointSize << ";\n"
1083              << "}\n";
1084         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1085     }
1086 
1087     // Basic fragment shader.
1088     {
1089         const auto frag = getBasicFragShader();
1090         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1091     }
1092 }
1093 
createInstance(Context & context) const1094 TestInstance *PointSizeCase::createInstance(Context &context) const
1095 {
1096     const IterationParams iterationParams = {
1097         getDefaultExtent(),       // VkExtent2D colorExtent;
1098         1u,                       // uint32_t numLayers;
1099         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1100         false,                    // bool indirect;
1101         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1102         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1103     };
1104 
1105     // Must match the shader.
1106     const tcu::Vec4 black(0.0f, 0.0f, 0.0f, 1.0f);
1107     const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1108 
1109     return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1110 }
1111 
checkSupport(Context & context) const1112 void PointSizeCase::checkSupport(Context &context) const
1113 {
1114     MeshShaderBuiltinCase::checkSupport(context);
1115     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_LARGE_POINTS);
1116 
1117     const auto &properties = context.getDeviceProperties();
1118     if (kPointSize < properties.limits.pointSizeRange[0] || kPointSize > properties.limits.pointSizeRange[1])
1119         TCU_THROW(NotSupportedError, "Required point size outside point size range");
1120 }
1121 
1122 // ClipDistance builtin case.
1123 class ClipDistanceCase : public MeshShaderBuiltinCase
1124 {
1125 public:
ClipDistanceCase(tcu::TestContext & testCtx,const std::string & name)1126     ClipDistanceCase(tcu::TestContext &testCtx, const std::string &name)
1127         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1128     {
1129     }
~ClipDistanceCase(void)1130     virtual ~ClipDistanceCase(void)
1131     {
1132     }
1133 
1134     void initPrograms(vk::SourceCollections &programCollection) const override;
1135     TestInstance *createInstance(Context &context) const override;
1136     void checkSupport(Context &context) const override;
1137 };
1138 
initPrograms(vk::SourceCollections & programCollection) const1139 void ClipDistanceCase::initPrograms(vk::SourceCollections &programCollection) const
1140 {
1141     // Mesh shader: full-screen quad using different clip distances.
1142     {
1143         std::ostringstream mesh;
1144         mesh << "#version 460\n"
1145              << "#extension GL_NV_mesh_shader : enable\n"
1146              << "\n"
1147              << "layout (local_size_x=1) in;\n"
1148              << "layout (triangles) out;\n"
1149              << "layout (max_vertices=4, max_primitives=2) out;\n"
1150              << "\n"
1151              << "out gl_MeshPerVertexNV {\n"
1152              << "    vec4  gl_Position;\n"
1153              << "    float gl_ClipDistance[2];\n"
1154              << "} gl_MeshVerticesNV[];\n"
1155              << "\n"
1156              << "void main ()\n"
1157              << "{\n"
1158              << "    gl_PrimitiveCountNV = 2u;\n"
1159              << "\n"
1160              << "    gl_PrimitiveIndicesNV[0] = 0;\n"
1161              << "    gl_PrimitiveIndicesNV[1] = 1;\n"
1162              << "    gl_PrimitiveIndicesNV[2] = 2;\n"
1163              << "    gl_PrimitiveIndicesNV[3] = 1;\n"
1164              << "    gl_PrimitiveIndicesNV[4] = 3;\n"
1165              << "    gl_PrimitiveIndicesNV[5] = 2;\n"
1166              << "\n"
1167              << "    gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1168              << "    gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  1.0, 0.0, 1.0);\n"
1169              << "    gl_MeshVerticesNV[2].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1170              << "    gl_MeshVerticesNV[3].gl_Position = vec4( 1.0,  1.0, 0.0, 1.0);\n"
1171              << "\n"
1172              // The first clip plane keeps the left half of the frame buffer.
1173              << "    gl_MeshVerticesNV[0].gl_ClipDistance[0] =  1.0;\n"
1174              << "    gl_MeshVerticesNV[1].gl_ClipDistance[0] =  1.0;\n"
1175              << "    gl_MeshVerticesNV[2].gl_ClipDistance[0] = -1.0;\n"
1176              << "    gl_MeshVerticesNV[3].gl_ClipDistance[0] = -1.0;\n"
1177              << "\n"
1178              // The second clip plane keeps the top half of the frame buffer.
1179              << "    gl_MeshVerticesNV[0].gl_ClipDistance[1] =  1.0;\n"
1180              << "    gl_MeshVerticesNV[1].gl_ClipDistance[1] = -1.0;\n"
1181              << "    gl_MeshVerticesNV[2].gl_ClipDistance[1] =  1.0;\n"
1182              << "    gl_MeshVerticesNV[3].gl_ClipDistance[1] = -1.0;\n"
1183              << "}\n";
1184         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1185     }
1186 
1187     // Fragment shader chooses a constant color.
1188     {
1189         std::ostringstream frag;
1190         frag << "#version 460\n"
1191              << "#extension GL_NV_mesh_shader : enable\n"
1192              << "\n"
1193              << "layout (location=0) out vec4 outColor;\n"
1194              << "\n"
1195              << "void main ()\n"
1196              << "{\n"
1197              // White color should not actually be used, as those fragments are supposed to be discarded.
1198              << "    outColor = ((gl_ClipDistance[0] >= 0.0 && gl_ClipDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : "
1199                 "vec4(1.0, 1.0, 1.0, 1.0));\n"
1200              << "}\n";
1201         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
1202     }
1203 }
1204 
createInstance(Context & context) const1205 TestInstance *ClipDistanceCase::createInstance(Context &context) const
1206 {
1207     const IterationParams iterationParams = {
1208         getDefaultExtent(),       // VkExtent2D colorExtent;
1209         1u,                       // uint32_t numLayers;
1210         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1211         false,                    // bool indirect;
1212         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1213         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1214     };
1215 
1216     // Must match the shader.
1217     const tcu::Vec4 black(0.0f, 0.0f, 0.0f, 1.0f);
1218     const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1219 
1220     return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1221 }
1222 
checkSupport(Context & context) const1223 void ClipDistanceCase::checkSupport(Context &context) const
1224 {
1225     MeshShaderBuiltinCase::checkSupport(context);
1226     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CLIP_DISTANCE);
1227 }
1228 
1229 // CullDistance builtin case.
1230 class CullDistanceCase : public MeshShaderBuiltinCase
1231 {
1232 public:
CullDistanceCase(tcu::TestContext & testCtx,const std::string & name)1233     CullDistanceCase(tcu::TestContext &testCtx, const std::string &name)
1234         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1235     {
1236     }
~CullDistanceCase(void)1237     virtual ~CullDistanceCase(void)
1238     {
1239     }
1240 
1241     void initPrograms(vk::SourceCollections &programCollection) const override;
1242     TestInstance *createInstance(Context &context) const override;
1243     void checkSupport(Context &context) const override;
1244 };
1245 
initPrograms(vk::SourceCollections & programCollection) const1246 void CullDistanceCase::initPrograms(vk::SourceCollections &programCollection) const
1247 {
1248     // Mesh shader: two quads covering the whole screen, one on top of the other.
1249     // Use cull distances to discard the bottom quad.
1250     // Use cull distances to paint the top one in two colors: blue on the left, white on the right.
1251     {
1252         std::ostringstream mesh;
1253         mesh << "#version 460\n"
1254              << "#extension GL_NV_mesh_shader : enable\n"
1255              << "\n"
1256              << "layout (local_size_x=1) in;\n"
1257              << "layout (triangles) out;\n"
1258              << "layout (max_vertices=6, max_primitives=4) out;\n"
1259              << "\n"
1260              << "out gl_MeshPerVertexNV {\n"
1261              << "    vec4  gl_Position;\n"
1262              << "    float gl_CullDistance[2];\n"
1263              << "} gl_MeshVerticesNV[];\n"
1264              << "\n"
1265              << "void main ()\n"
1266              << "{\n"
1267              << "    gl_PrimitiveCountNV = 4u;\n"
1268              << "\n"
1269              << "    gl_PrimitiveIndicesNV[0]  = 0;\n"
1270              << "    gl_PrimitiveIndicesNV[1]  = 1;\n"
1271              << "    gl_PrimitiveIndicesNV[2]  = 3;\n"
1272              << "    gl_PrimitiveIndicesNV[3]  = 1;\n"
1273              << "    gl_PrimitiveIndicesNV[4]  = 4;\n"
1274              << "    gl_PrimitiveIndicesNV[5]  = 3;\n"
1275              << "    gl_PrimitiveIndicesNV[6]  = 1;\n"
1276              << "    gl_PrimitiveIndicesNV[7]  = 2;\n"
1277              << "    gl_PrimitiveIndicesNV[8]  = 4;\n"
1278              << "    gl_PrimitiveIndicesNV[9]  = 2;\n"
1279              << "    gl_PrimitiveIndicesNV[10] = 5;\n"
1280              << "    gl_PrimitiveIndicesNV[11] = 4;\n"
1281              << "\n"
1282              << "    gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1283              << "    gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  0.0, 0.0, 1.0);\n"
1284              << "    gl_MeshVerticesNV[2].gl_Position = vec4(-1.0,  1.0, 0.0, 1.0);\n"
1285              << "    gl_MeshVerticesNV[3].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1286              << "    gl_MeshVerticesNV[4].gl_Position = vec4( 1.0,  0.0, 0.0, 1.0);\n"
1287              << "    gl_MeshVerticesNV[5].gl_Position = vec4( 1.0,  1.0, 0.0, 1.0);\n"
1288              << "\n"
1289              // The first cull plane discards the bottom quad
1290              << "    gl_MeshVerticesNV[0].gl_CullDistance[0] =  1.0;\n"
1291              << "    gl_MeshVerticesNV[1].gl_CullDistance[0] = -1.0;\n"
1292              << "    gl_MeshVerticesNV[2].gl_CullDistance[0] = -2.0;\n"
1293              << "    gl_MeshVerticesNV[3].gl_CullDistance[0] =  1.0;\n"
1294              << "    gl_MeshVerticesNV[4].gl_CullDistance[0] = -1.0;\n"
1295              << "    gl_MeshVerticesNV[5].gl_CullDistance[0] = -2.0;\n"
1296              << "\n"
1297              // The second cull plane helps paint left and right different.
1298              << "    gl_MeshVerticesNV[0].gl_CullDistance[1] =  1.0;\n"
1299              << "    gl_MeshVerticesNV[1].gl_CullDistance[1] =  1.0;\n"
1300              << "    gl_MeshVerticesNV[2].gl_CullDistance[1] =  1.0;\n"
1301              << "    gl_MeshVerticesNV[3].gl_CullDistance[1] = -1.0;\n"
1302              << "    gl_MeshVerticesNV[4].gl_CullDistance[1] = -1.0;\n"
1303              << "    gl_MeshVerticesNV[5].gl_CullDistance[1] = -1.0;\n"
1304              << "}\n";
1305         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1306     }
1307 
1308     // Fragment shader chooses color based on the second cull distance.
1309     {
1310         std::ostringstream frag;
1311         frag << "#version 460\n"
1312              << "#extension GL_NV_mesh_shader : enable\n"
1313              << "\n"
1314              << "layout (location=0) out vec4 outColor;\n"
1315              << "\n"
1316              << "void main ()\n"
1317              << "{\n"
1318              << "    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"
1319              << "}\n";
1320         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
1321     }
1322 }
1323 
createInstance(Context & context) const1324 TestInstance *CullDistanceCase::createInstance(Context &context) const
1325 {
1326     const IterationParams iterationParams = {
1327         getDefaultExtent(),       // VkExtent2D colorExtent;
1328         1u,                       // uint32_t numLayers;
1329         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1330         false,                    // bool indirect;
1331         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1332         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1333     };
1334 
1335     // Must match the shader.
1336     const tcu::Vec4 black(0.0f, 0.0f, 0.0f, 1.0f);
1337     const tcu::Vec4 blue(0.0f, 0.0f, 1.0f, 1.0f);
1338     const tcu::Vec4 white(1.0f, 1.0f, 1.0f, 1.0f);
1339 
1340     return new QuadrantsInstance(context, iterationParams, blue, white, black, black);
1341 }
1342 
checkSupport(Context & context) const1343 void CullDistanceCase::checkSupport(Context &context) const
1344 {
1345     MeshShaderBuiltinCase::checkSupport(context);
1346     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CULL_DISTANCE);
1347 }
1348 
1349 // Generates statements to draw a triangle around the given pixel number, knowing the framebuffer width (len).
1350 // Supposes the height of the framebuffer is 1.
triangleForPixel(const std::string & pixel,const std::string & len,const std::string & baseIndex)1351 std::string triangleForPixel(const std::string &pixel, const std::string &len, const std::string &baseIndex)
1352 {
1353     std::ostringstream statements;
1354     statements << "    const float imgWidth = float(" << len << ");\n"
1355                << "    const float pixWidth = (2.0 / imgWidth);\n"
1356                << "    const float halfPix  = (pixWidth / 2.0);\n"
1357                << "    const float xCenter  = (((float(" << pixel << ") + 0.5) / imgWidth) * 2.0 - 1.0);\n"
1358                << "    const float xLeft    = (xCenter - halfPix);\n"
1359                << "    const float xRight   = (xCenter + halfPix);\n"
1360                << "    const uvec3 indices  = uvec3(" << baseIndex << " + 0, " << baseIndex << " + 1, " << baseIndex
1361                << " + 2);\n"
1362                << "\n"
1363                << "    gl_PrimitiveIndicesNV[indices.x] = indices.x;\n"
1364                << "    gl_PrimitiveIndicesNV[indices.y] = indices.y;\n"
1365                << "    gl_PrimitiveIndicesNV[indices.z] = indices.z;\n"
1366                << "\n"
1367                << "    gl_MeshVerticesNV[indices.x].gl_Position = vec4(xLeft,    0.5, 0.0, 1.0);\n"
1368                << "    gl_MeshVerticesNV[indices.y].gl_Position = vec4(xRight,   0.5, 0.0, 1.0);\n"
1369                << "    gl_MeshVerticesNV[indices.z].gl_Position = vec4(xCenter, -0.5, 0.0, 1.0);\n";
1370     return statements.str();
1371 }
1372 
1373 // WorkGroupID builtin case.
1374 class WorkGroupIdCase : public MeshShaderBuiltinCase
1375 {
1376 public:
WorkGroupIdCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)1377     WorkGroupIdCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
1378         : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1379         , m_extent(getLinearExtent())
1380     {
1381     }
~WorkGroupIdCase(void)1382     virtual ~WorkGroupIdCase(void)
1383     {
1384     }
1385 
1386     void initPrograms(vk::SourceCollections &programCollection) const override;
1387     TestInstance *createInstance(Context &context) const override;
1388 
1389 protected:
1390     const VkExtent2D m_extent;
1391 };
1392 
initPrograms(vk::SourceCollections & programCollection) const1393 void WorkGroupIdCase::initPrograms(vk::SourceCollections &programCollection) const
1394 {
1395     const std::string taskDataDecl = "taskNV TaskData {\n"
1396                                      "    uint id;\n"
1397                                      "    uint size;\n"
1398                                      "} td;\n";
1399 
1400     // Mesh shader: each work group fills one pixel.
1401     {
1402         const std::string pixel = (m_taskNeeded ? "td.id" : "gl_WorkGroupID.x");
1403         const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1404 
1405         std::ostringstream mesh;
1406         mesh << "#version 460\n"
1407              << "#extension GL_NV_mesh_shader : enable\n"
1408              << "\n"
1409              << "layout (local_size_x=1) in;\n"
1410              << "layout (triangles) out;\n"
1411              << "layout (max_vertices=3, max_primitives=1) out;\n"
1412              << "\n"
1413              << (m_taskNeeded ? ("in " + taskDataDecl) : "") << "\n"
1414              << "void main ()\n"
1415              << "{\n"
1416              << "    gl_PrimitiveCountNV = 1u;\n"
1417              << "\n"
1418              << triangleForPixel(pixel, len, "0") << "}\n";
1419         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1420     }
1421 
1422     if (m_taskNeeded)
1423     {
1424         std::ostringstream task;
1425         task << "#version 460\n"
1426              << "#extension GL_NV_mesh_shader : enable\n"
1427              << "\n"
1428              << "layout (local_size_x=1) in;\n"
1429              << "\n"
1430              << "out " << taskDataDecl << "\n"
1431              << "void main ()\n"
1432              << "{\n"
1433              << "    gl_TaskCountNV = 1u;\n"
1434              << "    td.id          = gl_WorkGroupID.x;\n"
1435              << "    td.size        = " << m_extent.width << ";\n"
1436              << "}\n";
1437         programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1438     }
1439 
1440     // Basic fragment shader.
1441     {
1442         const auto frag = getBasicFragShader();
1443         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1444     }
1445 }
1446 
createInstance(Context & context) const1447 TestInstance *WorkGroupIdCase::createInstance(Context &context) const
1448 {
1449     // Must match the shader.
1450     const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1451     const IterationParams iterationParams = {
1452         m_extent,                               // VkExtent2D colorExtent;
1453         1u,                                     // uint32_t numLayers;
1454         getDefaultDrawCommands(m_extent.width), // DrawCommandVec drawArgs;
1455         false,                                  // bool indirect;
1456         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1457         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1458     };
1459     return new FullScreenColorInstance(context, iterationParams, expectedColors);
1460 }
1461 
1462 // Variable to use.
1463 enum class LocalInvocation
1464 {
1465     ID = 0,
1466     INDEX
1467 };
1468 
1469 // LocalInvocationId and LocalInvocationIndex builtin cases. These are also used to test WorkGroupSize.
1470 class LocalInvocationCase : public MeshShaderBuiltinCase
1471 {
1472 public:
LocalInvocationCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded,LocalInvocation variable)1473     LocalInvocationCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded, LocalInvocation variable)
1474         : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1475         , m_extent(getLinearExtent())
1476         , m_variable(variable)
1477     {
1478     }
~LocalInvocationCase(void)1479     virtual ~LocalInvocationCase(void)
1480     {
1481     }
1482 
1483     void initPrograms(vk::SourceCollections &programCollection) const override;
1484     TestInstance *createInstance(Context &context) const override;
1485 
1486 protected:
1487     const VkExtent2D m_extent;
1488     const LocalInvocation m_variable;
1489 };
1490 
initPrograms(vk::SourceCollections & programCollection) const1491 void LocalInvocationCase::initPrograms(vk::SourceCollections &programCollection) const
1492 {
1493     // Invocation index to use.
1494     const std::string localIndex =
1495         ((m_variable == LocalInvocation::ID) ? "gl_LocalInvocationID.x" : "gl_LocalInvocationIndex");
1496 
1497     // Task data.
1498     std::ostringstream taskDataDecl;
1499     taskDataDecl << "taskNV TaskData {\n"
1500                  // indexNumber[x] == x
1501                  << "    uint indexNumber[" << m_extent.width << "];\n"
1502                  << "    uint size;\n"
1503                  << "} td;\n";
1504     const auto taskDataDeclStr = taskDataDecl.str();
1505 
1506     // Mesh shader: each work group fills one pixel.
1507     {
1508         const std::string pixel     = (m_taskNeeded ? "td.indexNumber[gl_WorkGroupID.x]" : localIndex);
1509         const std::string len       = (m_taskNeeded ? "td.size" : "gl_WorkGroupSize.x");
1510         const auto localSize        = (m_taskNeeded ? 1u : m_extent.width);
1511         const auto maxVert          = localSize * 3u;
1512         const std::string baseIndex = (m_taskNeeded ? "0" : "(" + localIndex + " * 3u)");
1513 
1514         std::ostringstream mesh;
1515         mesh << "#version 460\n"
1516              << "#extension GL_NV_mesh_shader : enable\n"
1517              << "\n"
1518              << "layout (local_size_x=" << localSize << ") in;\n"
1519              << "layout (triangles) out;\n"
1520              << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1521              << "\n"
1522              << (m_taskNeeded ? ("in " + taskDataDeclStr) : "") << "\n"
1523              << "void main ()\n"
1524              << "{\n"
1525              << "    gl_PrimitiveCountNV = " << localSize << ";\n"
1526              << "\n"
1527              << triangleForPixel(pixel, len, baseIndex) << "}\n";
1528         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1529     }
1530 
1531     if (m_taskNeeded)
1532     {
1533         std::ostringstream task;
1534         task << "#version 460\n"
1535              << "#extension GL_NV_mesh_shader : enable\n"
1536              << "\n"
1537              << "layout (local_size_x=" << m_extent.width << ") in;\n"
1538              << "\n"
1539              << "out " << taskDataDeclStr << "\n"
1540              << "void main ()\n"
1541              << "{\n"
1542              << "    gl_TaskCountNV = " << m_extent.width << ";\n"
1543              << "    td.indexNumber[" << localIndex << "] = " << localIndex << ";\n"
1544              << "    td.size = gl_WorkGroupSize.x;\n"
1545              << "}\n";
1546         programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1547     }
1548 
1549     // Basic fragment shader.
1550     {
1551         const auto frag = getBasicFragShader();
1552         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1553     }
1554 }
1555 
createInstance(Context & context) const1556 TestInstance *LocalInvocationCase::createInstance(Context &context) const
1557 {
1558     // Must match the shader.
1559     const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1560     const IterationParams iterationParams = {
1561         m_extent,                 // VkExtent2D colorExtent;
1562         1u,                       // uint32_t numLayers;
1563         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
1564         false,                    // bool indirect;
1565         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1566         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1567     };
1568     return new FullScreenColorInstance(context, iterationParams, expectedColors);
1569 }
1570 
1571 // GlobalInvocationId builtin case.
1572 class GlobalInvocationIdCase : public MeshShaderBuiltinCase
1573 {
1574 public:
GlobalInvocationIdCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)1575     GlobalInvocationIdCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
1576         : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1577         , m_jobSize(getLargeJobSize())
1578         , m_extent{m_jobSize.numTasks * m_jobSize.localSize, 1u}
1579     {
1580     }
~GlobalInvocationIdCase(void)1581     virtual ~GlobalInvocationIdCase(void)
1582     {
1583     }
1584 
1585     void initPrograms(vk::SourceCollections &programCollection) const override;
1586     TestInstance *createInstance(Context &context) const override;
1587 
1588 protected:
1589     const JobSize m_jobSize;
1590     const VkExtent2D m_extent;
1591 };
1592 
initPrograms(vk::SourceCollections & programCollection) const1593 void GlobalInvocationIdCase::initPrograms(vk::SourceCollections &programCollection) const
1594 {
1595     const auto &localSize = m_jobSize.localSize;
1596 
1597     // Task data.
1598     std::ostringstream taskDataDecl;
1599     taskDataDecl << "taskNV TaskData {\n"
1600                  << "    uint pixelId[" << localSize << "];\n"
1601                  << "    uint size;\n"
1602                  << "} td;\n";
1603     const auto taskDataDeclStr = taskDataDecl.str();
1604 
1605     // Mesh shader: each work group fills one pixel.
1606     {
1607         const std::string pixel = (m_taskNeeded ? "td.pixelId[gl_LocalInvocationIndex]" : "gl_GlobalInvocationID.x");
1608         const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1609         const std::string baseIndex = "(gl_LocalInvocationIndex * 3u)";
1610         const auto maxVert          = localSize * 3u;
1611 
1612         std::ostringstream mesh;
1613         mesh << "#version 460\n"
1614              << "#extension GL_NV_mesh_shader : enable\n"
1615              << "\n"
1616              << "layout (local_size_x=" << localSize << ") in;\n"
1617              << "layout (triangles) out;\n"
1618              << "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1619              << "\n"
1620              << (m_taskNeeded ? ("in " + taskDataDeclStr) : "") << "\n"
1621              << "void main ()\n"
1622              << "{\n"
1623              << "    gl_PrimitiveCountNV = " << localSize << ";\n"
1624              << "\n"
1625              << triangleForPixel(pixel, len, baseIndex) << "}\n";
1626         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1627     }
1628 
1629     if (m_taskNeeded)
1630     {
1631         std::ostringstream task;
1632         task << "#version 460\n"
1633              << "#extension GL_NV_mesh_shader : enable\n"
1634              << "\n"
1635              << "layout (local_size_x=" << localSize << ") in;\n"
1636              << "\n"
1637              << "out " << taskDataDeclStr << "\n"
1638              << "void main ()\n"
1639              << "{\n"
1640              << "    gl_TaskCountNV = 1;\n"
1641              << "    td.pixelId[gl_LocalInvocationIndex] = gl_GlobalInvocationID.x;\n"
1642              << "    td.size = " << m_extent.width << ";\n"
1643              << "}\n";
1644         programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1645     }
1646 
1647     // Basic fragment shader.
1648     {
1649         const auto frag = getBasicFragShader();
1650         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1651     }
1652 }
1653 
createInstance(Context & context) const1654 TestInstance *GlobalInvocationIdCase::createInstance(Context &context) const
1655 {
1656     // Must match the shader.
1657     const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1658     const IterationParams iterationParams = {
1659         m_extent,                                   // VkExtent2D colorExtent;
1660         1u,                                         // uint32_t numLayers;
1661         getDefaultDrawCommands(m_jobSize.numTasks), // DrawCommandVec drawArgs;
1662         false,                                      // bool indirect;
1663         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1664         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1665     };
1666     return new FullScreenColorInstance(context, iterationParams, expectedColors);
1667 }
1668 
1669 // DrawIndex builtin case.
1670 class DrawIndexCase : public MeshShaderBuiltinCase
1671 {
1672 public:
DrawIndexCase(tcu::TestContext & testCtx,const std::string & name,bool taskNeeded)1673     DrawIndexCase(tcu::TestContext &testCtx, const std::string &name, bool taskNeeded)
1674         : MeshShaderBuiltinCase(testCtx, name, taskNeeded)
1675         , m_extent(getLinearExtent())
1676     {
1677     }
~DrawIndexCase(void)1678     virtual ~DrawIndexCase(void)
1679     {
1680     }
1681 
1682     void initPrograms(vk::SourceCollections &programCollection) const override;
1683     TestInstance *createInstance(Context &context) const override;
1684 
1685 protected:
1686     const VkExtent2D m_extent;
1687 };
1688 
initPrograms(vk::SourceCollections & programCollection) const1689 void DrawIndexCase::initPrograms(vk::SourceCollections &programCollection) const
1690 {
1691     const std::string taskDataDecl = "taskNV TaskData {\n"
1692                                      "    uint id;\n"
1693                                      "    uint size;\n"
1694                                      "} td;\n";
1695 
1696     const auto drawIndex = "uint(gl_DrawID)";
1697 
1698     // Mesh shader: each work group fills one pixel.
1699     {
1700         const std::string pixel = (m_taskNeeded ? "td.id" : drawIndex);
1701         const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1702 
1703         std::ostringstream mesh;
1704         mesh << "#version 460\n"
1705              << "#extension GL_NV_mesh_shader : enable\n"
1706              << "\n"
1707              << "layout (local_size_x=1) in;\n"
1708              << "layout (triangles) out;\n"
1709              << "layout (max_vertices=3, max_primitives=1) out;\n"
1710              << "\n"
1711              << (m_taskNeeded ? ("in " + taskDataDecl) : "") << "\n"
1712              << "void main ()\n"
1713              << "{\n"
1714              << "    gl_PrimitiveCountNV = 1u;\n"
1715              << "\n"
1716              << triangleForPixel(pixel, len, "0") << "}\n";
1717         programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1718     }
1719 
1720     if (m_taskNeeded)
1721     {
1722         std::ostringstream task;
1723         task << "#version 460\n"
1724              << "#extension GL_NV_mesh_shader : enable\n"
1725              << "\n"
1726              << "layout (local_size_x=1) in;\n"
1727              << "\n"
1728              << "out " << taskDataDecl << "\n"
1729              << "void main ()\n"
1730              << "{\n"
1731              << "    gl_TaskCountNV = 1u;\n"
1732              << "    td.id          = " << drawIndex << ";\n"
1733              << "    td.size        = " << m_extent.width << ";\n"
1734              << "}\n";
1735         programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1736     }
1737 
1738     // Basic fragment shader.
1739     {
1740         const auto frag = getBasicFragShader();
1741         programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1742     }
1743 }
1744 
createInstance(Context & context) const1745 TestInstance *DrawIndexCase::createInstance(Context &context) const
1746 {
1747     // Must match the shader.
1748     const ColorVec expectedColors(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1749     const DrawCommandVec commands(m_extent.width, makeDrawMeshTasksIndirectCommandNV(1u, 0u));
1750     const IterationParams iterationParams = {
1751         m_extent,     // VkExtent2D colorExtent;
1752         1u,           // uint32_t numLayers;
1753         commands,     // DrawCommandVec drawArgs;
1754         true,         // bool indirect;
1755         {},           //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
1756         tcu::Nothing, // tcu::Maybe<FragmentSize> fragmentSize;
1757     };
1758     return new FullScreenColorInstance(context, iterationParams, expectedColors);
1759 }
1760 
1761 // Primitive Shading Rate case.
1762 class PrimitiveShadingRateCase : public MeshShaderBuiltinCase
1763 {
1764 public:
PrimitiveShadingRateCase(tcu::TestContext & testCtx,const std::string & name,FragmentSize topSize,FragmentSize bottomSize)1765     PrimitiveShadingRateCase(tcu::TestContext &testCtx, const std::string &name, FragmentSize topSize,
1766                              FragmentSize bottomSize)
1767         : MeshShaderBuiltinCase(testCtx, name, false /*taskNeeded*/)
1768         , m_topSize(topSize)
1769         , m_bottomSize(bottomSize)
1770     {
1771     }
~PrimitiveShadingRateCase(void)1772     virtual ~PrimitiveShadingRateCase(void)
1773     {
1774     }
1775 
1776     void initPrograms(vk::SourceCollections &programCollection) const override;
1777     void checkSupport(Context &context) const override;
1778     TestInstance *createInstance(Context &context) const override;
1779 
1780 protected:
1781     const FragmentSize m_topSize;
1782     const FragmentSize m_bottomSize;
1783 };
1784 
initPrograms(vk::SourceCollections & programCollection) const1785 void PrimitiveShadingRateCase::initPrograms(vk::SourceCollections &programCollection) const
1786 {
1787     // Shading rate masks to use.
1788     const auto topMask       = getGLSLShadingRateMask(m_topSize);
1789     const auto bottomMask    = getGLSLShadingRateMask(m_bottomSize);
1790     const auto topMaskVal    = getSPVShadingRateValue(m_topSize);
1791     const auto bottomMaskVal = getSPVShadingRateValue(m_bottomSize);
1792 
1793     // Mesh shader.
1794     {
1795         // Similar to the GLSL code below if glslang accepted it.
1796         // Top quad with two triangles and bottom quad with two triangles.
1797         // One shading rate mask each.
1798 #if 0
1799 #version 460
1800 #extension GL_NV_mesh_shader : enable
1801 #extension GL_EXT_fragment_shading_rate : enable
1802 
1803         layout (local_size_x=1) in;
1804         layout (triangles) out;
1805         layout (max_vertices=6, max_primitives=4) out;
1806 
1807         perprimitiveNV out gl_MeshPerPrimitiveNV {
1808             int gl_PrimitiveShadingRateEXT;
1809         } gl_MeshPrimitivesNV[];
1810 
1811         void main ()
1812         {
1813             gl_PrimitiveCountNV = 4u;
1814 
1815             const vec4 topLeft  = vec4(-1.0, -1.0, 0.0, 1.0);
1816             const vec4 midLeft  = vec4(-1.0,  0.0, 0.0, 1.0);
1817             const vec4 botLeft  = vec4(-1.0,  1.0, 0.0, 1.0);
1818 
1819             const vec4 topRight = vec4( 1.0, -1.0, 0.0, 1.0);
1820             const vec4 midRight = vec4( 1.0,  0.0, 0.0, 1.0);
1821             const vec4 botRight = vec4( 1.0,  1.0, 0.0, 1.0);
1822 
1823             gl_MeshVerticesNV[0].gl_Position = topLeft;
1824             gl_MeshVerticesNV[1].gl_Position = midLeft;
1825             gl_MeshVerticesNV[2].gl_Position = botLeft;
1826 
1827             gl_MeshVerticesNV[3].gl_Position = topRight;
1828             gl_MeshVerticesNV[4].gl_Position = midRight;
1829             gl_MeshVerticesNV[5].gl_Position = botRight;
1830 
1831             gl_PrimitiveIndicesNV[0]  = 0u;
1832             gl_PrimitiveIndicesNV[1]  = 1u;
1833             gl_PrimitiveIndicesNV[2]  = 3u;
1834             gl_PrimitiveIndicesNV[3]  = 1u;
1835             gl_PrimitiveIndicesNV[4]  = 4u;
1836             gl_PrimitiveIndicesNV[5]  = 3u;
1837             gl_PrimitiveIndicesNV[6]  = 1u;
1838             gl_PrimitiveIndicesNV[7]  = 2u;
1839             gl_PrimitiveIndicesNV[8]  = 4u;
1840             gl_PrimitiveIndicesNV[9]  = 2u;
1841             gl_PrimitiveIndicesNV[10] = 5u;
1842             gl_PrimitiveIndicesNV[11] = 4u;
1843 
1844             gl_MeshPrimitivesNV[0].gl_PrimitiveShadingRateEXT = TOP_MASK;
1845             gl_MeshPrimitivesNV[1].gl_PrimitiveShadingRateEXT = TOP_MASK;
1846             gl_MeshPrimitivesNV[2].gl_PrimitiveShadingRateEXT = BOTTOM_MASK;
1847             gl_MeshPrimitivesNV[3].gl_PrimitiveShadingRateEXT = BOTTOM_MASK;
1848         }
1849 #endif
1850         std::ostringstream meshSPV;
1851         meshSPV << "; SPIR-V\n"
1852                 << "; Version: 1.0\n"
1853                 << "; Generator: Khronos Glslang Reference Front End; 10\n"
1854                 << "; Bound: 81\n"
1855                 << "; Schema: 0\n"
1856                 << "      OpCapability MeshShadingNV\n"
1857                 << "      OpCapability FragmentShadingRateKHR\n" // Added manually.
1858                 << "      OpExtension \"SPV_NV_mesh_shader\"\n"
1859                 << "      OpExtension \"SPV_KHR_fragment_shading_rate\"\n" // Added manually.
1860                 << " %1 = OpExtInstImport \"GLSL.std.450\"\n"
1861                 << "      OpMemoryModel Logical GLSL450\n"
1862                 << "      OpEntryPoint MeshNV %4 \"main\" %8 %20 %47 %73\n"
1863                 << "      OpExecutionMode %4 LocalSize 1 1 1\n"
1864                 << "      OpExecutionMode %4 OutputVertices 6\n"
1865                 << "      OpExecutionMode %4 OutputPrimitivesNV 4\n"
1866                 << "      OpExecutionMode %4 OutputTrianglesNV\n"
1867                 << "      OpDecorate %8 BuiltIn PrimitiveCountNV\n"
1868                 << "      OpMemberDecorate %16 0 BuiltIn Position\n"
1869                 << "      OpMemberDecorate %16 1 BuiltIn PointSize\n"
1870                 << "      OpMemberDecorate %16 2 BuiltIn ClipDistance\n"
1871                 << "      OpMemberDecorate %16 3 BuiltIn CullDistance\n"
1872                 << "      OpMemberDecorate %16 4 PerViewNV\n"
1873                 << "      OpMemberDecorate %16 4 BuiltIn PositionPerViewNV\n"
1874                 << "      OpMemberDecorate %16 5 PerViewNV\n"
1875                 << "      OpMemberDecorate %16 5 BuiltIn ClipDistancePerViewNV\n"
1876                 << "      OpMemberDecorate %16 6 PerViewNV\n"
1877                 << "      OpMemberDecorate %16 6 BuiltIn CullDistancePerViewNV\n"
1878                 << "      OpDecorate %16 Block\n"
1879                 << "      OpDecorate %47 BuiltIn PrimitiveIndicesNV\n"
1880                 << "      OpMemberDecorate %70 0 PerPrimitiveNV\n"
1881                 << "      OpMemberDecorate %70 0 BuiltIn PrimitiveShadingRateKHR\n" // Replaced PrimitiveID with this.
1882                 << "      OpDecorate %70 Block\n"
1883                 << "      OpDecorate %80 BuiltIn WorkgroupSize\n"
1884                 << " %2 = OpTypeVoid\n"
1885                 << " %3 = OpTypeFunction %2\n"
1886                 << " %6 = OpTypeInt 32 0\n"
1887                 << " %7 = OpTypePointer Output %6\n"
1888                 << " %8 = OpVariable %7 Output\n"
1889                 << " %9 = OpConstant %6 4\n"
1890                 << "%10 = OpTypeFloat 32\n"
1891                 << "%11 = OpTypeVector %10 4\n"
1892                 << "%12 = OpConstant %6 1\n"
1893                 << "%13 = OpTypeArray %10 %12\n"
1894                 << "%14 = OpTypeArray %11 %9\n"
1895                 << "%15 = OpTypeArray %13 %9\n"
1896                 << "%16 = OpTypeStruct %11 %10 %13 %13 %14 %15 %15\n"
1897                 << "%17 = OpConstant %6 6\n"
1898                 << "%18 = OpTypeArray %16 %17\n"
1899                 << "%19 = OpTypePointer Output %18\n"
1900                 << "%20 = OpVariable %19 Output\n"
1901                 << "%21 = OpTypeInt 32 1\n"
1902                 << "%tm = OpConstant %21 " << topMaskVal << "\n"    // Added mask value line.
1903                 << "%bm = OpConstant %21 " << bottomMaskVal << "\n" // Ditto.
1904                 << "%22 = OpConstant %21 0\n"
1905                 << "%23 = OpConstant %10 -1\n"
1906                 << "%24 = OpConstant %10 0\n"
1907                 << "%25 = OpConstant %10 1\n"
1908                 << "%26 = OpConstantComposite %11 %23 %23 %24 %25\n"
1909                 << "%27 = OpTypePointer Output %11\n"
1910                 << "%29 = OpConstant %21 1\n"
1911                 << "%30 = OpConstantComposite %11 %23 %24 %24 %25\n"
1912                 << "%32 = OpConstant %21 2\n"
1913                 << "%33 = OpConstantComposite %11 %23 %25 %24 %25\n"
1914                 << "%35 = OpConstant %21 3\n"
1915                 << "%36 = OpConstantComposite %11 %25 %23 %24 %25\n"
1916                 << "%38 = OpConstant %21 4\n"
1917                 << "%39 = OpConstantComposite %11 %25 %24 %24 %25\n"
1918                 << "%41 = OpConstant %21 5\n"
1919                 << "%42 = OpConstantComposite %11 %25 %25 %24 %25\n"
1920                 << "%44 = OpConstant %6 12\n"
1921                 << "%45 = OpTypeArray %6 %44\n"
1922                 << "%46 = OpTypePointer Output %45\n"
1923                 << "%47 = OpVariable %46 Output\n"
1924                 << "%48 = OpConstant %6 0\n"
1925                 << "%51 = OpConstant %6 3\n"
1926                 << "%56 = OpConstant %21 6\n"
1927                 << "%58 = OpConstant %21 7\n"
1928                 << "%59 = OpConstant %6 2\n"
1929                 << "%61 = OpConstant %21 8\n"
1930                 << "%63 = OpConstant %21 9\n"
1931                 << "%65 = OpConstant %21 10\n"
1932                 << "%66 = OpConstant %6 5\n"
1933                 << "%68 = OpConstant %21 11\n"
1934                 << "%70 = OpTypeStruct %21\n"
1935                 << "%71 = OpTypeArray %70 %9\n"
1936                 << "%72 = OpTypePointer Output %71\n"
1937                 << "%73 = OpVariable %72 Output\n"
1938                 << "%74 = OpTypePointer Output %21\n"
1939                 << "%79 = OpTypeVector %6 3\n"
1940                 << "%80 = OpConstantComposite %79 %12 %12 %12\n"
1941                 << " %4 = OpFunction %2 None %3\n"
1942                 << " %5 = OpLabel\n"
1943                 << "      OpStore %8 %9\n"
1944                 << "%28 = OpAccessChain %27 %20 %22 %22\n"
1945                 << "      OpStore %28 %26\n"
1946                 << "%31 = OpAccessChain %27 %20 %29 %22\n"
1947                 << "      OpStore %31 %30\n"
1948                 << "%34 = OpAccessChain %27 %20 %32 %22\n"
1949                 << "      OpStore %34 %33\n"
1950                 << "%37 = OpAccessChain %27 %20 %35 %22\n"
1951                 << "      OpStore %37 %36\n"
1952                 << "%40 = OpAccessChain %27 %20 %38 %22\n"
1953                 << "      OpStore %40 %39\n"
1954                 << "%43 = OpAccessChain %27 %20 %41 %22\n"
1955                 << "      OpStore %43 %42\n"
1956                 << "%49 = OpAccessChain %7 %47 %22\n"
1957                 << "      OpStore %49 %48\n"
1958                 << "%50 = OpAccessChain %7 %47 %29\n"
1959                 << "      OpStore %50 %12\n"
1960                 << "%52 = OpAccessChain %7 %47 %32\n"
1961                 << "      OpStore %52 %51\n"
1962                 << "%53 = OpAccessChain %7 %47 %35\n"
1963                 << "      OpStore %53 %12\n"
1964                 << "%54 = OpAccessChain %7 %47 %38\n"
1965                 << "      OpStore %54 %9\n"
1966                 << "%55 = OpAccessChain %7 %47 %41\n"
1967                 << "      OpStore %55 %51\n"
1968                 << "%57 = OpAccessChain %7 %47 %56\n"
1969                 << "      OpStore %57 %12\n"
1970                 << "%60 = OpAccessChain %7 %47 %58\n"
1971                 << "      OpStore %60 %59\n"
1972                 << "%62 = OpAccessChain %7 %47 %61\n"
1973                 << "      OpStore %62 %9\n"
1974                 << "%64 = OpAccessChain %7 %47 %63\n"
1975                 << "      OpStore %64 %59\n"
1976                 << "%67 = OpAccessChain %7 %47 %65\n"
1977                 << "      OpStore %67 %66\n"
1978                 << "%69 = OpAccessChain %7 %47 %68\n"
1979                 << "      OpStore %69 %9\n"
1980                 << "%75 = OpAccessChain %74 %73 %22 %22\n"
1981                 << "      OpStore %75 %tm\n" // Store the proper mask here. First triangle, top primitive.
1982                 << "%76 = OpAccessChain %74 %73 %29 %22\n"
1983                 << "      OpStore %76 %tm\n" // Second triangle, top primitive.
1984                 << "%77 = OpAccessChain %74 %73 %32 %22\n"
1985                 << "      OpStore %77 %bm\n" // Third triangle, bottom primitive.
1986                 << "%78 = OpAccessChain %74 %73 %35 %22\n"
1987                 << "      OpStore %78 %bm\n" // Fourth triangle, bottom primitive.
1988                 << "      OpReturn\n"
1989                 << "      OpFunctionEnd\n";
1990         programCollection.spirvAsmSources.add("mesh") << meshSPV.str();
1991     }
1992 
1993     // Frag shader.
1994     {
1995         const auto extent     = getDefaultExtent();
1996         const auto halfHeight = static_cast<float>(extent.height) / 2.0f;
1997 
1998         std::ostringstream frag;
1999         frag << "#version 460\n"
2000              << "#extension GL_NV_mesh_shader : enable\n"
2001              << "#extension GL_EXT_fragment_shading_rate : enable\n"
2002              << "\n"
2003              << "layout (location=0) out vec4 outColor;\n"
2004              << "\n"
2005              << "void main ()\n"
2006              << "{\n"
2007              // Checks the shading rate matches.
2008              << "    const int expectedRate = ((gl_FragCoord.y < " << halfHeight << ")? " << topMask << " : "
2009              << bottomMask << ");\n"
2010              << "    outColor = ((gl_ShadingRateEXT == expectedRate) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, "
2011                 "1.0));\n"
2012              << "}\n";
2013         programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
2014     }
2015 }
2016 
checkSupport(Context & context) const2017 void PrimitiveShadingRateCase::checkSupport(Context &context) const
2018 {
2019     MeshShaderBuiltinCase::checkSupport(context);
2020 
2021     context.requireDeviceFunctionality("VK_KHR_fragment_shading_rate");
2022 }
2023 
createInstance(Context & context) const2024 TestInstance *PrimitiveShadingRateCase::createInstance(Context &context) const
2025 {
2026     const ColorVec expectedColors(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
2027     FragmentSizeVector fsInUse{m_topSize, m_bottomSize};
2028     const IterationParams iterationParams = {
2029         getDefaultExtent(),       // VkExtent2D colorExtent;
2030         1u,                       // uint32_t numLayers;
2031         getDefaultDrawCommands(), // DrawCommandVec drawArgs;
2032         false,                    // bool indirect;
2033         {}, //    ViewportVec                    viewports;    // If empty, a single default viewport is used.
2034         tcu::just(getBadShadingRateSize(begin(fsInUse), end(fsInUse))), // tcu::Maybe<FragmentSize> fragmentSize;
2035     };
2036     return new FullScreenColorInstance(context, iterationParams, expectedColors);
2037 }
2038 
2039 } // namespace
2040 
createMeshShaderBuiltinTests(tcu::TestContext & testCtx)2041 tcu::TestCaseGroup *createMeshShaderBuiltinTests(tcu::TestContext &testCtx)
2042 {
2043     GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "builtin"));
2044 
2045     mainGroup->addChild(new PositionCase(testCtx, "position"));
2046     mainGroup->addChild(new PointSizeCase(testCtx, "point_size"));
2047     mainGroup->addChild(new ClipDistanceCase(testCtx, "clip_distance"));
2048     mainGroup->addChild(new CullDistanceCase(testCtx, "cull_distance"));
2049     mainGroup->addChild(new PrimitiveIdCase(testCtx, "primitive_id_glsl", true /*glslFrag*/));
2050     mainGroup->addChild(new PrimitiveIdCase(testCtx, "primitive_id_spirv", false /*glslFrag*/));
2051     mainGroup->addChild(new LayerCase(testCtx, "layer", false /*shareVertices*/));
2052     mainGroup->addChild(new LayerCase(testCtx, "layer_shared", true /*shareVertices*/));
2053     mainGroup->addChild(new ViewportIndexCase(testCtx, "viewport_index", false /*shareVertices*/));
2054     mainGroup->addChild(new ViewportIndexCase(testCtx, "viewport_index_shared", true /*shareVertices*/));
2055     mainGroup->addChild(new WorkGroupIdCase(testCtx, "work_group_id_in_mesh", false /*taskNeeded*/));
2056     mainGroup->addChild(new WorkGroupIdCase(testCtx, "work_group_id_in_task", true /*taskNeeded*/));
2057     mainGroup->addChild(
2058         new LocalInvocationCase(testCtx, "local_invocation_id_in_mesh", false /*taskNeeded*/, LocalInvocation::ID));
2059     mainGroup->addChild(
2060         new LocalInvocationCase(testCtx, "local_invocation_id_in_task", true /*taskNeeded*/, LocalInvocation::ID));
2061     mainGroup->addChild(new LocalInvocationCase(testCtx, "local_invocation_index_in_task", true /*taskNeeded*/,
2062                                                 LocalInvocation::INDEX));
2063     mainGroup->addChild(new LocalInvocationCase(testCtx, "local_invocation_index_in_mesh", false /*taskNeeded*/,
2064                                                 LocalInvocation::INDEX));
2065     mainGroup->addChild(new GlobalInvocationIdCase(testCtx, "global_invocation_id_in_mesh", false /*taskNeeded*/));
2066     mainGroup->addChild(new GlobalInvocationIdCase(testCtx, "global_invocation_id_in_task", true /*taskNeeded*/));
2067     mainGroup->addChild(new DrawIndexCase(testCtx, "draw_index_in_mesh", false /*taskNeeded*/));
2068     mainGroup->addChild(new DrawIndexCase(testCtx, "draw_index_in_task", true /*taskNeeded*/));
2069 
2070     // Primitive shading rate tests.
2071     {
2072         const auto sizeCount = static_cast<int>(FragmentSize::SIZE_COUNT);
2073 
2074         for (int i = 0; i < sizeCount; ++i)
2075             for (int j = 0; j < sizeCount; ++j)
2076             {
2077                 const auto topSize    = static_cast<FragmentSize>(i);
2078                 const auto bottomSize = static_cast<FragmentSize>(j);
2079 
2080                 const auto topExtent    = getShadingRateSize(topSize);
2081                 const auto bottomExtent = getShadingRateSize(bottomSize);
2082 
2083                 const auto testName = "primitive_shading_rate_" + std::to_string(topExtent.width) + "x" +
2084                                       std::to_string(topExtent.height) + "_" + std::to_string(bottomExtent.width) +
2085                                       "x" + std::to_string(bottomExtent.height);
2086 
2087                 mainGroup->addChild(new PrimitiveShadingRateCase(testCtx, testName, topSize, bottomSize));
2088             }
2089     }
2090 
2091     return mainGroup.release();
2092 }
2093 
2094 } // namespace MeshShader
2095 } // namespace vkt
2096