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 "vktSubgroupsBallotOtherTests.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_INVERSE_BALLOT = 0,
42     OPTYPE_BALLOT_BIT_EXTRACT,
43     OPTYPE_BALLOT_BIT_COUNT,
44     OPTYPE_BALLOT_INCLUSIVE_BIT_COUNT,
45     OPTYPE_BALLOT_EXCLUSIVE_BIT_COUNT,
46     OPTYPE_BALLOT_FIND_LSB,
47     OPTYPE_BALLOT_FIND_MSB,
48     OPTYPE_LAST
49 };
50 
51 struct CaseDefinition
52 {
53     OpType opType;
54     VkShaderStageFlags shaderStage;
55     de::SharedPtr<bool> geometryPointSizeSupported;
56     bool requiredSubgroupSize;
57 };
58 
checkVertexPipelineStages(const void * internalData,vector<const void * > datas,uint32_t width,uint32_t)59 bool checkVertexPipelineStages(const void *internalData, vector<const void *> datas, uint32_t width, uint32_t)
60 {
61     DE_UNREF(internalData);
62 
63     return subgroups::check(datas, width, 0xf);
64 }
65 
checkComputeOrMesh(const void * internalData,vector<const void * > datas,const uint32_t numWorkgroups[3],const uint32_t localSize[3],uint32_t)66 bool checkComputeOrMesh(const void *internalData, vector<const void *> datas, const uint32_t numWorkgroups[3],
67                         const uint32_t localSize[3], uint32_t)
68 {
69     DE_UNREF(internalData);
70 
71     return subgroups::checkComputeOrMesh(datas, numWorkgroups, localSize, 0xf);
72 }
73 
getOpTypeName(OpType opType)74 string getOpTypeName(OpType opType)
75 {
76     switch (opType)
77     {
78     case OPTYPE_INVERSE_BALLOT:
79         return "subgroupInverseBallot";
80     case OPTYPE_BALLOT_BIT_EXTRACT:
81         return "subgroupBallotBitExtract";
82     case OPTYPE_BALLOT_BIT_COUNT:
83         return "subgroupBallotBitCount";
84     case OPTYPE_BALLOT_INCLUSIVE_BIT_COUNT:
85         return "subgroupBallotInclusiveBitCount";
86     case OPTYPE_BALLOT_EXCLUSIVE_BIT_COUNT:
87         return "subgroupBallotExclusiveBitCount";
88     case OPTYPE_BALLOT_FIND_LSB:
89         return "subgroupBallotFindLSB";
90     case OPTYPE_BALLOT_FIND_MSB:
91         return "subgroupBallotFindMSB";
92     default:
93         TCU_THROW(InternalError, "Unsupported op type");
94     }
95 }
96 
getExtHeader(const CaseDefinition &)97 string getExtHeader(const CaseDefinition &)
98 {
99     return "#extension GL_KHR_shader_subgroup_ballot: enable\n";
100 }
101 
getPerStageHeadDeclarations(const CaseDefinition & caseDef)102 vector<string> getPerStageHeadDeclarations(const CaseDefinition &caseDef)
103 {
104     const uint32_t stageCount = subgroups::getStagesCount(caseDef.shaderStage);
105     const bool fragment       = (caseDef.shaderStage & VK_SHADER_STAGE_FRAGMENT_BIT) != 0;
106     vector<string> result(stageCount, string());
107 
108     if (fragment)
109         result.reserve(result.size() + 1);
110 
111     for (size_t i = 0; i < result.size(); ++i)
112     {
113         result[i] = "layout(set = 0, binding = " + de::toString(i) +
114                     ", std430) buffer Buffer1\n"
115                     "{\n"
116                     "  uint result[];\n"
117                     "};\n";
118     }
119 
120     if (fragment)
121     {
122         const string fragPart = "layout(location = 0) out uint result;\n"
123                                 "precision highp int;\n";
124 
125         result.push_back(fragPart);
126     }
127 
128     return result;
129 }
130 
getFramebufferPerStageHeadDeclarations(const CaseDefinition & caseDef)131 vector<string> getFramebufferPerStageHeadDeclarations(const CaseDefinition &caseDef)
132 {
133     vector<string> result;
134 
135     DE_UNREF(caseDef);
136 
137     result.push_back("layout(location = 0) out float result;\n");
138     result.push_back("layout(location = 0) out float out_color;\n");
139     result.push_back("layout(location = 0) out float out_color[];\n");
140     result.push_back("layout(location = 0) out float out_color;\n");
141 
142     return result;
143 }
144 
getTestString(const CaseDefinition & caseDef)145 string getTestString(const CaseDefinition &caseDef)
146 {
147     ostringstream bdy;
148 
149     bdy << "  uvec4 allOnes = uvec4(0xFFFFFFFF);\n"
150         << "  uvec4 allZeros = uvec4(0);\n"
151         << "  uint tempResult = 0;\n"
152         << "#define MAKE_HIGH_BALLOT_RESULT(i) uvec4("
153         << "i >= 32 ? 0 : (0xFFFFFFFF << i), "
154         << "i >= 64 ? 0 : (0xFFFFFFFF << ((i < 32) ? 0 : (i - 32))), "
155         << "i >= 96 ? 0 : (0xFFFFFFFF << ((i < 64) ? 0 : (i - 64))), "
156         << "i >= 128 ? 0 : (0xFFFFFFFF << ((i < 96) ? 0 : (i - 96))))\n"
157         << "#define MAKE_SINGLE_BIT_BALLOT_RESULT(i) uvec4("
158         << "i >= 32 ? 0 : 0x1 << i, "
159         << "i < 32 || i >= 64 ? 0 : 0x1 << (i - 32), "
160         << "i < 64 || i >= 96 ? 0 : 0x1 << (i - 64), "
161         << "i < 96 || i >= 128 ? 0 : 0x1 << (i - 96))\n";
162 
163     switch (caseDef.opType)
164     {
165     default:
166         DE_FATAL("Unknown op type!");
167         break;
168     case OPTYPE_INVERSE_BALLOT:
169         bdy << "  tempResult |= subgroupInverseBallot(allOnes) ? 0x1 : 0;\n"
170             << "  tempResult |= subgroupInverseBallot(allZeros) ? 0 : 0x2;\n"
171             << "  tempResult |= subgroupInverseBallot(subgroupBallot(true)) ? 0x4 : 0;\n"
172             << "  tempResult |= 0x8;\n";
173         break;
174     case OPTYPE_BALLOT_BIT_EXTRACT:
175         bdy << "  tempResult |= subgroupBallotBitExtract(allOnes, gl_SubgroupInvocationID) ? 0x1 : 0;\n"
176             << "  tempResult |= subgroupBallotBitExtract(allZeros, gl_SubgroupInvocationID) ? 0 : 0x2;\n"
177             << "  tempResult |= subgroupBallotBitExtract(subgroupBallot(true), gl_SubgroupInvocationID) ? 0x4 : 0;\n"
178             << "  tempResult |= 0x8;\n"
179             << "  for (uint i = 0; i < gl_SubgroupSize; i++)\n"
180             << "  {\n"
181             << "    if (!subgroupBallotBitExtract(allOnes, gl_SubgroupInvocationID))\n"
182             << "    {\n"
183             << "      tempResult &= ~0x8;\n"
184             << "    }\n"
185             << "  }\n";
186         break;
187     case OPTYPE_BALLOT_BIT_COUNT:
188         bdy << "  /* To ensure a 32-bit computation, use a variable with default highp precision. */\n"
189             << "  uint SubgroupSize = gl_SubgroupSize;\n"
190             << "  tempResult |= SubgroupSize == subgroupBallotBitCount(allOnes) ? 0x1 : 0;\n"
191             << "  tempResult |= 0 == subgroupBallotBitCount(allZeros) ? 0x2 : 0;\n"
192             << "  tempResult |= 0 < subgroupBallotBitCount(subgroupBallot(true)) ? 0x4 : 0;\n"
193             << "  tempResult |= 0 == subgroupBallotBitCount(MAKE_HIGH_BALLOT_RESULT(SubgroupSize)) ? 0x8 : 0;\n";
194         break;
195     case OPTYPE_BALLOT_INCLUSIVE_BIT_COUNT:
196         bdy << "  uint inclusiveOffset = gl_SubgroupInvocationID + 1;\n"
197             << "  tempResult |= inclusiveOffset == subgroupBallotInclusiveBitCount(allOnes) ? 0x1 : 0;\n"
198             << "  tempResult |= 0 == subgroupBallotInclusiveBitCount(allZeros) ? 0x2 : 0;\n"
199             << "  tempResult |= 0 < subgroupBallotInclusiveBitCount(subgroupBallot(true)) ? 0x4 : 0;\n"
200             << "  tempResult |= 0x8;\n"
201             << "  for (uint i = 0; i < 128; i++)\n"
202             << "  {\n"
203             << "    uint ref = inclusiveOffset - min(inclusiveOffset, i);\n"
204             << "    uvec4 b = MAKE_HIGH_BALLOT_RESULT(i);\n"
205             << "    uint inclusiveBitCount = subgroupBallotInclusiveBitCount(b);\n"
206             << "    if (inclusiveBitCount != ref)\n"
207             << "    {\n"
208             << "      tempResult &= ~0x8;\n"
209             << "    }\n"
210             << "  }\n";
211         break;
212     case OPTYPE_BALLOT_EXCLUSIVE_BIT_COUNT:
213         bdy << "  uint exclusiveOffset = gl_SubgroupInvocationID;\n"
214             << "  tempResult |= exclusiveOffset == subgroupBallotExclusiveBitCount(allOnes) ? 0x1 : 0;\n"
215             << "  tempResult |= 0 == subgroupBallotExclusiveBitCount(allZeros) ? 0x2 : 0;\n"
216             << "  tempResult |= 0x4;\n"
217             << "  tempResult |= 0x8;\n"
218             << "  for (uint i = 0; i < 128; i++)\n"
219             << "  {\n"
220             << "    uint ref = exclusiveOffset - min(exclusiveOffset, i);\n"
221             << "    uvec4 b = MAKE_HIGH_BALLOT_RESULT(i);\n"
222             << "    uint exclusiveBitCount = subgroupBallotExclusiveBitCount(b);\n"
223             << "    if (exclusiveBitCount != ref)\n"
224             << "    {\n"
225             << "      tempResult &= ~0x8;\n"
226             << "    }\n"
227             << "  }\n";
228         break;
229     case OPTYPE_BALLOT_FIND_LSB:
230         bdy << "  tempResult |= 0 == subgroupBallotFindLSB(allOnes) ? 0x1 : 0;\n"
231             << "  if (subgroupElect())\n"
232             << "  {\n"
233             << "    tempResult |= 0x2;\n"
234             << "  }\n"
235             << "  else\n"
236             << "  {\n"
237             << "    tempResult |= 0 < subgroupBallotFindLSB(subgroupBallot(true)) ? 0x2 : 0;\n"
238             << "  }\n"
239             << "  tempResult |= gl_SubgroupSize > subgroupBallotFindLSB(subgroupBallot(true)) ? 0x4 : 0;\n"
240             << "  tempResult |= 0x8;\n"
241             << "  for (uint i = 0; i < gl_SubgroupSize; i++)\n"
242             << "  {\n"
243             << "    if (i != subgroupBallotFindLSB(MAKE_HIGH_BALLOT_RESULT(i)))\n"
244             << "    {\n"
245             << "      tempResult &= ~0x8;\n"
246             << "    }\n"
247             << "  }\n";
248         break;
249     case OPTYPE_BALLOT_FIND_MSB:
250         bdy << "  tempResult |= (gl_SubgroupSize - 1) == subgroupBallotFindMSB(allOnes) ? 0x1 : 0;\n"
251             << "  if (subgroupElect())\n"
252             << "  {\n"
253             << "    tempResult |= 0x2;\n"
254             << "  }\n"
255             << "  else\n"
256             << "  {\n"
257             << "    tempResult |= 0 < subgroupBallotFindMSB(subgroupBallot(true)) ? 0x2 : 0;\n"
258             << "  }\n"
259             << "  tempResult |= gl_SubgroupSize > subgroupBallotFindMSB(subgroupBallot(true)) ? 0x4 : 0;\n"
260             << "  tempResult |= 0x8;\n"
261             << "  for (uint i = 0; i < gl_SubgroupSize; i++)\n"
262             << "  {\n"
263             << "    if (i != subgroupBallotFindMSB(MAKE_SINGLE_BIT_BALLOT_RESULT(i)))\n"
264             << "    {\n"
265             << "      tempResult &= ~0x8;\n"
266             << "    }\n"
267             << "  }\n";
268         break;
269     }
270 
271     bdy << "  tempRes = tempResult;\n";
272 
273     return bdy.str();
274 }
275 
initFrameBufferPrograms(SourceCollections & programCollection,CaseDefinition caseDef)276 void initFrameBufferPrograms(SourceCollections &programCollection, CaseDefinition caseDef)
277 {
278     const ShaderBuildOptions buildOptions(programCollection.usedVulkanVersion, SPIRV_VERSION_1_3, 0u);
279     const string extHeader                = getExtHeader(caseDef);
280     const string testSrc                  = getTestString(caseDef);
281     const vector<string> headDeclarations = getFramebufferPerStageHeadDeclarations(caseDef);
282     const bool pointSizeSupported         = *caseDef.geometryPointSizeSupported;
283 
284     subgroups::initStdFrameBufferPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT,
285                                           pointSizeSupported, extHeader, testSrc, "", headDeclarations);
286 }
287 
initPrograms(SourceCollections & programCollection,CaseDefinition caseDef)288 void initPrograms(SourceCollections &programCollection, CaseDefinition caseDef)
289 {
290 #ifndef CTS_USES_VULKANSC
291     const bool spirv14required =
292         (isAllRayTracingStages(caseDef.shaderStage) || isAllMeshShadingStages(caseDef.shaderStage));
293 #else
294     const bool spirv14required = false;
295 #endif // CTS_USES_VULKANSC
296     const SpirvVersion spirvVersion = (spirv14required ? SPIRV_VERSION_1_4 : SPIRV_VERSION_1_3);
297     const ShaderBuildOptions buildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, spirv14required);
298     const string extHeader                = getExtHeader(caseDef);
299     const string testSrc                  = getTestString(caseDef);
300     const vector<string> headDeclarations = getPerStageHeadDeclarations(caseDef);
301     const bool pointSizeSupported         = *caseDef.geometryPointSizeSupported;
302 
303     subgroups::initStdPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT,
304                                pointSizeSupported, extHeader, testSrc, "", headDeclarations);
305 }
306 
supportedCheck(Context & context,CaseDefinition caseDef)307 void supportedCheck(Context &context, CaseDefinition caseDef)
308 {
309     if (!subgroups::isSubgroupSupported(context))
310         TCU_THROW(NotSupportedError, "Subgroup operations are not supported");
311 
312     if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_BALLOT_BIT))
313     {
314         TCU_THROW(NotSupportedError, "Device does not support subgroup ballot operations");
315     }
316 
317     if (caseDef.requiredSubgroupSize)
318     {
319         context.requireDeviceFunctionality("VK_EXT_subgroup_size_control");
320 
321 #ifndef CTS_USES_VULKANSC
322         const VkPhysicalDeviceSubgroupSizeControlFeatures &subgroupSizeControlFeatures =
323             context.getSubgroupSizeControlFeatures();
324         const VkPhysicalDeviceSubgroupSizeControlProperties &subgroupSizeControlProperties =
325             context.getSubgroupSizeControlProperties();
326 #else
327         const VkPhysicalDeviceSubgroupSizeControlFeaturesEXT &subgroupSizeControlFeatures =
328             context.getSubgroupSizeControlFeaturesEXT();
329         const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT &subgroupSizeControlProperties =
330             context.getSubgroupSizeControlPropertiesEXT();
331 #endif // CTS_USES_VULKANSC
332 
333         if (subgroupSizeControlFeatures.subgroupSizeControl == false)
334             TCU_THROW(NotSupportedError, "Device does not support varying subgroup sizes nor required subgroup size");
335 
336         if (subgroupSizeControlFeatures.computeFullSubgroups == false)
337             TCU_THROW(NotSupportedError, "Device does not support full subgroups in compute shaders");
338 
339         if ((subgroupSizeControlProperties.requiredSubgroupSizeStages & caseDef.shaderStage) != caseDef.shaderStage)
340             TCU_THROW(NotSupportedError, "Required subgroup size is not supported for shader stage");
341     }
342 
343     *caseDef.geometryPointSizeSupported = subgroups::isTessellationAndGeometryPointSizeSupported(context);
344 
345 #ifndef CTS_USES_VULKANSC
346     if (isAllRayTracingStages(caseDef.shaderStage))
347     {
348         context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
349     }
350     else if (isAllMeshShadingStages(caseDef.shaderStage))
351     {
352         context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
353         context.requireDeviceFunctionality("VK_EXT_mesh_shader");
354 
355         if ((caseDef.shaderStage & VK_SHADER_STAGE_TASK_BIT_EXT) != 0u)
356         {
357             const auto &features = context.getMeshShaderFeaturesEXT();
358             if (!features.taskShader)
359                 TCU_THROW(NotSupportedError, "Task shaders not supported");
360         }
361     }
362 #endif // CTS_USES_VULKANSC
363 
364     subgroups::supportedCheckShader(context, caseDef.shaderStage);
365 }
366 
noSSBOtest(Context & context,const CaseDefinition caseDef)367 TestStatus noSSBOtest(Context &context, const CaseDefinition caseDef)
368 {
369     switch (caseDef.shaderStage)
370     {
371     case VK_SHADER_STAGE_VERTEX_BIT:
372         return subgroups::makeVertexFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
373                                                     checkVertexPipelineStages);
374     case VK_SHADER_STAGE_GEOMETRY_BIT:
375         return subgroups::makeGeometryFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
376                                                       checkVertexPipelineStages);
377     case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
378         return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
379                                                                     checkVertexPipelineStages);
380     case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
381         return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
382                                                                     checkVertexPipelineStages);
383     default:
384         TCU_THROW(InternalError, "Unhandled shader stage");
385     }
386 }
387 
test(Context & context,const CaseDefinition caseDef)388 TestStatus test(Context &context, const CaseDefinition caseDef)
389 {
390     const bool isCompute = isAllComputeStages(caseDef.shaderStage);
391 #ifndef CTS_USES_VULKANSC
392     const bool isMesh = isAllMeshShadingStages(caseDef.shaderStage);
393 #else
394     const bool isMesh = false;
395 #endif // CTS_USES_VULKANSC
396     DE_ASSERT(!(isCompute && isMesh));
397 
398     if (isCompute || isMesh)
399     {
400 #ifndef CTS_USES_VULKANSC
401         const VkPhysicalDeviceSubgroupSizeControlProperties &subgroupSizeControlProperties =
402             context.getSubgroupSizeControlProperties();
403 #else
404         const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT &subgroupSizeControlProperties =
405             context.getSubgroupSizeControlPropertiesEXT();
406 #endif // CTS_USES_VULKANSC
407         TestLog &log = context.getTestContext().getLog();
408 
409         if (caseDef.requiredSubgroupSize == false)
410         {
411             if (isCompute)
412                 return subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkComputeOrMesh);
413             else
414                 return subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, nullptr, 0, nullptr, checkComputeOrMesh);
415         }
416 
417         log << TestLog::Message << "Testing required subgroup size range ["
418             << subgroupSizeControlProperties.minSubgroupSize << ", " << subgroupSizeControlProperties.maxSubgroupSize
419             << "]" << TestLog::EndMessage;
420 
421         // According to the spec, requiredSubgroupSize must be a power-of-two integer.
422         for (uint32_t size = subgroupSizeControlProperties.minSubgroupSize;
423              size <= subgroupSizeControlProperties.maxSubgroupSize; size *= 2)
424         {
425             TestStatus result(QP_TEST_RESULT_INTERNAL_ERROR, "Internal Error");
426 
427             if (isCompute)
428                 result = subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0u, DE_NULL,
429                                                     checkComputeOrMesh, size);
430             else
431                 result = subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, nullptr, 0u, nullptr, checkComputeOrMesh,
432                                                  size);
433 
434             if (result.getCode() != QP_TEST_RESULT_PASS)
435             {
436                 log << TestLog::Message << "subgroupSize " << size << " failed" << TestLog::EndMessage;
437 
438                 return result;
439             }
440         }
441 
442         return TestStatus::pass("OK");
443     }
444     else if (isAllGraphicsStages(caseDef.shaderStage))
445     {
446         const VkShaderStageFlags stages = subgroups::getPossibleGraphicsSubgroupStages(context, caseDef.shaderStage);
447 
448         return subgroups::allStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages,
449                                     stages);
450     }
451 #ifndef CTS_USES_VULKANSC
452     else if (isAllRayTracingStages(caseDef.shaderStage))
453     {
454         const VkShaderStageFlags stages = subgroups::getPossibleRayTracingSubgroupStages(context, caseDef.shaderStage);
455 
456         return subgroups::allRayTracingStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
457                                               checkVertexPipelineStages, stages);
458     }
459 #endif // CTS_USES_VULKANSC
460     else
461         TCU_THROW(InternalError, "Unknown stage or invalid stage set");
462 
463     return TestStatus::pass("OK");
464 }
465 } // namespace
466 
467 namespace vkt
468 {
469 namespace subgroups
470 {
createSubgroupsBallotOtherTests(TestContext & testCtx)471 TestCaseGroup *createSubgroupsBallotOtherTests(TestContext &testCtx)
472 {
473     de::MovePtr<TestCaseGroup> group(new TestCaseGroup(testCtx, "ballot_other"));
474     de::MovePtr<TestCaseGroup> graphicGroup(new TestCaseGroup(testCtx, "graphics"));
475     de::MovePtr<TestCaseGroup> computeGroup(new TestCaseGroup(testCtx, "compute"));
476     de::MovePtr<TestCaseGroup> framebufferGroup(new TestCaseGroup(testCtx, "framebuffer"));
477 #ifndef CTS_USES_VULKANSC
478     de::MovePtr<TestCaseGroup> raytracingGroup(new TestCaseGroup(testCtx, "ray_tracing"));
479     de::MovePtr<TestCaseGroup> meshGroup(new TestCaseGroup(testCtx, "mesh"));
480 #endif // CTS_USES_VULKANSC
481     const VkShaderStageFlags fbStages[] = {
482         VK_SHADER_STAGE_VERTEX_BIT,
483         VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
484         VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
485         VK_SHADER_STAGE_GEOMETRY_BIT,
486     };
487 #ifndef CTS_USES_VULKANSC
488     const VkShaderStageFlags meshStages[] = {
489         VK_SHADER_STAGE_MESH_BIT_EXT,
490         VK_SHADER_STAGE_TASK_BIT_EXT,
491     };
492 #endif // CTS_USES_VULKANSC
493     const bool boolValues[] = {false, true};
494 
495     for (int opTypeIndex = 0; opTypeIndex < OPTYPE_LAST; ++opTypeIndex)
496     {
497         const OpType opType = static_cast<OpType>(opTypeIndex);
498         const string op     = de::toLower(getOpTypeName(opType));
499 
500         for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
501         {
502             const bool requiredSubgroupSize = boolValues[groupSizeNdx];
503             const string testName           = op + (requiredSubgroupSize ? "_requiredsubgroupsize" : "");
504             const CaseDefinition caseDef    = {
505                 opType,                        //  OpType opType;
506                 VK_SHADER_STAGE_COMPUTE_BIT,   //  VkShaderStageFlags shaderStage;
507                 de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
508                 requiredSubgroupSize           //  bool requiredSubgroupSize;
509             };
510 
511             addFunctionCaseWithPrograms(computeGroup.get(), testName, supportedCheck, initPrograms, test, caseDef);
512         }
513 
514 #ifndef CTS_USES_VULKANSC
515         for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
516         {
517             for (const auto &stage : meshStages)
518             {
519                 const bool requiredSubgroupSize = boolValues[groupSizeNdx];
520                 const string testName =
521                     op + (requiredSubgroupSize ? "_requiredsubgroupsize" : "") + "_" + getShaderStageName(stage);
522                 const CaseDefinition caseDef = {
523                     opType,                        //  OpType opType;
524                     stage,                         //  VkShaderStageFlags shaderStage;
525                     de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
526                     requiredSubgroupSize           //  bool requiredSubgroupSize;
527                 };
528 
529                 addFunctionCaseWithPrograms(meshGroup.get(), testName, supportedCheck, initPrograms, test, caseDef);
530             }
531         }
532 #endif // CTS_USES_VULKANSC
533 
534         {
535             const CaseDefinition caseDef = {
536                 opType,                        //  OpType opType;
537                 VK_SHADER_STAGE_ALL_GRAPHICS,  //  VkShaderStageFlags shaderStage;
538                 de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
539                 false                          //  bool requiredSubgroupSize;
540             };
541 
542             addFunctionCaseWithPrograms(graphicGroup.get(), op, supportedCheck, initPrograms, test, caseDef);
543         }
544 
545 #ifndef CTS_USES_VULKANSC
546         {
547             const CaseDefinition caseDef = {
548                 opType,                        //  OpType opType;
549                 SHADER_STAGE_ALL_RAY_TRACING,  //  VkShaderStageFlags shaderStage;
550                 de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
551                 false                          //  bool requiredSubgroupSize;
552             };
553 
554             addFunctionCaseWithPrograms(raytracingGroup.get(), op, supportedCheck, initPrograms, test, caseDef);
555         }
556 #endif // CTS_USES_VULKANSC
557 
558         for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(fbStages); ++stageIndex)
559         {
560             const CaseDefinition caseDef = {
561                 opType,                        //  OpType opType;
562                 fbStages[stageIndex],          //  VkShaderStageFlags shaderStage;
563                 de::SharedPtr<bool>(new bool), //  de::SharedPtr<bool> geometryPointSizeSupported;
564                 false                          //  bool requiredSubgroupSize;
565             };
566             const string testName = op + "_" + getShaderStageName(caseDef.shaderStage);
567 
568             addFunctionCaseWithPrograms(framebufferGroup.get(), testName, supportedCheck, initFrameBufferPrograms,
569                                         noSSBOtest, caseDef);
570         }
571     }
572 
573     group->addChild(graphicGroup.release());
574     group->addChild(computeGroup.release());
575     group->addChild(framebufferGroup.release());
576 #ifndef CTS_USES_VULKANSC
577     group->addChild(raytracingGroup.release());
578     group->addChild(meshGroup.release());
579 #endif // CTS_USES_VULKANSC
580 
581     return group.release();
582 }
583 
584 } // namespace subgroups
585 } // namespace vkt
586