• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * OpenGL Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2017-2019 The Khronos Group Inc.
6  * Copyright (c) 2017 Codeplay Software Ltd.
7  * Copyright (c) 2019 NVIDIA Corporation.
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 "glcSubgroupsShapeTests.hpp"
27 #include "glcSubgroupsTestsUtils.hpp"
28 
29 #include <string>
30 #include <vector>
31 
32 using namespace tcu;
33 using namespace std;
34 
35 namespace glc
36 {
37 namespace subgroups
38 {
39 namespace
40 {
checkVertexPipelineStages(std::vector<const void * > datas,deUint32 width,deUint32)41 static bool checkVertexPipelineStages(std::vector<const void*> datas,
42 									  deUint32 width, deUint32)
43 {
44 	return glc::subgroups::check(datas, width, 1);
45 }
46 
checkComputeStage(std::vector<const void * > datas,const deUint32 numWorkgroups[3],const deUint32 localSize[3],deUint32)47 static bool checkComputeStage(std::vector<const void*> datas,
48 						 const deUint32 numWorkgroups[3], const deUint32 localSize[3],
49 						 deUint32)
50 {
51 	return glc::subgroups::checkCompute(datas, numWorkgroups, localSize, 1);
52 }
53 
54 enum OpType
55 {
56 	OPTYPE_CLUSTERED = 0,
57 	OPTYPE_QUAD,
58 	OPTYPE_LAST
59 };
60 
getOpTypeName(int opType)61 std::string getOpTypeName(int opType)
62 {
63 	switch (opType)
64 	{
65 		default:
66 			DE_FATAL("Unsupported op type");
67 			return "";
68 		case OPTYPE_CLUSTERED:
69 			return "clustered";
70 		case OPTYPE_QUAD:
71 			return "quad";
72 	}
73 }
74 
75 struct CaseDefinition
76 {
77 	int					opType;
78 	ShaderStageFlags	shaderStage;
79 };
80 
initFrameBufferPrograms(SourceCollections & programCollection,CaseDefinition caseDef)81 void initFrameBufferPrograms (SourceCollections& programCollection, CaseDefinition caseDef)
82 {
83 	std::ostringstream				bdy;
84 	std::string						extension = (OPTYPE_CLUSTERED == caseDef.opType) ?
85 										"#extension GL_KHR_shader_subgroup_clustered: enable\n" :
86 										"#extension GL_KHR_shader_subgroup_quad: enable\n";
87 
88 	subgroups::setFragmentShaderFrameBuffer(programCollection);
89 
90 	if (SHADER_STAGE_VERTEX_BIT != caseDef.shaderStage)
91 		subgroups::setVertexShaderFrameBuffer(programCollection);
92 
93 	extension += "#extension GL_KHR_shader_subgroup_ballot: enable\n";
94 
95 	bdy << "  uint tempResult = 0x1u;\n"
96 		<< "  uvec4 mask = subgroupBallot(true);\n";
97 
98 	if (OPTYPE_CLUSTERED == caseDef.opType)
99 	{
100 		for (deUint32 i = 1; i <= subgroups::maxSupportedSubgroupSize(); i *= 2)
101 		{
102 			bdy << "  if (gl_SubgroupSize >= " << i << "u)\n"
103 				<< "  {\n"
104 				<< "    uvec4 contribution = uvec4(0);\n"
105 				<< "    uint modID = gl_SubgroupInvocationID % 32u;\n"
106 				<< "    switch (gl_SubgroupInvocationID / 32u)\n"
107 				<< "    {\n"
108 				<< "    case 0u: contribution.x = 1u << modID; break;\n"
109 				<< "    case 1u: contribution.y = 1u << modID; break;\n"
110 				<< "    case 2u: contribution.z = 1u << modID; break;\n"
111 				<< "    case 3u: contribution.w = 1u << modID; break;\n"
112 				<< "    }\n"
113 				<< "    uvec4 result = subgroupClusteredOr(contribution, " << i << "u);\n"
114 				<< "    uint rootID = gl_SubgroupInvocationID & ~(" << i - 1 << "u);\n"
115 				<< "    for (uint i = 0u; i < " << i << "u; i++)\n"
116 				<< "    {\n"
117 				<< "      uint nextID = rootID + i;\n"
118 				<< "      if (subgroupBallotBitExtract(mask, nextID) ^^ subgroupBallotBitExtract(result, nextID))\n"
119 				<< "      {\n"
120 				<< "        tempResult = 0u;\n"
121 				<< "      }\n"
122 				<< "    }\n"
123 				<< "  }\n";
124 		}
125 	}
126 	else
127 	{
128 		bdy << "  uint cluster[4] =\n"
129 			<< "  uint[](\n"
130 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 0u),\n"
131 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 1u),\n"
132 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 2u),\n"
133 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 3u)\n"
134 			<< "  );\n"
135 			<< "  uint rootID = gl_SubgroupInvocationID & ~0x3u;\n"
136 			<< "  for (uint i = 0u; i < 4u; i++)\n"
137 			<< "  {\n"
138 			<< "    uint nextID = rootID + i;\n"
139 			<< "    if (subgroupBallotBitExtract(mask, nextID) && (cluster[i] != nextID))\n"
140 			<< "    {\n"
141 			<< "      tempResult = mask.x;\n"
142 			<< "    }\n"
143 			<< "  }\n";
144 	}
145 
146 	if (SHADER_STAGE_VERTEX_BIT == caseDef.shaderStage)
147 	{
148 		std::ostringstream vertexSrc;
149 		vertexSrc << "${VERSION_DECL}\n"
150 			<< extension
151 			<< "layout(location = 0) in highp vec4 in_position;\n"
152 			<< "layout(location = 0) out float result;\n"
153 			<< "\n"
154 			<< "void main (void)\n"
155 			<< "{\n"
156 			<< bdy.str()
157 			<< "  result = float(tempResult);\n"
158 			<< "  gl_Position = in_position;\n"
159 			<< "  gl_PointSize = 1.0f;\n"
160 			<< "}\n";
161 		programCollection.add("vert") << glu::VertexSource(vertexSrc.str());
162 	}
163 	else if (SHADER_STAGE_GEOMETRY_BIT == caseDef.shaderStage)
164 	{
165 		std::ostringstream geometry;
166 
167 		geometry << "${VERSION_DECL}\n"
168 			<< extension
169 			<< "layout(points) in;\n"
170 			<< "layout(points, max_vertices = 1) out;\n"
171 			<< "layout(location = 0) out float out_color;\n"
172 			<< "\n"
173 			<< "void main (void)\n"
174 			<< "{\n"
175 			<< bdy.str()
176 			<< "  out_color = float(tempResult);\n"
177 			<< "  gl_Position = gl_in[0].gl_Position;\n"
178 			<< "  EmitVertex();\n"
179 			<< "  EndPrimitive();\n"
180 			<< "}\n";
181 
182 		programCollection.add("geometry") << glu::GeometrySource(geometry.str());
183 	}
184 	else if (SHADER_STAGE_TESS_CONTROL_BIT == caseDef.shaderStage)
185 	{
186 		std::ostringstream controlSource;
187 
188 		controlSource << "${VERSION_DECL}\n"
189 			<< extension
190 			<< "layout(vertices = 2) out;\n"
191 			<< "layout(location = 0) out float out_color[];\n"
192 			<< "\n"
193 			<< "void main (void)\n"
194 			<< "{\n"
195 			<< "  if (gl_InvocationID == 0)\n"
196 			<<"  {\n"
197 			<< "    gl_TessLevelOuter[0] = 1.0f;\n"
198 			<< "    gl_TessLevelOuter[1] = 1.0f;\n"
199 			<< "  }\n"
200 			<< bdy.str()
201 			<< "  out_color[gl_InvocationID] = float(tempResult);\n"
202 			<< "  gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
203 			<< "}\n";
204 
205 		programCollection.add("tesc") << glu::TessellationControlSource(controlSource.str());
206 		subgroups::setTesEvalShaderFrameBuffer(programCollection);
207 	}
208 	else if (SHADER_STAGE_TESS_EVALUATION_BIT == caseDef.shaderStage)
209 	{
210 		std::ostringstream evaluationSource;
211 
212 		evaluationSource << "${VERSION_DECL}\n"
213 			<< extension
214 			<< "layout(isolines, equal_spacing, ccw) in;\n"
215 			<< "layout(location = 0) out float out_color;\n"
216 			<< "void main (void)\n"
217 			<< "{\n"
218 			<< bdy.str()
219 			<< "  out_color = float(tempResult);\n"
220 			<< "  gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n"
221 			<< "}\n";
222 
223 		subgroups::setTesCtrlShaderFrameBuffer(programCollection);
224 		programCollection.add("tese") << glu::TessellationEvaluationSource(evaluationSource.str());
225 	}
226 	else
227 	{
228 		DE_FATAL("Unsupported shader stage");
229 	}
230 }
231 
initPrograms(SourceCollections & programCollection,CaseDefinition caseDef)232 void initPrograms(SourceCollections& programCollection, CaseDefinition caseDef)
233 {
234 	std::string extension = (OPTYPE_CLUSTERED == caseDef.opType) ?
235 							"#extension GL_KHR_shader_subgroup_clustered: enable\n" :
236 							"#extension GL_KHR_shader_subgroup_quad: enable\n";
237 
238 	extension += "#extension GL_KHR_shader_subgroup_ballot: enable\n";
239 
240 	std::ostringstream bdy;
241 
242 	bdy << "  uint tempResult = 0x1u;\n"
243 		<< "  uvec4 mask = subgroupBallot(true);\n";
244 
245 	if (OPTYPE_CLUSTERED == caseDef.opType)
246 	{
247 		for (deUint32 i = 1; i <= subgroups::maxSupportedSubgroupSize(); i *= 2)
248 		{
249 			bdy << "  if (gl_SubgroupSize >= " << i << "u)\n"
250 				<< "  {\n"
251 				<< "    uvec4 contribution = uvec4(0);\n"
252 				<< "    uint modID = gl_SubgroupInvocationID % 32u;\n"
253 				<< "    switch (gl_SubgroupInvocationID / 32u)\n"
254 				<< "    {\n"
255 				<< "    case 0u: contribution.x = 1u << modID; break;\n"
256 				<< "    case 1u: contribution.y = 1u << modID; break;\n"
257 				<< "    case 2u: contribution.z = 1u << modID; break;\n"
258 				<< "    case 3u: contribution.w = 1u << modID; break;\n"
259 				<< "    }\n"
260 				<< "    uvec4 result = subgroupClusteredOr(contribution, " << i << "u);\n"
261 				<< "    uint rootID = gl_SubgroupInvocationID & ~(" << i - 1 << "u);\n"
262 				<< "    for (uint i = 0u; i < " << i << "u; i++)\n"
263 				<< "    {\n"
264 				<< "      uint nextID = rootID + i;\n"
265 				<< "      if (subgroupBallotBitExtract(mask, nextID) ^^ subgroupBallotBitExtract(result, nextID))\n"
266 				<< "      {\n"
267 				<< "        tempResult = 0u;\n"
268 				<< "      }\n"
269 				<< "    }\n"
270 				<< "  }\n";
271 		}
272 	}
273 	else
274 	{
275 		bdy << "  uint cluster[4] =\n"
276 			<< "  uint[](\n"
277 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 0u),\n"
278 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 1u),\n"
279 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 2u),\n"
280 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 3u)\n"
281 			<< "  );\n"
282 			<< "  uint rootID = gl_SubgroupInvocationID & ~0x3u;\n"
283 			<< "  for (uint i = 0u; i < 4u; i++)\n"
284 			<< "  {\n"
285 			<< "    uint nextID = rootID + i;\n"
286 			<< "    if (subgroupBallotBitExtract(mask, nextID) && (cluster[i] != nextID))\n"
287 			<< "    {\n"
288 			<< "      tempResult = mask.x;\n"
289 			<< "    }\n"
290 			<< "  }\n";
291 	}
292 
293 	if (SHADER_STAGE_COMPUTE_BIT == caseDef.shaderStage)
294 	{
295 		std::ostringstream src;
296 
297 		src << "${VERSION_DECL}\n"
298 			<< extension
299 			<< "layout (${LOCAL_SIZE_X}, ${LOCAL_SIZE_Y}, ${LOCAL_SIZE_Z}) in;\n"
300 			<< "layout(binding = 0, std430) buffer Buffer0\n"
301 			<< "{\n"
302 			<< "  uint result[];\n"
303 			<< "};\n"
304 			<< "\n"
305 			<< "void main (void)\n"
306 			<< "{\n"
307 			<< "  uvec3 globalSize = gl_NumWorkGroups * gl_WorkGroupSize;\n"
308 			<< "  highp uint offset = globalSize.x * ((globalSize.y * "
309 			"gl_GlobalInvocationID.z) + gl_GlobalInvocationID.y) + "
310 			"gl_GlobalInvocationID.x;\n"
311 			<< bdy.str()
312 			<< "  result[offset] = tempResult;\n"
313 			<< "}\n";
314 
315 		programCollection.add("comp") << glu::ComputeSource(src.str());
316 	}
317 	else
318 	{
319 		{
320 			const string vertex =
321 				"${VERSION_DECL}\n"
322 				+ extension +
323 				"layout(binding = 0, std430) buffer Buffer0\n"
324 				"{\n"
325 				"  uint result[];\n"
326 				"} b0;\n"
327 				"\n"
328 				"void main (void)\n"
329 				"{\n"
330 				+ bdy.str() +
331 				"  b0.result[gl_VertexID] = tempResult;\n"
332 				"  float pixelSize = 2.0f/1024.0f;\n"
333 				"  float pixelPosition = pixelSize/2.0f - 1.0f;\n"
334 				"  gl_Position = vec4(float(gl_VertexID) * pixelSize + pixelPosition, 0.0f, 0.0f, 1.0f);\n"
335 				"}\n";
336 
337 			programCollection.add("vert") << glu::VertexSource(vertex);
338 		}
339 
340 		{
341 			const string tesc =
342 				"${VERSION_DECL}\n"
343 				+ extension +
344 				"layout(vertices=1) out;\n"
345 				"layout(binding = 1, std430) buffer Buffer1\n"
346 				"{\n"
347 				"  uint result[];\n"
348 				"} b1;\n"
349 				"\n"
350 				"void main (void)\n"
351 				"{\n"
352 				+ bdy.str() +
353 				"  b1.result[gl_PrimitiveID] = 1u;\n"
354 				"  if (gl_InvocationID == 0)\n"
355 				"  {\n"
356 				"    gl_TessLevelOuter[0] = 1.0f;\n"
357 				"    gl_TessLevelOuter[1] = 1.0f;\n"
358 				"  }\n"
359 				"  gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
360 				"}\n";
361 
362 			programCollection.add("tesc") << glu::TessellationControlSource(tesc);
363 		}
364 
365 		{
366 			const string tese =
367 				"${VERSION_DECL}\n"
368 				+ extension +
369 				"layout(isolines) in;\n"
370 				"layout(binding = 2, std430) buffer Buffer2\n"
371 				"{\n"
372 				"  uint result[];\n"
373 				"} b2;\n"
374 				"\n"
375 				"void main (void)\n"
376 				"{\n"
377 				+ bdy.str() +
378 				"  b2.result[gl_PrimitiveID * 2 + int(gl_TessCoord.x + 0.5)] = 1u;\n"
379 				"  float pixelSize = 2.0f/1024.0f;\n"
380 				"  gl_Position = gl_in[0].gl_Position + gl_TessCoord.x * pixelSize / 2.0f;\n"
381 				"}\n";
382 
383 			programCollection.add("tese") << glu::TessellationEvaluationSource(tese);
384 		}
385 
386 		{
387 			const string geometry =
388 				// version added by addGeometryShadersFromTemplate
389 				extension +
390 				"layout(${TOPOLOGY}) in;\n"
391 				"layout(points, max_vertices = 1) out;\n"
392 				"layout(binding = 3, std430) buffer Buffer3\n"
393 				"{\n"
394 				"  uint result[];\n"
395 				"} b3;\n"
396 				"\n"
397 				"void main (void)\n"
398 				"{\n"
399 				+ bdy.str() +
400 				"  b3.result[gl_PrimitiveIDIn] = tempResult;\n"
401 				"  gl_Position = gl_in[0].gl_Position;\n"
402 				"  EmitVertex();\n"
403 				"  EndPrimitive();\n"
404 				"}\n";
405 
406 			subgroups::addGeometryShadersFromTemplate(geometry, programCollection);
407 		}
408 
409 		{
410 			const string fragment =
411 				"${VERSION_DECL}\n"
412 				+ extension +
413 				"precision highp int;\n"
414 				"layout(location = 0) out uint result;\n"
415 				"void main (void)\n"
416 				"{\n"
417 				+ bdy.str() +
418 				"  result = tempResult;\n"
419 				"}\n";
420 
421 			programCollection.add("fragment") << glu::FragmentSource(fragment);
422 		}
423 		subgroups::addNoSubgroupShader(programCollection);
424 	}
425 }
426 
supportedCheck(Context & context,CaseDefinition caseDef)427 void supportedCheck (Context& context, CaseDefinition caseDef)
428 {
429 	if (!subgroups::isSubgroupSupported(context))
430 		TCU_THROW(NotSupportedError, "Subgroup operations are not supported");
431 
432 	if (!subgroups::isSubgroupFeatureSupportedForDevice(context, SUBGROUP_FEATURE_BALLOT_BIT))
433 	{
434 		TCU_THROW(NotSupportedError, "Device does not support subgroup ballot operations");
435 	}
436 
437 	if (OPTYPE_CLUSTERED == caseDef.opType)
438 	{
439 		if (!subgroups::isSubgroupFeatureSupportedForDevice(context, SUBGROUP_FEATURE_CLUSTERED_BIT))
440 		{
441 			TCU_THROW(NotSupportedError, "Subgroup shape tests require that clustered operations are supported!");
442 		}
443 	}
444 
445 	if (OPTYPE_QUAD == caseDef.opType)
446 	{
447 		if (!subgroups::isSubgroupFeatureSupportedForDevice(context, SUBGROUP_FEATURE_QUAD_BIT))
448 		{
449 			TCU_THROW(NotSupportedError, "Subgroup shape tests require that quad operations are supported!");
450 		}
451 	}
452 }
453 
noSSBOtest(Context & context,const CaseDefinition caseDef)454 tcu::TestStatus noSSBOtest (Context& context, const CaseDefinition caseDef)
455 {
456 	if (!subgroups::areSubgroupOperationsSupportedForStage(
457 				context, caseDef.shaderStage))
458 	{
459 		if (subgroups::areSubgroupOperationsRequiredForStage(
460 					caseDef.shaderStage))
461 		{
462 			return tcu::TestStatus::fail(
463 					   "Shader stage " +
464 					   subgroups::getShaderStageName(caseDef.shaderStage) +
465 					   " is required to support subgroup operations!");
466 		}
467 		else
468 		{
469 			TCU_THROW(NotSupportedError, "Device does not support subgroup operations for this stage");
470 		}
471 	}
472 
473 	if (SHADER_STAGE_VERTEX_BIT == caseDef.shaderStage)
474 		return subgroups::makeVertexFrameBufferTest(context, FORMAT_R32_UINT, DE_NULL, 0, checkVertexPipelineStages);
475 	else if (SHADER_STAGE_GEOMETRY_BIT == caseDef.shaderStage)
476 		return subgroups::makeGeometryFrameBufferTest(context, FORMAT_R32_UINT, DE_NULL, 0, checkVertexPipelineStages);
477 	else if (SHADER_STAGE_TESS_CONTROL_BIT == caseDef.shaderStage)
478 		return subgroups::makeTessellationEvaluationFrameBufferTest(context, FORMAT_R32_UINT, DE_NULL, 0, checkVertexPipelineStages, SHADER_STAGE_TESS_CONTROL_BIT);
479 	else if (SHADER_STAGE_TESS_EVALUATION_BIT == caseDef.shaderStage)
480 		return subgroups::makeTessellationEvaluationFrameBufferTest(context,  FORMAT_R32_UINT, DE_NULL, 0, checkVertexPipelineStages, SHADER_STAGE_TESS_EVALUATION_BIT);
481 	else
482 		TCU_THROW(InternalError, "Unhandled shader stage");
483 }
484 
485 
test(Context & context,const CaseDefinition caseDef)486 tcu::TestStatus test(Context& context, const CaseDefinition caseDef)
487 {
488 	if (!subgroups::isSubgroupFeatureSupportedForDevice(context, SUBGROUP_FEATURE_BASIC_BIT))
489 	{
490 		return tcu::TestStatus::fail(
491 				   "Subgroup feature " +
492 				   subgroups::getSubgroupFeatureName(SUBGROUP_FEATURE_BASIC_BIT) +
493 				   " is a required capability!");
494 	}
495 
496 	if (SHADER_STAGE_COMPUTE_BIT == caseDef.shaderStage)
497 	{
498 		if (!subgroups::areSubgroupOperationsSupportedForStage(context, caseDef.shaderStage))
499 		{
500 			return tcu::TestStatus::fail(
501 					   "Shader stage " +
502 					   subgroups::getShaderStageName(caseDef.shaderStage) +
503 					   " is required to support subgroup operations!");
504 		}
505 		return subgroups::makeComputeTest(context, FORMAT_R32_UINT, DE_NULL, 0, checkComputeStage);
506 	}
507 	else
508 	{
509 		int supportedStages = context.getDeqpContext().getContextInfo().getInt(GL_SUBGROUP_SUPPORTED_STAGES_KHR);
510 
511 		ShaderStageFlags stages = (ShaderStageFlags)(caseDef.shaderStage & supportedStages);
512 
513 		if (SHADER_STAGE_FRAGMENT_BIT != stages && !subgroups::isVertexSSBOSupportedForDevice(context))
514 		{
515 			if ( (stages & SHADER_STAGE_FRAGMENT_BIT) == 0)
516 				TCU_THROW(NotSupportedError, "Device does not support vertex stage SSBO writes");
517 			else
518 				stages = SHADER_STAGE_FRAGMENT_BIT;
519 		}
520 
521 		if ((ShaderStageFlags)0u == stages)
522 			TCU_THROW(NotSupportedError, "Subgroup operations are not supported for any graphic shader");
523 
524 		return subgroups::allStages(context, FORMAT_R32_UINT, DE_NULL, 0, checkVertexPipelineStages, stages);
525 	}
526 }
527 }
528 
createSubgroupsShapeTests(deqp::Context & testCtx)529 deqp::TestCaseGroup* createSubgroupsShapeTests(deqp::Context& testCtx)
530 {
531 	de::MovePtr<deqp::TestCaseGroup> graphicGroup(new deqp::TestCaseGroup(
532 		testCtx, "graphics", "Subgroup shape category tests: graphics"));
533 	de::MovePtr<deqp::TestCaseGroup> computeGroup(new deqp::TestCaseGroup(
534 		testCtx, "compute", "Subgroup shape category tests: compute"));
535 	de::MovePtr<deqp::TestCaseGroup> framebufferGroup(new deqp::TestCaseGroup(
536 		testCtx, "framebuffer", "Subgroup shape category tests: framebuffer"));
537 
538 	const ShaderStageFlags stages[] =
539 	{
540 		SHADER_STAGE_VERTEX_BIT,
541 		SHADER_STAGE_TESS_EVALUATION_BIT,
542 		SHADER_STAGE_TESS_CONTROL_BIT,
543 		SHADER_STAGE_GEOMETRY_BIT,
544 	};
545 
546 	for (int opTypeIndex = 0; opTypeIndex < OPTYPE_LAST; ++opTypeIndex)
547 	{
548 		const std::string op = de::toLower(getOpTypeName(opTypeIndex));
549 
550 		{
551 			const CaseDefinition caseDef = {opTypeIndex, SHADER_STAGE_COMPUTE_BIT};
552 			SubgroupFactory<CaseDefinition>::addFunctionCaseWithPrograms(computeGroup.get(), op, "", supportedCheck, initPrograms, test, caseDef);
553 
554 		}
555 
556 		{
557 			const CaseDefinition caseDef =
558 			{
559 				opTypeIndex,
560 				SHADER_STAGE_ALL_GRAPHICS
561 			};
562 			SubgroupFactory<CaseDefinition>::addFunctionCaseWithPrograms(graphicGroup.get(),
563 									op, "",
564 									supportedCheck, initPrograms, test, caseDef);
565 		}
566 
567 		for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(stages); ++stageIndex)
568 		{
569 			const CaseDefinition caseDef = {opTypeIndex, stages[stageIndex]};
570 			SubgroupFactory<CaseDefinition>::addFunctionCaseWithPrograms(framebufferGroup.get(),op + "_" + getShaderStageName(caseDef.shaderStage), "",
571 										supportedCheck, initFrameBufferPrograms, noSSBOtest, caseDef);
572 		}
573 	}
574 
575 	de::MovePtr<deqp::TestCaseGroup> group(new deqp::TestCaseGroup(
576 		testCtx, "shape", "Subgroup shape category tests"));
577 
578 	group->addChild(graphicGroup.release());
579 	group->addChild(computeGroup.release());
580 	group->addChild(framebufferGroup.release());
581 
582 	return group.release();
583 }
584 
585 } // subgroups
586 } // glc
587