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