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