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 Property Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktMeshShaderPropertyTests.hpp"
26 #include "vktMeshShaderUtil.hpp"
27 #include "vktTestCase.hpp"
28 
29 #include "vkBufferWithMemory.hpp"
30 #include "vkObjUtil.hpp"
31 #include "vkBuilderUtil.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkCmdUtil.hpp"
34 #include "vkBarrierUtil.hpp"
35 
36 #include "tcuStringTemplate.hpp"
37 
38 #include <vector>
39 #include <string>
40 #include <map>
41 #include <sstream>
42 
43 namespace vkt
44 {
45 namespace MeshShader
46 {
47 
48 namespace
49 {
50 
51 using GroupPtr        = de::MovePtr<tcu::TestCaseGroup>;
52 using ReplacementsMap = std::map<std::string, std::string>;
53 
54 using namespace vk;
55 
getTaskShaderTemplate()56 tcu::StringTemplate getTaskShaderTemplate()
57 {
58     return tcu::StringTemplate("#version 460\n"
59                                "#extension GL_NV_mesh_shader : enable\n"
60                                "\n"
61                                "layout (local_size_x=${TASK_LOCAL_SIZE_X:default=1}) in;\n"
62                                "\n"
63                                "${TASK_GLOBAL_DECL:opt}"
64                                "\n"
65                                "${TASK_MESH_INTERFACE_OUT:opt}"
66                                "\n"
67                                "void main ()\n"
68                                "{\n"
69                                "    gl_TaskCountNV = ${TASK_TASK_COUNT:default=0};\n"
70                                "${TASK_BODY:opt}"
71                                "}\n");
72 }
73 
getMeshShaderTemplate()74 tcu::StringTemplate getMeshShaderTemplate()
75 {
76     return tcu::StringTemplate("#version 460\n"
77                                "#extension GL_NV_mesh_shader : enable\n"
78                                "\n"
79                                "layout (local_size_x=${MESH_LOCAL_SIZE_X:default=1}) in;\n"
80                                "layout (triangles) out;\n"
81                                "layout (max_vertices=3, max_primitives=1) out;\n"
82                                "\n"
83                                "${MESH_GLOBAL_DECL:opt}"
84                                "\n"
85                                "${TASK_MESH_INTERFACE_IN:opt}"
86                                "\n"
87                                "void main ()\n"
88                                "{\n"
89                                "    gl_PrimitiveCountNV = 0u;\n"
90                                "${MESH_BODY:opt}"
91                                "}\n");
92 }
93 
getCommonStorageBufferDecl()94 std::string getCommonStorageBufferDecl()
95 {
96     return "layout (set=0, binding=0) buffer OutputBlock { uint values[]; } ov;\n";
97 }
98 
genericCheckSupport(Context & context,bool taskShaderNeeded)99 void genericCheckSupport(Context &context, bool taskShaderNeeded)
100 {
101     checkTaskMeshShaderSupportNV(context, taskShaderNeeded, true);
102 
103     context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
104 }
105 
106 struct InstanceParams
107 {
108     uint32_t bufferElements;
109     uint32_t taskCount;
110 };
111 
112 class MeshShaderPropertyInstance : public vkt::TestInstance
113 {
114 public:
MeshShaderPropertyInstance(Context & context,const InstanceParams & params)115     MeshShaderPropertyInstance(Context &context, const InstanceParams &params)
116         : vkt::TestInstance(context)
117         , m_params(params)
118     {
119     }
~MeshShaderPropertyInstance(void)120     virtual ~MeshShaderPropertyInstance(void)
121     {
122     }
123 
124     tcu::TestStatus iterate() override;
125 
126 protected:
127     InstanceParams m_params;
128 };
129 
iterate()130 tcu::TestStatus MeshShaderPropertyInstance::iterate()
131 {
132     const auto &vkd       = m_context.getDeviceInterface();
133     const auto device     = m_context.getDevice();
134     auto &alloc           = m_context.getDefaultAllocator();
135     const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
136     const auto queue      = m_context.getUniversalQueue();
137     const auto &binaries  = m_context.getBinaryCollection();
138     const auto extent     = makeExtent3D(1u, 1u, 1u);
139     const auto bindPoint  = VK_PIPELINE_BIND_POINT_GRAPHICS;
140     const auto useTask    = binaries.contains("task");
141 
142     const auto storageBufferSize =
143         static_cast<VkDeviceSize>(m_params.bufferElements) * static_cast<VkDeviceSize>(sizeof(uint32_t));
144     const auto storageBufferUsage  = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
145     const auto storageBufferType   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
146     const auto storageBufferStages = (VK_SHADER_STAGE_MESH_BIT_NV | (useTask ? VK_SHADER_STAGE_TASK_BIT_NV : 0));
147 
148     // Create storage buffer with the required space.
149     const auto storageBufferInfo = makeBufferCreateInfo(storageBufferSize, storageBufferUsage);
150     BufferWithMemory storageBuffer(vkd, device, alloc, storageBufferInfo, MemoryRequirement::HostVisible);
151     auto &storageBufferAlloc         = storageBuffer.getAllocation();
152     void *storageBufferDataPtr       = storageBufferAlloc.getHostPtr();
153     const auto storageBufferDescInfo = makeDescriptorBufferInfo(storageBuffer.get(), 0ull, storageBufferSize);
154 
155     deMemset(storageBufferDataPtr, 0xFF, static_cast<size_t>(storageBufferSize));
156     flushAlloc(vkd, device, storageBufferAlloc);
157 
158     // Descriptor pool.
159     DescriptorPoolBuilder poolBuilder;
160     poolBuilder.addType(storageBufferType);
161     const auto descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
162 
163     // Descriptor set layout and pipeline layout.
164     DescriptorSetLayoutBuilder layoutBuilder;
165     layoutBuilder.addSingleBinding(storageBufferType, storageBufferStages);
166     const auto setLayout      = layoutBuilder.build(vkd, device);
167     const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
168 
169     // Allocate and prepare descriptor set.
170     const auto descriptorSet = makeDescriptorSet(vkd, device, descriptorPool.get(), setLayout.get());
171 
172     DescriptorSetUpdateBuilder setUpdateBuilder;
173     setUpdateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u),
174                                  storageBufferType, &storageBufferDescInfo);
175     setUpdateBuilder.update(vkd, device);
176 
177     // Create empty render pass and framebuffer.
178     const auto renderPass  = makeRenderPass(vkd, device);
179     const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), 0u, nullptr, extent.width, extent.height);
180 
181     // Shader modules and pipeline.
182     Move<VkShaderModule> taskModule;
183     Move<VkShaderModule> meshModule;
184     const Move<VkShaderModule> fragModule; // No fragment shader.
185 
186     if (useTask)
187         taskModule = createShaderModule(vkd, device, binaries.get("task"));
188     meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
189 
190     const std::vector<VkViewport> viewports(1u, makeViewport(extent));
191     const std::vector<VkRect2D> scissors(1u, makeRect2D(extent));
192 
193     const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(), taskModule.get(), meshModule.get(),
194                                                fragModule.get(), renderPass.get(), viewports, scissors);
195 
196     // Command pool and buffer.
197     const auto cmdPool      = makeCommandPool(vkd, device, queueIndex);
198     const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
199     const auto cmdBuffer    = cmdBufferPtr.get();
200 
201     // Run the pipeline.
202     beginCommandBuffer(vkd, cmdBuffer);
203 
204     beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0));
205     vkd.cmdBindDescriptorSets(cmdBuffer, bindPoint, pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
206     vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipeline.get());
207     vkd.cmdDrawMeshTasksNV(cmdBuffer, m_params.taskCount, 0u);
208     endRenderPass(vkd, cmdBuffer);
209 
210     const auto shaderToHostBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
211     vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u,
212                            &shaderToHostBarrier, 0u, nullptr, 0u, nullptr);
213 
214     endCommandBuffer(vkd, cmdBuffer);
215     submitCommandsAndWait(vkd, device, queue, cmdBuffer);
216 
217     // Verify the storage buffer has the expected results.
218     invalidateAlloc(vkd, device, storageBufferAlloc);
219 
220     std::vector<uint32_t> bufferData(m_params.bufferElements);
221     deMemcpy(bufferData.data(), storageBufferDataPtr, de::dataSize(bufferData));
222 
223     for (size_t idx = 0u; idx < bufferData.size(); ++idx)
224     {
225         const auto expected     = static_cast<uint32_t>(idx);
226         const auto &bufferValue = bufferData[idx];
227 
228         if (bufferValue != expected)
229             TCU_FAIL("Unexpected value found in buffer position " + de::toString(idx) + ": " +
230                      de::toString(bufferValue));
231     }
232 
233     return tcu::TestStatus::pass("Pass");
234 }
235 
236 class MaxDrawMeshTasksCountCase : public vkt::TestCase
237 {
238 public:
239     enum class TestType
240     {
241         TASK = 0,
242         MESH
243     };
244 
MaxDrawMeshTasksCountCase(tcu::TestContext & testCtx,const std::string & name,TestType testType)245     MaxDrawMeshTasksCountCase(tcu::TestContext &testCtx, const std::string &name, TestType testType)
246         : vkt::TestCase(testCtx, name)
247         , m_testType(testType)
248     {
249     }
~MaxDrawMeshTasksCountCase(void)250     virtual ~MaxDrawMeshTasksCountCase(void)
251     {
252     }
253 
254     void initPrograms(vk::SourceCollections &programCollection) const override;
255     TestInstance *createInstance(Context &context) const override;
256     void checkSupport(Context &context) const override;
257 
258     static constexpr uint32_t minLimit = 65535u;
259 
260 protected:
261     TestType m_testType;
262 };
263 
checkSupport(Context & context) const264 void MaxDrawMeshTasksCountCase::checkSupport(Context &context) const
265 {
266     genericCheckSupport(context, (m_testType == TestType::TASK));
267 
268     const auto &properties = context.getMeshShaderProperties();
269     if (properties.maxDrawMeshTasksCount < minLimit)
270         TCU_FAIL("maxDrawMeshTasksCount property below the minimum limit");
271 }
272 
createInstance(Context & context) const273 TestInstance *MaxDrawMeshTasksCountCase::createInstance(Context &context) const
274 {
275     const InstanceParams params = {
276         minLimit, // uint32_t bufferElements;
277         minLimit, // uint32_t taskCount;
278     };
279     return new MeshShaderPropertyInstance(context, params);
280 }
281 
initPrograms(vk::SourceCollections & programCollection) const282 void MaxDrawMeshTasksCountCase::initPrograms(vk::SourceCollections &programCollection) const
283 {
284     ReplacementsMap meshReplacements;
285     ReplacementsMap taskReplacements;
286 
287     const auto meshTemplate = getMeshShaderTemplate();
288 
289     const std::string desc = getCommonStorageBufferDecl();
290     const std::string body = "    ov.values[gl_WorkGroupID.x] = gl_WorkGroupID.x;\n";
291 
292     if (m_testType == TestType::TASK)
293     {
294         const auto taskTemplate              = getTaskShaderTemplate();
295         taskReplacements["TASK_GLOBAL_DECL"] = desc;
296         taskReplacements["TASK_BODY"]        = body;
297 
298         programCollection.glslSources.add("task") << glu::TaskSource(taskTemplate.specialize(taskReplacements));
299     }
300     else
301     {
302         meshReplacements["MESH_GLOBAL_DECL"] = desc;
303         meshReplacements["MESH_BODY"]        = body;
304     }
305 
306     programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
307 }
308 
309 class MaxTaskWorkGroupInvocationsCase : public vkt::TestCase
310 {
311 public:
MaxTaskWorkGroupInvocationsCase(tcu::TestContext & testCtx,const std::string & name)312     MaxTaskWorkGroupInvocationsCase(tcu::TestContext &testCtx, const std::string &name) : vkt::TestCase(testCtx, name)
313     {
314     }
~MaxTaskWorkGroupInvocationsCase(void)315     virtual ~MaxTaskWorkGroupInvocationsCase(void)
316     {
317     }
318 
319     void initPrograms(vk::SourceCollections &programCollection) const override;
320     TestInstance *createInstance(Context &context) const override;
321     void checkSupport(Context &context) const override;
322 
323     static constexpr uint32_t minLimit = 32u;
324 };
325 
checkSupport(Context & context) const326 void MaxTaskWorkGroupInvocationsCase::checkSupport(Context &context) const
327 {
328     genericCheckSupport(context, true /*taskShaderNeeded*/);
329 
330     const auto &properties = context.getMeshShaderProperties();
331     if (properties.maxTaskWorkGroupInvocations < minLimit)
332         TCU_FAIL("maxTaskWorkGroupInvocations property below the minimum limit");
333 }
334 
createInstance(Context & context) const335 TestInstance *MaxTaskWorkGroupInvocationsCase::createInstance(Context &context) const
336 {
337     const InstanceParams params = {
338         minLimit, // uint32_t bufferElements;
339         1u,       // uint32_t taskCount;
340     };
341     return new MeshShaderPropertyInstance(context, params);
342 }
343 
initPrograms(vk::SourceCollections & programCollection) const344 void MaxTaskWorkGroupInvocationsCase::initPrograms(vk::SourceCollections &programCollection) const
345 {
346     const ReplacementsMap meshReplacements;
347     const auto meshTemplate = getMeshShaderTemplate();
348 
349     programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
350 
351     ReplacementsMap taskReplacements;
352     const auto taskTemplate = getTaskShaderTemplate();
353 
354     taskReplacements["TASK_GLOBAL_DECL"]  = getCommonStorageBufferDecl();
355     taskReplacements["TASK_BODY"]         = "    ov.values[gl_LocalInvocationID.x] = gl_LocalInvocationID.x;\n";
356     taskReplacements["TASK_LOCAL_SIZE_X"] = de::toString(uint32_t{minLimit});
357 
358     programCollection.glslSources.add("task") << glu::TaskSource(taskTemplate.specialize(taskReplacements));
359 }
360 
361 // In the case of the NV extension, this is very similar to the test above. Added for completion.
362 class MaxTaskWorkGroupSizeCase : public MaxTaskWorkGroupInvocationsCase
363 {
364 public:
MaxTaskWorkGroupSizeCase(tcu::TestContext & testCtx,const std::string & name)365     MaxTaskWorkGroupSizeCase(tcu::TestContext &testCtx, const std::string &name)
366         : MaxTaskWorkGroupInvocationsCase(testCtx, name)
367     {
368     }
369 
370     void checkSupport(Context &context) const override;
371 
372     static constexpr uint32_t minSizeX = 32u;
373     static constexpr uint32_t minSizeY = 1u;
374     static constexpr uint32_t minSizeZ = 1u;
375 };
376 
checkSupport(Context & context) const377 void MaxTaskWorkGroupSizeCase::checkSupport(Context &context) const
378 {
379     genericCheckSupport(context, true /*taskShaderNeeded*/);
380 
381     const auto &properties = context.getMeshShaderProperties();
382     if (properties.maxTaskWorkGroupSize[0] < minSizeX || properties.maxTaskWorkGroupSize[1] < minSizeY ||
383         properties.maxTaskWorkGroupSize[2] < minSizeZ)
384     {
385         TCU_FAIL("maxTaskWorkGroupSize property below the minimum limit");
386     }
387 }
388 
389 class MaxTaskOutputCountCase : public vkt::TestCase
390 {
391 public:
MaxTaskOutputCountCase(tcu::TestContext & testCtx,const std::string & name)392     MaxTaskOutputCountCase(tcu::TestContext &testCtx, const std::string &name) : vkt::TestCase(testCtx, name)
393     {
394     }
~MaxTaskOutputCountCase(void)395     virtual ~MaxTaskOutputCountCase(void)
396     {
397     }
398 
399     void initPrograms(vk::SourceCollections &programCollection) const override;
400     TestInstance *createInstance(Context &context) const override;
401     void checkSupport(Context &context) const override;
402 
403     static constexpr uint32_t minLimit = 65535u;
404 };
405 
checkSupport(Context & context) const406 void MaxTaskOutputCountCase::checkSupport(Context &context) const
407 {
408     genericCheckSupport(context, true /*taskShaderNeeded*/);
409 
410     const auto &properties = context.getMeshShaderProperties();
411     if (properties.maxTaskOutputCount < minLimit)
412         TCU_FAIL("maxTaskOutputCount property below the minimum limit");
413 }
414 
createInstance(Context & context) const415 TestInstance *MaxTaskOutputCountCase::createInstance(Context &context) const
416 {
417     const InstanceParams params = {
418         minLimit, // uint32_t bufferElements;
419         1u,       // uint32_t taskCount;
420     };
421     return new MeshShaderPropertyInstance(context, params);
422 }
423 
initPrograms(vk::SourceCollections & programCollection) const424 void MaxTaskOutputCountCase::initPrograms(vk::SourceCollections &programCollection) const
425 {
426     ReplacementsMap meshReplacements;
427     ReplacementsMap taskReplacements;
428     const auto meshTemplate = getMeshShaderTemplate();
429     const auto taskTemplate = getTaskShaderTemplate();
430 
431     taskReplacements["TASK_TASK_COUNT"]  = de::toString(uint32_t{minLimit});
432     meshReplacements["MESH_GLOBAL_DECL"] = getCommonStorageBufferDecl();
433     meshReplacements["MESH_BODY"]        = "    ov.values[gl_WorkGroupID.x] = gl_WorkGroupID.x;\n";
434 
435     programCollection.glslSources.add("task") << glu::TaskSource(taskTemplate.specialize(taskReplacements));
436     programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
437 }
438 
439 class MaxMeshWorkGroupInvocationsCase : public vkt::TestCase
440 {
441 public:
MaxMeshWorkGroupInvocationsCase(tcu::TestContext & testCtx,const std::string & name)442     MaxMeshWorkGroupInvocationsCase(tcu::TestContext &testCtx, const std::string &name) : vkt::TestCase(testCtx, name)
443     {
444     }
~MaxMeshWorkGroupInvocationsCase(void)445     virtual ~MaxMeshWorkGroupInvocationsCase(void)
446     {
447     }
448 
449     void initPrograms(vk::SourceCollections &programCollection) const override;
450     TestInstance *createInstance(Context &context) const override;
451     void checkSupport(Context &context) const override;
452 
453     static constexpr uint32_t minLimit = 32u;
454 };
455 
checkSupport(Context & context) const456 void MaxMeshWorkGroupInvocationsCase::checkSupport(Context &context) const
457 {
458     genericCheckSupport(context, false /*taskShaderNeeded*/);
459 
460     const auto &properties = context.getMeshShaderProperties();
461     if (properties.maxMeshWorkGroupInvocations < minLimit)
462         TCU_FAIL("maxMeshWorkGroupInvocations property below the minimum limit");
463 }
464 
createInstance(Context & context) const465 TestInstance *MaxMeshWorkGroupInvocationsCase::createInstance(Context &context) const
466 {
467     const InstanceParams params = {
468         minLimit, // uint32_t bufferElements;
469         1u,       // uint32_t taskCount;
470     };
471     return new MeshShaderPropertyInstance(context, params);
472 }
473 
initPrograms(vk::SourceCollections & programCollection) const474 void MaxMeshWorkGroupInvocationsCase::initPrograms(vk::SourceCollections &programCollection) const
475 {
476     ReplacementsMap meshReplacements;
477     const auto meshTemplate = getMeshShaderTemplate();
478 
479     meshReplacements["MESH_LOCAL_SIZE_X"] = de::toString(uint32_t{minLimit});
480     meshReplacements["MESH_GLOBAL_DECL"]  = getCommonStorageBufferDecl();
481     meshReplacements["MESH_BODY"]         = "    ov.values[gl_LocalInvocationID.x] = gl_LocalInvocationID.x;\n";
482 
483     programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
484 }
485 
486 // In the case of the NV extension, this is very similar to the test above. Added for completion.
487 class MaxMeshWorkGroupSizeCase : public MaxMeshWorkGroupInvocationsCase
488 {
489 public:
MaxMeshWorkGroupSizeCase(tcu::TestContext & testCtx,const std::string & name)490     MaxMeshWorkGroupSizeCase(tcu::TestContext &testCtx, const std::string &name)
491         : MaxMeshWorkGroupInvocationsCase(testCtx, name)
492     {
493     }
494 
495     void checkSupport(Context &context) const override;
496 
497     static constexpr uint32_t minSizeX = 32u;
498     static constexpr uint32_t minSizeY = 1u;
499     static constexpr uint32_t minSizeZ = 1u;
500 };
501 
checkSupport(Context & context) const502 void MaxMeshWorkGroupSizeCase::checkSupport(Context &context) const
503 {
504     genericCheckSupport(context, false /*taskShaderNeeded*/);
505 
506     const auto &properties = context.getMeshShaderProperties();
507     if (properties.maxMeshWorkGroupSize[0] < minSizeX || properties.maxMeshWorkGroupSize[1] < minSizeY ||
508         properties.maxMeshWorkGroupSize[2] < minSizeZ)
509     {
510         TCU_FAIL("maxMeshWorkGroupSize property below the minimum limit");
511     }
512 }
513 
getSharedArrayDecl(uint32_t numElements)514 std::string getSharedArrayDecl(uint32_t numElements)
515 {
516     std::ostringstream decl;
517     decl << "const uint arrayElements = " << de::toString(numElements) << ";\n"
518          << "shared uint sharedArray[arrayElements];\n";
519     return decl.str();
520 }
521 
getSharedMemoryBody(uint32_t localSize)522 std::string getSharedMemoryBody(uint32_t localSize)
523 {
524     std::ostringstream body;
525     body
526         << "\n"
527         << "    if (gl_LocalInvocationID.x == 0u)\n"
528         << "    {\n"
529         << "        for (uint i = 0; i < arrayElements; ++i)\n"
530         << "            sharedArray[i] = 0u;\n"
531         << "    }\n"
532         << "\n"
533         << "    memoryBarrierShared();\n"
534         << "    barrier();\n"
535         << "\n"
536         << "    for (uint i = 0; i < arrayElements; ++i)\n"
537         << "        atomicAdd(sharedArray[i], 1u);\n"
538         << "\n"
539         << "    memoryBarrierShared();\n"
540         << "    barrier();\n"
541         << "\n"
542         << "    uint allGood = 1u;\n"
543         << "    for (uint i = 0; i < arrayElements; ++i)\n"
544         << "    {\n"
545         << "        if (sharedArray[i] != " << localSize << ")\n"
546         << "        {\n"
547         << "            allGood = 0u;\n"
548         << "            break;\n"
549         << "        }\n"
550         << "    }\n"
551         << "\n"
552         << "    ov.values[gl_LocalInvocationID.x] = ((allGood == 1u) ? gl_LocalInvocationID.x : gl_WorkGroupSize.x);\n";
553 
554     return body.str();
555 }
556 
557 class MaxTaskTotalMemorySizeCase : public vkt::TestCase
558 {
559 public:
MaxTaskTotalMemorySizeCase(tcu::TestContext & testCtx,const std::string & name)560     MaxTaskTotalMemorySizeCase(tcu::TestContext &testCtx, const std::string &name) : vkt::TestCase(testCtx, name)
561     {
562     }
~MaxTaskTotalMemorySizeCase(void)563     virtual ~MaxTaskTotalMemorySizeCase(void)
564     {
565     }
566 
567     void initPrograms(vk::SourceCollections &programCollection) const override;
568     TestInstance *createInstance(Context &context) const override;
569     void checkSupport(Context &context) const override;
570 
571     static constexpr uint32_t localSize = 32u;
572     static constexpr uint32_t minLimit  = 16384u;
573 };
574 
createInstance(Context & context) const575 TestInstance *MaxTaskTotalMemorySizeCase::createInstance(Context &context) const
576 {
577     const InstanceParams params = {
578         localSize, // uint32_t bufferElements;
579         1u,        // uint32_t taskCount;
580     };
581     return new MeshShaderPropertyInstance(context, params);
582 }
583 
checkSupport(Context & context) const584 void MaxTaskTotalMemorySizeCase::checkSupport(Context &context) const
585 {
586     genericCheckSupport(context, true /*taskShaderNeeded*/);
587 
588     const auto &properties = context.getMeshShaderProperties();
589     if (properties.maxTaskTotalMemorySize < minLimit)
590         TCU_FAIL("maxTaskTotalMemorySize property below the minimum limit");
591 }
592 
initPrograms(vk::SourceCollections & programCollection) const593 void MaxTaskTotalMemorySizeCase::initPrograms(vk::SourceCollections &programCollection) const
594 {
595     const ReplacementsMap meshReplacements;
596     const auto meshTemplate = getMeshShaderTemplate();
597 
598     programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
599 
600     const auto taskTemplate  = getTaskShaderTemplate();
601     const auto arrayElements = minLimit / static_cast<uint32_t>(sizeof(uint32_t));
602 
603     const auto globalDecls = getCommonStorageBufferDecl() + getSharedArrayDecl(arrayElements);
604     const auto body        = getSharedMemoryBody(localSize);
605 
606     ReplacementsMap taskReplacements;
607     taskReplacements["TASK_LOCAL_SIZE_X"] = de::toString(uint32_t{localSize});
608     taskReplacements["TASK_GLOBAL_DECL"]  = globalDecls;
609     taskReplacements["TASK_BODY"]         = body;
610 
611     programCollection.glslSources.add("task") << glu::TaskSource(taskTemplate.specialize(taskReplacements));
612 }
613 
614 // Very similar to the previous one in NV.
615 class MaxMeshTotalMemorySizeCase : public vkt::TestCase
616 {
617 public:
MaxMeshTotalMemorySizeCase(tcu::TestContext & testCtx,const std::string & name)618     MaxMeshTotalMemorySizeCase(tcu::TestContext &testCtx, const std::string &name) : vkt::TestCase(testCtx, name)
619     {
620     }
~MaxMeshTotalMemorySizeCase(void)621     virtual ~MaxMeshTotalMemorySizeCase(void)
622     {
623     }
624 
625     void initPrograms(vk::SourceCollections &programCollection) const override;
626     TestInstance *createInstance(Context &context) const override;
627     void checkSupport(Context &context) const override;
628 
629     static constexpr uint32_t localSize = 32u;
630     static constexpr uint32_t minLimit  = 16384u;
631 };
632 
createInstance(Context & context) const633 TestInstance *MaxMeshTotalMemorySizeCase::createInstance(Context &context) const
634 {
635     const InstanceParams params = {
636         localSize, // uint32_t bufferElements;
637         1u,        // uint32_t taskCount;
638     };
639     return new MeshShaderPropertyInstance(context, params);
640 }
641 
checkSupport(Context & context) const642 void MaxMeshTotalMemorySizeCase::checkSupport(Context &context) const
643 {
644     genericCheckSupport(context, false /*taskShaderNeeded*/);
645 
646     const auto &properties = context.getMeshShaderProperties();
647     if (properties.maxMeshTotalMemorySize < minLimit)
648         TCU_FAIL("maxMeshTotalMemorySize property below the minimum limit");
649 }
650 
initPrograms(vk::SourceCollections & programCollection) const651 void MaxMeshTotalMemorySizeCase::initPrograms(vk::SourceCollections &programCollection) const
652 {
653     const auto meshTemplate  = getMeshShaderTemplate();
654     const auto arrayElements = minLimit / static_cast<uint32_t>(sizeof(uint32_t));
655 
656     const auto globalDecls = getCommonStorageBufferDecl() + getSharedArrayDecl(arrayElements);
657     const auto body        = getSharedMemoryBody(localSize);
658 
659     ReplacementsMap meshReplacements;
660     meshReplacements["MESH_LOCAL_SIZE_X"] = de::toString(uint32_t{localSize});
661     meshReplacements["MESH_GLOBAL_DECL"]  = globalDecls;
662     meshReplacements["MESH_BODY"]         = body;
663 
664     programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
665 }
666 
667 } // namespace
668 
createMeshShaderPropertyTests(tcu::TestContext & testCtx)669 tcu::TestCaseGroup *createMeshShaderPropertyTests(tcu::TestContext &testCtx)
670 {
671     GroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "property"));
672 
673     mainGroup->addChild(new MaxDrawMeshTasksCountCase(testCtx, "max_draw_mesh_tasks_count_with_task",
674                                                       MaxDrawMeshTasksCountCase::TestType::TASK));
675     mainGroup->addChild(new MaxDrawMeshTasksCountCase(testCtx, "max_draw_mesh_tasks_count_with_mesh",
676                                                       MaxDrawMeshTasksCountCase::TestType::MESH));
677     mainGroup->addChild(new MaxTaskWorkGroupInvocationsCase(testCtx, "max_task_work_group_invocations"));
678     mainGroup->addChild(new MaxTaskWorkGroupSizeCase(testCtx, "max_task_work_group_size"));
679     mainGroup->addChild(new MaxTaskOutputCountCase(testCtx, "max_task_output_count"));
680     mainGroup->addChild(new MaxMeshWorkGroupInvocationsCase(testCtx, "max_mesh_work_group_invocations"));
681     mainGroup->addChild(new MaxMeshWorkGroupSizeCase(testCtx, "max_mesh_work_group_size"));
682     mainGroup->addChild(new MaxTaskTotalMemorySizeCase(testCtx, "max_task_total_memory_size"));
683     mainGroup->addChild(new MaxMeshTotalMemorySizeCase(testCtx, "max_mesh_total_memory_size"));
684 
685     return mainGroup.release();
686 }
687 
688 } // namespace MeshShader
689 } // namespace vkt
690