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