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