1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2019 The Khronos Group Inc.
6  * Copyright (c) 2019 Google Inc.
7  * Copyright (c) 2017 Codeplay Software Ltd.
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  */ /*!
22  * \file
23  * \brief Subgroups Tests
24  */ /*--------------------------------------------------------------------*/
25 
26 #include "vktSubgroupsBallotBroadcastTests.hpp"
27 #include "vktSubgroupsTestsUtils.hpp"
28 
29 #include <string>
30 #include <vector>
31 
32 using namespace tcu;
33 using namespace std;
34 using namespace vk;
35 using namespace vkt;
36 
37 namespace
38 {
39 enum OpType
40 {
41     OPTYPE_BROADCAST = 0,
42     OPTYPE_BROADCAST_NONCONST,
43     OPTYPE_BROADCAST_FIRST,
44     OPTYPE_LAST
45 };
46 
47 struct CaseDefinition
48 {
49     OpType opType;
50     VkShaderStageFlags shaderStage;
51     VkFormat format;
52     de::SharedPtr<bool> geometryPointSizeSupported;
53     bool extShaderSubGroupBallotTests;
54     bool subgroupSizeControl;
55     uint32_t requiredSubgroupSize;
56     bool requires8BitUniformBuffer;
57     bool requires16BitUniformBuffer;
58 };
59 
checkVertexPipelineStages(const void * internalData,vector<const void * > datas,uint32_t width,uint32_t)60 bool checkVertexPipelineStages(const void *internalData, vector<const void *> datas, uint32_t width, uint32_t)
61 {
62     DE_UNREF(internalData);
63 
64     return subgroups::check(datas, width, 3);
65 }
66 
checkComputeOrMesh(const void * internalData,vector<const void * > datas,const uint32_t numWorkgroups[3],const uint32_t localSize[3],uint32_t)67 bool checkComputeOrMesh(const void *internalData, vector<const void *> datas, const uint32_t numWorkgroups[3],
68                         const uint32_t localSize[3], uint32_t)
69 {
70     DE_UNREF(internalData);
71 
72     return subgroups::checkComputeOrMesh(datas, numWorkgroups, localSize, 3);
73 }
74 
getOpTypeCaseName(OpType opType)75 string getOpTypeCaseName(OpType opType)
76 {
77     switch (opType)
78     {
79     case OPTYPE_BROADCAST:
80         return "subgroupbroadcast";
81     case OPTYPE_BROADCAST_NONCONST:
82         return "subgroupbroadcast_nonconst";
83     case OPTYPE_BROADCAST_FIRST:
84         return "subgroupbroadcastfirst";
85     default:
86         TCU_THROW(InternalError, "Unsupported op type");
87     }
88 }
89 
getExtHeader(const CaseDefinition & caseDef)90 string getExtHeader(const CaseDefinition &caseDef)
91 {
92     return (caseDef.extShaderSubGroupBallotTests ? "#extension GL_ARB_shader_ballot: enable\n"
93                                                    "#extension GL_KHR_shader_subgroup_basic: enable\n"
94                                                    "#extension GL_ARB_gpu_shader_int64: enable\n" :
95                                                    "#extension GL_KHR_shader_subgroup_ballot: enable\n") +
96            subgroups::getAdditionalExtensionForFormat(caseDef.format);
97 }
98 
getTestSrc(const CaseDefinition & caseDef)99 string getTestSrc(const CaseDefinition &caseDef)
100 {
101     ostringstream bdy;
102     string broadcast;
103     string broadcastFirst;
104     string mask;
105     int max;
106     const string fmt = subgroups::getFormatNameForGLSL(caseDef.format);
107 
108     if (caseDef.extShaderSubGroupBallotTests)
109     {
110         broadcast      = "readInvocationARB";
111         broadcastFirst = "readFirstInvocationARB";
112         mask           = "mask = ballotARB(true);\n";
113         max            = 64;
114 
115         bdy << "  uint64_t mask;\n"
116             << mask << "  uint sgSize = gl_SubGroupSizeARB;\n"
117             << "  uint sgInvocation = gl_SubGroupInvocationARB;\n";
118     }
119     else
120     {
121         broadcast      = "subgroupBroadcast";
122         broadcastFirst = "subgroupBroadcastFirst";
123         mask           = "mask = subgroupBallot(true);\n";
124 
125         if (caseDef.subgroupSizeControl)
126             max = caseDef.requiredSubgroupSize;
127         else
128             max = (int)subgroups::maxSupportedSubgroupSize();
129 
130         bdy << "  uvec4 mask = subgroupBallot(true);\n"
131             << "  uint sgSize = gl_SubgroupSize;\n"
132             << "  uint sgInvocation = gl_SubgroupInvocationID;\n";
133     }
134 
135     if (caseDef.opType == OPTYPE_BROADCAST)
136     {
137         bdy << "  tempRes = 0x3;\n"
138             << "  " << fmt << " ops[" << max << "];\n"
139             << "  " << fmt << " d = data[sgInvocation];\n";
140 
141         for (int i = 0; i < max; i++)
142             bdy << "  ops[" << i << "] = " << broadcast << "(d, " << i << "u);\n";
143 
144         bdy << "  for(int id = 0; id < sgSize; id++)\n"
145             << "  {\n"
146             << "    if (subgroupBallotBitExtract(mask, id) && ops[id] != data[id])\n"
147             << "    {\n"
148             << "      tempRes = 0;\n"
149             << "    }\n"
150             << "  };\n";
151     }
152     else if (caseDef.opType == OPTYPE_BROADCAST_NONCONST)
153     {
154         const string validate = "    if (subgroupBallotBitExtract(mask, id) && op != data[id])\n"
155                                 "        tempRes = 0;\n";
156 
157         bdy << "  tempRes= 0x3;\n"
158             << "  for (uint id = 0; id < sgSize; id++)\n"
159             << "  {\n"
160             << "    " << fmt << " op = " << broadcast << "(data[sgInvocation], id);\n"
161             << validate << "  }\n"
162             << "  // Test lane id that is only uniform across active lanes\n"
163             << "  if (sgInvocation >= sgSize / 2)\n"
164             << "  {\n"
165             << "    uint id = sgInvocation & ~((sgSize / 2) - 1);\n"
166             << "    " << fmt << " op = " << broadcast << "(data[sgInvocation], id);\n"
167             << validate << "  }\n";
168     }
169     else if (caseDef.opType == OPTYPE_BROADCAST_FIRST)
170     {
171         bdy << "  tempRes = 0;\n"
172             << "  uint firstActive = 0;\n"
173             << "  for (uint i = 0; i < sgSize; i++)\n"
174             << "  {\n"
175             << "    if (subgroupBallotBitExtract(mask, i))\n"
176             << "    {\n"
177             << "      firstActive = i;\n"
178             << "      break;\n"
179             << "    }\n"
180             << "  }\n"
181             << "  tempRes |= (" << broadcastFirst << "(data[sgInvocation]) == data[firstActive]) ? 0x1 : 0;\n"
182             << "  // make the firstActive invocation inactive now\n"
183             << "  if (firstActive != sgInvocation)\n"
184             << "  {\n"
185             << mask << "    for (uint i = 0; i < sgSize; i++)\n"
186             << "    {\n"
187             << "      if (subgroupBallotBitExtract(mask, i))\n"
188             << "      {\n"
189             << "        firstActive = i;\n"
190             << "        break;\n"
191             << "      }\n"
192             << "    }\n"
193             << "    tempRes |= (" << broadcastFirst << "(data[sgInvocation]) == data[firstActive]) ? 0x2 : 0;\n"
194             << "  }\n"
195             << "  else\n"
196             << "  {\n"
197             << "    // the firstActive invocation didn't partake in the second result so set it to true\n"
198             << "    tempRes |= 0x2;\n"
199             << "  }\n";
200     }
201     else
202         TCU_THROW(InternalError, "Unknown operation type");
203 
204     return bdy.str();
205 }
206 
getHelperFunctionARB(const CaseDefinition & caseDef)207 string getHelperFunctionARB(const CaseDefinition &caseDef)
208 {
209     ostringstream bdy;
210 
211     if (caseDef.extShaderSubGroupBallotTests == false)
212         return "";
213 
214     bdy << "bool subgroupBallotBitExtract(uint64_t value, uint index)\n";
215     bdy << "{\n";
216     bdy << "    if (index > 63)\n";
217     bdy << "        return false;\n";
218     bdy << "    uint64_t mask = 1ul << index;\n";
219     bdy << "    if (bool((value & mask)) == true)\n";
220     bdy << "        return true;\n";
221     bdy << "    return false;\n";
222     bdy << "}\n";
223 
224     return bdy.str();
225 }
226 
initFrameBufferPrograms(SourceCollections & programCollection,CaseDefinition caseDef)227 void initFrameBufferPrograms(SourceCollections &programCollection, CaseDefinition caseDef)
228 {
229     const SpirvVersion spirvVersion =
230         (caseDef.opType == OPTYPE_BROADCAST_NONCONST) ? SPIRV_VERSION_1_5 : SPIRV_VERSION_1_3;
231     const ShaderBuildOptions buildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u);
232     const string extHeader = getExtHeader(caseDef);
233     const string testSrc   = getTestSrc(caseDef);
234     const string helperStr = getHelperFunctionARB(caseDef);
235 
236     subgroups::initStdFrameBufferPrograms(programCollection, buildOptions, caseDef.shaderStage, caseDef.format,
237                                           *caseDef.geometryPointSizeSupported, extHeader, testSrc, helperStr);
238 }
239 
initPrograms(SourceCollections & programCollection,CaseDefinition caseDef)240 void initPrograms(SourceCollections &programCollection, CaseDefinition caseDef)
241 {
242     const bool spirv15required = caseDef.opType == OPTYPE_BROADCAST_NONCONST;
243 #ifndef CTS_USES_VULKANSC
244     const bool spirv14required =
245         (isAllRayTracingStages(caseDef.shaderStage) || isAllMeshShadingStages(caseDef.shaderStage));
246 #else
247     const bool spirv14required = false;
248 #endif // CTS_USES_VULKANSC
249     const SpirvVersion spirvVersion = spirv15required ? SPIRV_VERSION_1_5 :
250                                       spirv14required ? SPIRV_VERSION_1_4 :
251                                                         SPIRV_VERSION_1_3;
252     const ShaderBuildOptions buildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u,
253                                           (spirv14required && !spirv15required));
254     const string extHeader = getExtHeader(caseDef);
255     const string testSrc   = getTestSrc(caseDef);
256     const string helperStr = getHelperFunctionARB(caseDef);
257 
258     subgroups::initStdPrograms(programCollection, buildOptions, caseDef.shaderStage, caseDef.format,
259                                *caseDef.geometryPointSizeSupported, extHeader, testSrc, helperStr);
260 }
261 
supportedCheck(Context & context,CaseDefinition caseDef)262 void supportedCheck(Context &context, CaseDefinition caseDef)
263 {
264     if (!subgroups::isSubgroupSupported(context))
265         TCU_THROW(NotSupportedError, "Subgroup operations are not supported");
266 
267     if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_BALLOT_BIT))
268         TCU_THROW(NotSupportedError, "Device does not support subgroup ballot operations");
269 
270     if (!subgroups::isFormatSupportedForDevice(context, caseDef.format))
271         TCU_THROW(NotSupportedError, "Device does not support the specified format in subgroup operations");
272 
273     if (caseDef.requires16BitUniformBuffer)
274     {
275         if (!subgroups::is16BitUBOStorageSupported(context))
276         {
277             TCU_THROW(NotSupportedError, "Device does not support the specified format in subgroup operations");
278         }
279     }
280 
281     if (caseDef.requires8BitUniformBuffer)
282     {
283         if (!subgroups::is8BitUBOStorageSupported(context))
284         {
285             TCU_THROW(NotSupportedError, "Device does not support the specified format in subgroup operations");
286         }
287     }
288 
289     if (caseDef.extShaderSubGroupBallotTests)
290     {
291         context.requireDeviceFunctionality("VK_EXT_shader_subgroup_ballot");
292 
293         if (!subgroups::isInt64SupportedForDevice(context))
294             TCU_THROW(NotSupportedError, "Device does not support int64 data types");
295     }
296 
297     if ((caseDef.opType == OPTYPE_BROADCAST_NONCONST) && !subgroups::isSubgroupBroadcastDynamicIdSupported(context))
298         TCU_THROW(NotSupportedError, "Device does not support SubgroupBroadcastDynamicId");
299 
300     if (caseDef.subgroupSizeControl)
301     {
302         context.requireDeviceFunctionality("VK_EXT_subgroup_size_control");
303 
304 #ifndef CTS_USES_VULKANSC
305         const VkPhysicalDeviceSubgroupSizeControlFeatures &subgroupSizeControlFeatures =
306             context.getSubgroupSizeControlFeatures();
307         const VkPhysicalDeviceSubgroupSizeControlProperties &subgroupSizeControlProperties =
308             context.getSubgroupSizeControlProperties();
309 #else
310         const VkPhysicalDeviceSubgroupSizeControlFeaturesEXT &subgroupSizeControlFeatures =
311             context.getSubgroupSizeControlFeaturesEXT();
312         const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT &subgroupSizeControlProperties =
313             context.getSubgroupSizeControlPropertiesEXT();
314 #endif // CTS_USES_VULKANSC
315 
316         if (subgroupSizeControlFeatures.subgroupSizeControl == false)
317             TCU_THROW(NotSupportedError, "Device does not support varying subgroup sizes nor required subgroup size");
318 
319         if (subgroupSizeControlFeatures.computeFullSubgroups == false)
320             TCU_THROW(NotSupportedError, "Device does not support full subgroups in compute shaders");
321 
322         if (caseDef.requiredSubgroupSize < subgroupSizeControlProperties.minSubgroupSize ||
323             caseDef.requiredSubgroupSize > subgroupSizeControlProperties.maxSubgroupSize)
324         {
325             TCU_THROW(NotSupportedError, "Unsupported subgroup size");
326         }
327 
328         if ((subgroupSizeControlProperties.requiredSubgroupSizeStages & caseDef.shaderStage) != caseDef.shaderStage)
329             TCU_THROW(NotSupportedError, "Required subgroup size is not supported for shader stage");
330     }
331 
332     *caseDef.geometryPointSizeSupported = subgroups::isTessellationAndGeometryPointSizeSupported(context);
333 
334 #ifndef CTS_USES_VULKANSC
335     if (isAllRayTracingStages(caseDef.shaderStage))
336     {
337         context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
338     }
339     else if (isAllMeshShadingStages(caseDef.shaderStage))
340     {
341         context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
342         context.requireDeviceFunctionality("VK_EXT_mesh_shader");
343 
344         if ((caseDef.shaderStage & VK_SHADER_STAGE_TASK_BIT_EXT) != 0u)
345         {
346             const auto &features = context.getMeshShaderFeaturesEXT();
347             if (!features.taskShader)
348                 TCU_THROW(NotSupportedError, "Task shaders not supported");
349         }
350     }
351 #endif // CTS_USES_VULKANSC
352 
353     subgroups::supportedCheckShader(context, caseDef.shaderStage);
354 }
355 
noSSBOtest(Context & context,const CaseDefinition caseDef)356 TestStatus noSSBOtest(Context &context, const CaseDefinition caseDef)
357 {
358     const VkDeviceSize numElements = caseDef.extShaderSubGroupBallotTests ? 64u : subgroups::maxSupportedSubgroupSize();
359     const subgroups::SSBOData inputData = {
360         subgroups::SSBOData::InitializeNonZero, //  InputDataInitializeType initializeType;
361         subgroups::SSBOData::LayoutStd140,      //  InputDataLayoutType layout;
362         caseDef.format,                         //  vk::VkFormat format;
363         numElements,                            //  vk::VkDeviceSize numElements;
364         subgroups::SSBOData::BindingUBO,        //  BindingType bindingType;
365     };
366 
367     switch (caseDef.shaderStage)
368     {
369     case VK_SHADER_STAGE_VERTEX_BIT:
370         return subgroups::makeVertexFrameBufferTest(context, VK_FORMAT_R32_UINT, &inputData, 1, DE_NULL,
371                                                     checkVertexPipelineStages);
372     case VK_SHADER_STAGE_GEOMETRY_BIT:
373         return subgroups::makeGeometryFrameBufferTest(context, VK_FORMAT_R32_UINT, &inputData, 1, DE_NULL,
374                                                       checkVertexPipelineStages);
375     case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
376         return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, &inputData, 1, DE_NULL,
377                                                                     checkVertexPipelineStages, caseDef.shaderStage);
378     case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
379         return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, &inputData, 1, DE_NULL,
380                                                                     checkVertexPipelineStages, caseDef.shaderStage);
381     default:
382         TCU_THROW(InternalError, "Unhandled shader stage");
383     }
384 }
385 
test(Context & context,const CaseDefinition caseDef)386 TestStatus test(Context &context, const CaseDefinition caseDef)
387 {
388     const VkDeviceSize numElements = caseDef.extShaderSubGroupBallotTests ? 64u : subgroups::maxSupportedSubgroupSize();
389     const bool isCompute           = isAllComputeStages(caseDef.shaderStage);
390 #ifndef CTS_USES_VULKANSC
391     const bool isMesh = isAllMeshShadingStages(caseDef.shaderStage);
392 #else
393     const bool isMesh = false;
394 #endif // CTS_USES_VULKANSC
395 
396     DE_ASSERT(!(isCompute && isMesh));
397 
398     if (isCompute || isMesh)
399     {
400         const subgroups::SSBOData inputData = {
401             subgroups::SSBOData::InitializeNonZero, //  InputDataInitializeType initializeType;
402             subgroups::SSBOData::LayoutStd430,      //  InputDataLayoutType layout;
403             caseDef.format,                         //  vk::VkFormat format;
404             numElements,                            //  vk::VkDeviceSize numElements;
405         };
406 
407         if (isCompute)
408         {
409             if (caseDef.subgroupSizeControl)
410                 return subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, &inputData, 1, DE_NULL,
411                                                   checkComputeOrMesh, caseDef.requiredSubgroupSize);
412             else
413                 return subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, &inputData, 1, DE_NULL,
414                                                   checkComputeOrMesh);
415         }
416         else
417         {
418             if (caseDef.subgroupSizeControl)
419                 return subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, &inputData, 1, nullptr, checkComputeOrMesh,
420                                                caseDef.requiredSubgroupSize);
421             else
422                 return subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, &inputData, 1, nullptr, checkComputeOrMesh);
423         }
424     }
425     else if (isAllGraphicsStages(caseDef.shaderStage))
426     {
427         const VkShaderStageFlags stages = subgroups::getPossibleGraphicsSubgroupStages(context, caseDef.shaderStage);
428         const subgroups::SSBOData inputData = {
429             subgroups::SSBOData::InitializeNonZero, //  InputDataInitializeType initializeType;
430             subgroups::SSBOData::LayoutStd430,      //  InputDataLayoutType layout;
431             caseDef.format,                         //  vk::VkFormat format;
432             numElements,                            //  vk::VkDeviceSize numElements;
433             subgroups::SSBOData::BindingSSBO,       //  bool isImage;
434             4u,                                     //  uint32_t binding;
435             stages,                                 //  vk::VkShaderStageFlagBits stages;
436         };
437 
438         return subgroups::allStages(context, VK_FORMAT_R32_UINT, &inputData, 1, DE_NULL, checkVertexPipelineStages,
439                                     stages);
440     }
441 #ifndef CTS_USES_VULKANSC
442     else if (isAllRayTracingStages(caseDef.shaderStage))
443     {
444         const VkShaderStageFlags stages = subgroups::getPossibleRayTracingSubgroupStages(context, caseDef.shaderStage);
445         const subgroups::SSBOData inputData = {
446             subgroups::SSBOData::InitializeNonZero, //  InputDataInitializeType initializeType;
447             subgroups::SSBOData::LayoutStd430,      //  InputDataLayoutType layout;
448             caseDef.format,                         //  vk::VkFormat format;
449             numElements,                            //  vk::VkDeviceSize numElements;
450             subgroups::SSBOData::BindingSSBO,       //  bool isImage;
451             6u,                                     //  uint32_t binding;
452             stages,                                 //  vk::VkShaderStageFlagBits stages;
453         };
454 
455         return subgroups::allRayTracingStages(context, VK_FORMAT_R32_UINT, &inputData, 1, DE_NULL,
456                                               checkVertexPipelineStages, stages);
457     }
458 #endif // CTS_USES_VULKANSC
459     else
460         TCU_THROW(InternalError, "Unknown stage or invalid stage set");
461 }
462 } // namespace
463 
464 namespace vkt
465 {
466 namespace subgroups
467 {
createSubgroupsBallotBroadcastTests(TestContext & testCtx)468 TestCaseGroup *createSubgroupsBallotBroadcastTests(TestContext &testCtx)
469 {
470     de::MovePtr<TestCaseGroup> group(new TestCaseGroup(testCtx, "ballot_broadcast"));
471     de::MovePtr<TestCaseGroup> graphicGroup(new TestCaseGroup(testCtx, "graphics"));
472     de::MovePtr<TestCaseGroup> computeGroup(new TestCaseGroup(testCtx, "compute"));
473     de::MovePtr<TestCaseGroup> framebufferGroup(new TestCaseGroup(testCtx, "framebuffer"));
474 #ifndef CTS_USES_VULKANSC
475     de::MovePtr<TestCaseGroup> raytracingGroup(new TestCaseGroup(testCtx, "ray_tracing"));
476     de::MovePtr<TestCaseGroup> meshGroup(new TestCaseGroup(testCtx, "mesh"));
477     de::MovePtr<TestCaseGroup> meshGroupARB(new TestCaseGroup(testCtx, "mesh"));
478 #endif // CTS_USES_VULKANSC
479 
480     de::MovePtr<TestCaseGroup> groupARB(new TestCaseGroup(testCtx, "ext_shader_subgroup_ballot"));
481     de::MovePtr<TestCaseGroup> graphicGroupARB(new TestCaseGroup(testCtx, "graphics"));
482     de::MovePtr<TestCaseGroup> computeGroupARB(new TestCaseGroup(testCtx, "compute"));
483     de::MovePtr<TestCaseGroup> framebufferGroupARB(new TestCaseGroup(testCtx, "framebuffer"));
484 
485     const VkShaderStageFlags fbStages[] = {
486         VK_SHADER_STAGE_VERTEX_BIT,
487         VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
488         VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
489         VK_SHADER_STAGE_GEOMETRY_BIT,
490     };
491 #ifndef CTS_USES_VULKANSC
492     const VkShaderStageFlags meshStages[] = {
493         VK_SHADER_STAGE_MESH_BIT_EXT,
494         VK_SHADER_STAGE_TASK_BIT_EXT,
495     };
496 #endif // CTS_USES_VULKANSC
497     const bool boolValues[] = {false, true};
498 
499     {
500         const vector<VkFormat> formats = subgroups::getAllFormats();
501 
502         for (size_t formatIndex = 0; formatIndex < formats.size(); ++formatIndex)
503         {
504             const VkFormat format = formats[formatIndex];
505             // Vector, boolean and double types are not supported by functions defined in VK_EXT_shader_subgroup_ballot.
506             const bool formatTypeIsSupportedARB =
507                 format == VK_FORMAT_R32_SINT || format == VK_FORMAT_R32_UINT || format == VK_FORMAT_R32_SFLOAT;
508             const bool needs8BitUBOStorage  = isFormat8bitTy(format);
509             const bool needs16BitUBOStorage = isFormat16BitTy(format);
510 
511             for (int opTypeIndex = 0; opTypeIndex < OPTYPE_LAST; ++opTypeIndex)
512             {
513                 const OpType opType = static_cast<OpType>(opTypeIndex);
514                 const string name   = getOpTypeCaseName(opType) + "_" + subgroups::getFormatNameForGLSL(format);
515 
516                 for (size_t extNdx = 0; extNdx < DE_LENGTH_OF_ARRAY(boolValues); ++extNdx)
517                 {
518                     const bool extShaderSubGroupBallotTests = boolValues[extNdx];
519 
520                     if (extShaderSubGroupBallotTests && !formatTypeIsSupportedARB)
521                         continue;
522 
523                     {
524                         TestCaseGroup *testGroup =
525                             extShaderSubGroupBallotTests ? computeGroupARB.get() : computeGroup.get();
526                         {
527                             const CaseDefinition caseDef = {
528                                 opType,                        //  OpType opType;
529                                 VK_SHADER_STAGE_COMPUTE_BIT,   //  VkShaderStageFlags shaderStage;
530                                 format,                        //  VkFormat format;
531                                 de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
532                                 extShaderSubGroupBallotTests,  //  bool extShaderSubGroupBallotTests;
533                                 false,                         //  bool subgroupSizeControl;
534                                 0u,                            //  uint32_t requiredSubgroupSize;
535                                 false,                         //  bool requires8BitUniformBuffer;
536                                 false,                         //  bool requires16BitUniformBuffer;
537                             };
538 
539                             addFunctionCaseWithPrograms(testGroup, name, supportedCheck, initPrograms, test, caseDef);
540                         }
541 
542                         for (uint32_t subgroupSize = 1; subgroupSize <= subgroups::maxSupportedSubgroupSize();
543                              subgroupSize *= 2)
544                         {
545                             const CaseDefinition caseDef = {
546                                 opType,                        //  OpType opType;
547                                 VK_SHADER_STAGE_COMPUTE_BIT,   //  VkShaderStageFlags shaderStage;
548                                 format,                        //  VkFormat format;
549                                 de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
550                                 extShaderSubGroupBallotTests,  //  bool extShaderSubGroupBallotTests;
551                                 true,                          //  bool subgroupSizeControl;
552                                 subgroupSize,                  //  uint32_t requiredSubgroupSize;
553                                 false,                         //  bool requires8BitUniformBuffer;
554                                 false                          //  bool requires16BitUniformBuffer;
555                             };
556                             const string testName = name + "_requiredsubgroupsize" + de::toString(subgroupSize);
557 
558                             addFunctionCaseWithPrograms(testGroup, testName, supportedCheck, initPrograms, test,
559                                                         caseDef);
560                         }
561                     }
562 
563 #ifndef CTS_USES_VULKANSC
564                     for (const auto &stage : meshStages)
565                     {
566                         const auto stageName = "_" + getShaderStageName(stage);
567 
568                         TestCaseGroup *testGroup = extShaderSubGroupBallotTests ? meshGroupARB.get() : meshGroup.get();
569                         {
570                             const CaseDefinition caseDef = {
571                                 opType,                        //  OpType opType;
572                                 stage,                         //  VkShaderStageFlags shaderStage;
573                                 format,                        //  VkFormat format;
574                                 de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
575                                 extShaderSubGroupBallotTests,  //  bool extShaderSubGroupBallotTests;
576                                 false,                         //  bool subgroupSizeControl;
577                                 0u,                            //  uint32_t requiredSubgroupSize;
578                                 false,                         //  bool requires8BitUniformBuffer;
579                                 false,                         //  bool requires16BitUniformBuffer;
580                             };
581 
582                             addFunctionCaseWithPrograms(testGroup, name + stageName, supportedCheck, initPrograms, test,
583                                                         caseDef);
584                         }
585 
586                         for (uint32_t subgroupSize = 1; subgroupSize <= subgroups::maxSupportedSubgroupSize();
587                              subgroupSize *= 2)
588                         {
589                             const CaseDefinition caseDef = {
590                                 opType,                        //  OpType opType;
591                                 stage,                         //  VkShaderStageFlags shaderStage;
592                                 format,                        //  VkFormat format;
593                                 de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
594                                 extShaderSubGroupBallotTests,  //  bool extShaderSubGroupBallotTests;
595                                 true,                          //  bool subgroupSizeControl;
596                                 subgroupSize,                  //  uint32_t requiredSubgroupSize;
597                                 false,                         //  bool requires8BitUniformBuffer;
598                                 false,                         //  bool requires16BitUniformBuffer;
599                             };
600                             const string testName =
601                                 name + "_requiredsubgroupsize" + de::toString(subgroupSize) + stageName;
602 
603                             addFunctionCaseWithPrograms(testGroup, testName, supportedCheck, initPrograms, test,
604                                                         caseDef);
605                         }
606                     }
607 #endif // CTS_USES_VULKANSC
608 
609                     {
610                         TestCaseGroup *testGroup =
611                             extShaderSubGroupBallotTests ? graphicGroupARB.get() : graphicGroup.get();
612                         const CaseDefinition caseDef = {
613                             opType,                        //  OpType opType;
614                             VK_SHADER_STAGE_ALL_GRAPHICS,  //  VkShaderStageFlags shaderStage;
615                             format,                        //  VkFormat format;
616                             de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
617                             extShaderSubGroupBallotTests,  //  bool extShaderSubGroupBallotTests;
618                             false,                         //  bool subgroupSizeControl;
619                             0u,                            //  uint32_t requiredSubgroupSize;
620                             false,                         //  bool requires8BitUniformBuffer;
621                             false                          //  bool requires16BitUniformBuffer;
622                         };
623 
624                         addFunctionCaseWithPrograms(testGroup, name, supportedCheck, initPrograms, test, caseDef);
625                     }
626 
627                     {
628                         TestCaseGroup *testGroup =
629                             extShaderSubGroupBallotTests ? framebufferGroupARB.get() : framebufferGroup.get();
630 
631                         for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(fbStages); ++stageIndex)
632                         {
633                             const CaseDefinition caseDef = {
634                                 opType,                        //  OpType opType;
635                                 fbStages[stageIndex],          //  VkShaderStageFlags shaderStage;
636                                 format,                        //  VkFormat format;
637                                 de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
638                                 extShaderSubGroupBallotTests,  //  bool extShaderSubGroupBallotTests;
639                                 false,                         //  bool subgroupSizeControl;
640                                 0u,                            //  uint32_t requiredSubgroupSize;
641                                 bool(needs8BitUBOStorage),     //  bool requires8BitUniformBuffer;
642                                 bool(needs16BitUBOStorage)     //  bool requires16BitUniformBuffer;
643                             };
644 
645                             addFunctionCaseWithPrograms(testGroup, name + getShaderStageName(caseDef.shaderStage),
646                                                         supportedCheck, initFrameBufferPrograms, noSSBOtest, caseDef);
647                         }
648                     }
649                 }
650             }
651         }
652     }
653 
654 #ifndef CTS_USES_VULKANSC
655     {
656         const vector<VkFormat> formats = subgroups::getAllRayTracingFormats();
657 
658         for (size_t formatIndex = 0; formatIndex < formats.size(); ++formatIndex)
659         {
660             const VkFormat format   = formats[formatIndex];
661             const string formatName = subgroups::getFormatNameForGLSL(format);
662 
663             for (int opTypeIndex = 0; opTypeIndex < OPTYPE_LAST; ++opTypeIndex)
664             {
665                 const OpType opType          = static_cast<OpType>(opTypeIndex);
666                 const string name            = getOpTypeCaseName(opType) + "_" + formatName;
667                 const CaseDefinition caseDef = {
668                     opType,                        //  OpType opType;
669                     SHADER_STAGE_ALL_RAY_TRACING,  //  VkShaderStageFlags shaderStage;
670                     format,                        //  VkFormat format;
671                     de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
672                     false,                         //  bool extShaderSubGroupBallotTests;
673                     false,                         //  bool subgroupSizeControl;
674                     0,                             //  int requiredSubgroupSize;
675                     false,                         //  bool requires8BitUniformBuffer;
676                     false                          //  bool requires16BitUniformBuffer;
677                 };
678 
679                 addFunctionCaseWithPrograms(raytracingGroup.get(), name, supportedCheck, initPrograms, test, caseDef);
680             }
681         }
682     }
683 #endif // CTS_USES_VULKANSC
684 
685     groupARB->addChild(graphicGroupARB.release());
686     groupARB->addChild(computeGroupARB.release());
687     groupARB->addChild(framebufferGroupARB.release());
688 
689     group->addChild(graphicGroup.release());
690     group->addChild(computeGroup.release());
691     group->addChild(framebufferGroup.release());
692 #ifndef CTS_USES_VULKANSC
693     group->addChild(raytracingGroup.release());
694     group->addChild(meshGroup.release());
695     groupARB->addChild(meshGroupARB.release());
696 #endif // CTS_USES_VULKANSC
697     group->addChild(groupARB.release());
698 
699     return group.release();
700 }
701 } // namespace subgroups
702 } // namespace vkt
703