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 "glcSubgroupsBuiltinMaskVarTests.hpp"
27#include "glcSubgroupsTestsUtils.hpp"
28
29#include <string>
30#include <vector>
31
32using namespace tcu;
33using namespace std;
34
35namespace glc
36{
37namespace subgroups
38{
39
40static bool checkVertexPipelineStages(std::vector<const void*> datas,
41									  deUint32 width, deUint32)
42{
43	return check(datas, width, 1);
44}
45
46static bool checkComputeStage(std::vector<const void*> datas,
47						 const deUint32 numWorkgroups[3], const deUint32 localSize[3],
48						 deUint32)
49{
50	return checkCompute(datas, numWorkgroups, localSize, 1);
51}
52
53namespace
54{
55struct CaseDefinition
56{
57	std::string			varName;
58	ShaderStageFlags	shaderStage;
59};
60}
61
62std::string subgroupMask (const CaseDefinition& caseDef)
63{
64	std::ostringstream bdy;
65
66	bdy << "  uint tempResult = 0x1u;\n"
67		<< "  uint bit        = 0x1u;\n"
68		<< "  uint bitCount   = 0x0u;\n"
69		<< "  uvec4 mask = subgroupBallot(true);\n"
70		<< "  uvec4 var = " << caseDef.varName << ";\n"
71		<< "  for (uint i = 0u; i < gl_SubgroupSize; i++)\n"
72		<< "  {\n";
73
74	if ("gl_SubgroupEqMask" == caseDef.varName)
75	{
76		bdy << "    if ((i == gl_SubgroupInvocationID) ^^ subgroupBallotBitExtract(var, i))\n"
77			<< "    {\n"
78			<< "      tempResult = 0u;\n"
79			<< "    }\n";
80	}
81	else if ("gl_SubgroupGeMask" == caseDef.varName)
82	{
83		bdy << "    if ((i >= gl_SubgroupInvocationID) ^^ subgroupBallotBitExtract(var, i))\n"
84			<< "    {\n"
85			<< "      tempResult = 0u;\n"
86			<< "    }\n";
87	}
88	else if ("gl_SubgroupGtMask" == caseDef.varName)
89	{
90		bdy << "    if ((i > gl_SubgroupInvocationID) ^^ subgroupBallotBitExtract(var, i))\n"
91			<< "    {\n"
92			<< "      tempResult = 0u;\n"
93			<< "    }\n";
94	}
95	else if ("gl_SubgroupLeMask" == caseDef.varName)
96	{
97		bdy << "    if ((i <= gl_SubgroupInvocationID) ^^ subgroupBallotBitExtract(var, i))\n"
98			<< "    {\n"
99			<< "      tempResult = 0u;\n"
100			<< "    }\n";
101	}
102	else if ("gl_SubgroupLtMask" == caseDef.varName)
103	{
104		bdy << "    if ((i < gl_SubgroupInvocationID) ^^ subgroupBallotBitExtract(var, i))\n"
105			<< "    {\n"
106			<< "      tempResult = 0u;\n"
107			<< "    }\n";
108	}
109
110	bdy << "  }\n"
111		<< "  for (uint i = 0u; i < 32u; i++)\n"
112		<< "  {\n"
113		<< "    if ((var.x & bit) > 0u)\n"
114		<< "    {\n"
115		<< "      bitCount++;\n"
116		<< "    }\n"
117		<< "    if ((var.y & bit) > 0u)\n"
118		<< "    {\n"
119		<< "      bitCount++;\n"
120		<< "    }\n"
121		<< "    if ((var.z & bit) > 0u)\n"
122		<< "    {\n"
123		<< "      bitCount++;\n"
124		<< "    }\n"
125		<< "    if ((var.w & bit) > 0u)\n"
126		<< "    {\n"
127		<< "      bitCount++;\n"
128		<< "    }\n"
129		<< "    bit = bit << 1u;\n"
130		<< "  }\n"
131		<< "  if (subgroupBallotBitCount(var) != bitCount)\n"
132		<< "  {\n"
133		<< "    tempResult = 0u;\n"
134		<< "  }\n";
135	return bdy.str();
136}
137
138void initFrameBufferPrograms(SourceCollections& programCollection, CaseDefinition caseDef)
139{
140	subgroups::setFragmentShaderFrameBuffer(programCollection);
141
142	if (SHADER_STAGE_VERTEX_BIT != caseDef.shaderStage)
143		subgroups::setVertexShaderFrameBuffer(programCollection);
144
145	if (SHADER_STAGE_VERTEX_BIT == caseDef.shaderStage)
146	{
147		const string bdy = subgroupMask(caseDef);
148		const string vertexGLSL =
149			"${VERSION_DECL}\n"
150			"#extension GL_KHR_shader_subgroup_ballot: enable\n"
151			"layout(location = 0) out float out_color;\n"
152			"layout(location = 0) in highp vec4 in_position;\n"
153			"\n"
154			"void main (void)\n"
155			"{\n"
156			+ bdy +
157			"  out_color = float(tempResult);\n"
158			"  gl_Position = in_position;\n"
159			"  gl_PointSize = 1.0f;\n"
160			"}\n";
161		programCollection.add("vert") << glu::VertexSource(vertexGLSL);
162	}
163	else if (SHADER_STAGE_TESS_EVALUATION_BIT == caseDef.shaderStage)
164	{
165		const string bdy = subgroupMask(caseDef);
166		const string  evaluationSourceGLSL =
167			"${VERSION_DECL}\n"
168			"#extension GL_KHR_shader_subgroup_ballot: enable\n"
169			"${TESS_EXTENSION}\n"
170			"layout(isolines, equal_spacing, ccw ) in;\n"
171			"layout(location = 0) out float out_color;\n"
172			"\n"
173			"void main (void)\n"
174			"{\n"
175			+ bdy +
176			"  out_color = float(tempResult);\n"
177			"  gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n"
178			"}\n";
179		programCollection.add("tese") << glu::TessellationEvaluationSource(evaluationSourceGLSL);
180		subgroups::setTesCtrlShaderFrameBuffer(programCollection);
181	}
182	else if (SHADER_STAGE_TESS_CONTROL_BIT == caseDef.shaderStage)
183	{
184		const string bdy = subgroupMask(caseDef);
185		const string  controlSourceGLSL =
186			"${VERSION_DECL}\n"
187			"${TESS_EXTENSION}\n"
188			"#extension GL_KHR_shader_subgroup_ballot: enable\n"
189			"layout(vertices = 2) out;\n"
190			"layout(location = 0) out float out_color[];\n"
191			"void main (void)\n"
192			"{\n"
193			"  if (gl_InvocationID == 0)\n"
194			"  {\n"
195			"    gl_TessLevelOuter[0] = 1.0f;\n"
196			"    gl_TessLevelOuter[1] = 1.0f;\n"
197			"  }\n"
198			+ bdy +
199			"  out_color[gl_InvocationID] = float(tempResult);\n"
200			"  gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
201			"}\n";
202		programCollection.add("tesc") << glu::TessellationControlSource(controlSourceGLSL);
203		subgroups::setTesEvalShaderFrameBuffer(programCollection);
204	}
205	else if (SHADER_STAGE_GEOMETRY_BIT == caseDef.shaderStage)
206	{
207		const string bdy = subgroupMask(caseDef);
208		const string geometryGLSL =
209			"${VERSION_DECL}\n"
210			"#extension GL_KHR_shader_subgroup_ballot: enable\n"
211			"layout(points) in;\n"
212			"layout(points, max_vertices = 1) out;\n"
213			"layout(location = 0) out float out_color;\n"
214			"\n"
215			"void main (void)\n"
216			"{\n"
217			+ bdy +
218			"  out_color = float(tempResult);\n"
219			"  gl_Position = gl_in[0].gl_Position;\n"
220			"  EmitVertex();\n"
221			"  EndPrimitive();\n"
222			"}\n";
223		programCollection.add("geometry") << glu::GeometrySource(geometryGLSL);
224	}
225	else
226	{
227		DE_FATAL("Unsupported shader stage");
228	}
229}
230
231
232void initPrograms(SourceCollections& programCollection, CaseDefinition caseDef)
233{
234	const string bdy = subgroupMask(caseDef);
235
236	if (SHADER_STAGE_COMPUTE_BIT == caseDef.shaderStage)
237	{
238		std::ostringstream src;
239
240		src << "${VERSION_DECL}\n"
241			<< "#extension GL_KHR_shader_subgroup_ballot: enable\n"
242			<< "layout (${LOCAL_SIZE_X}, ${LOCAL_SIZE_Y}, ${LOCAL_SIZE_Z}) in;\n"
243			<< "layout(binding = 0, std430) buffer Output\n"
244			<< "{\n"
245			<< "  uint result[];\n"
246			<< "};\n"
247			<< "\n"
248			<< "void main (void)\n"
249			<< "{\n"
250			<< "  uvec3 globalSize = gl_NumWorkGroups * gl_WorkGroupSize;\n"
251			<< "  highp uint offset = globalSize.x * ((globalSize.y * "
252			"gl_GlobalInvocationID.z) + gl_GlobalInvocationID.y) + "
253			"gl_GlobalInvocationID.x;\n"
254			<< bdy
255			<< "  result[offset] = tempResult;\n"
256			<< "}\n";
257
258		programCollection.add("comp") << glu::ComputeSource(src.str());
259	}
260	else
261	{
262		{
263			const string vertex =
264				"${VERSION_DECL}\n"
265				"#extension GL_KHR_shader_subgroup_ballot: enable\n"
266				"layout(binding = 0, std430) buffer Output0\n"
267				"{\n"
268				"  uint result[];\n"
269				"} b0;\n"
270				"\n"
271				"void main (void)\n"
272				"{\n"
273				+ bdy +
274				"  b0.result[gl_VertexID] = tempResult;\n"
275				"  float pixelSize = 2.0f/1024.0f;\n"
276				"  float pixelPosition = pixelSize/2.0f - 1.0f;\n"
277				"  gl_Position = vec4(float(gl_VertexID) * pixelSize + pixelPosition, 0.0f, 0.0f, 1.0f);\n"
278				"  gl_PointSize = 1.0f;\n"
279				"}\n";
280			programCollection.add("vert") << glu::VertexSource(vertex);
281		}
282
283		{
284			const string tesc =
285				"${VERSION_DECL}\n"
286				"#extension GL_KHR_shader_subgroup_ballot: enable\n"
287				"layout(vertices=1) out;\n"
288				"layout(binding = 1, std430) buffer Output1\n"
289				"{\n"
290				"  uint result[];\n"
291				"} b1;\n"
292				"\n"
293				"void main (void)\n"
294				"{\n"
295				+ bdy +
296				"  b1.result[gl_PrimitiveID] = tempResult;\n"
297				"  if (gl_InvocationID == 0)\n"
298				"  {\n"
299				"    gl_TessLevelOuter[0] = 1.0f;\n"
300				"    gl_TessLevelOuter[1] = 1.0f;\n"
301				"  }\n"
302				"  gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
303				"}\n";
304			programCollection.add("tesc") << glu::TessellationControlSource(tesc);
305		}
306
307		{
308			const string tese =
309				"${VERSION_DECL}\n"
310				"#extension GL_KHR_shader_subgroup_ballot: enable\n"
311				"layout(isolines) in;\n"
312				"layout(binding = 2, std430) buffer Output2\n"
313				"{\n"
314				"  uint result[];\n"
315				"} b2;\n"
316				"\n"
317				"void main (void)\n"
318				"{\n"
319				+ bdy +
320				"  b2.result[gl_PrimitiveID * 2 + int(gl_TessCoord.x + 0.5)] = tempResult;\n"
321				"  float pixelSize = 2.0f/1024.0f;\n"
322				"  gl_Position = gl_in[0].gl_Position + gl_TessCoord.x * pixelSize / 2.0f;\n"
323				"}\n";
324
325			programCollection.add("tese") << glu::TessellationEvaluationSource(tese);
326		}
327
328		{
329			const string geometry =
330				"#extension GL_KHR_shader_subgroup_ballot: enable\n"
331				"layout(${TOPOLOGY}) in;\n"
332				"layout(points, max_vertices = 1) out;\n"
333				"layout(binding = 3, std430) buffer Output3\n"
334				"{\n"
335				"  uint result[];\n"
336				"} b3;\n"
337				"\n"
338				"void main (void)\n"
339				"{\n"
340				+ bdy +
341				"  b3.result[gl_PrimitiveIDIn] = tempResult;\n"
342				"  gl_Position = gl_in[0].gl_Position;\n"
343				"  EmitVertex();\n"
344				"  EndPrimitive();\n"
345				"}\n";
346
347			subgroups::addGeometryShadersFromTemplate(geometry, programCollection);
348		}
349
350		{
351			const string fragment =
352				"${VERSION_DECL}\n"
353				"#extension GL_KHR_shader_subgroup_ballot: enable\n"
354				"precision highp int;\n"
355				"layout(location = 0) out uint result;\n"
356				"void main (void)\n"
357				"{\n"
358				+ bdy +
359				"  result = tempResult;\n"
360				"}\n";
361
362			programCollection.add("fragment") << glu::FragmentSource(fragment);
363		}
364
365		subgroups::addNoSubgroupShader(programCollection);
366	}
367}
368
369void supportedCheck (Context& context, CaseDefinition caseDef)
370{
371	DE_UNREF(caseDef);
372	if (!subgroups::isSubgroupSupported(context))
373		TCU_THROW(NotSupportedError, "Subgroup operations are not supported");
374}
375
376tcu::TestStatus noSSBOtest(Context& context, const CaseDefinition caseDef)
377{
378	if (!areSubgroupOperationsSupportedForStage(
379				context, caseDef.shaderStage))
380	{
381		if (areSubgroupOperationsRequiredForStage(caseDef.shaderStage))
382		{
383			return tcu::TestStatus::fail(
384					   "Shader stage " + getShaderStageName(caseDef.shaderStage) +
385					   " is required to support subgroup operations!");
386		}
387		else
388		{
389			TCU_THROW(NotSupportedError, "Device does not support subgroup operations for this stage");
390		}
391	}
392
393	if (!subgroups::isSubgroupFeatureSupportedForDevice(context, SUBGROUP_FEATURE_BALLOT_BIT))
394	{
395		TCU_THROW(NotSupportedError, "Device does not support subgroup ballot operations");
396	}
397
398	if (SHADER_STAGE_VERTEX_BIT == caseDef.shaderStage)
399		return makeVertexFrameBufferTest(context, FORMAT_R32_UINT, DE_NULL, 0, checkVertexPipelineStages);
400	else if ((SHADER_STAGE_TESS_EVALUATION_BIT | SHADER_STAGE_TESS_CONTROL_BIT) & caseDef.shaderStage )
401		return makeTessellationEvaluationFrameBufferTest(context, FORMAT_R32_UINT, DE_NULL, 0, checkVertexPipelineStages);
402
403	return makeGeometryFrameBufferTest(context, FORMAT_R32_UINT, DE_NULL, 0, checkVertexPipelineStages);
404}
405
406
407tcu::TestStatus test(Context& context, const CaseDefinition caseDef)
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 (SHADER_STAGE_COMPUTE_BIT == caseDef.shaderStage)
415	{
416		if (!areSubgroupOperationsSupportedForStage(context, caseDef.shaderStage))
417		{
418				return tcu::TestStatus::fail(
419						   "Shader stage " + getShaderStageName(caseDef.shaderStage) +
420						   " is required to support subgroup operations!");
421		}
422		return makeComputeTest(context, FORMAT_R32_UINT, DE_NULL, 0, checkComputeStage);
423	}
424	else
425	{
426		int supportedStages = context.getDeqpContext().getContextInfo().getInt(GL_SUBGROUP_SUPPORTED_STAGES_KHR);
427
428		subgroups::ShaderStageFlags stages = (subgroups::ShaderStageFlags)(caseDef.shaderStage & supportedStages);
429
430		if ( SHADER_STAGE_FRAGMENT_BIT != stages && !subgroups::isVertexSSBOSupportedForDevice(context))
431		{
432			if ( (stages & SHADER_STAGE_FRAGMENT_BIT) == 0)
433				TCU_THROW(NotSupportedError, "Device does not support vertex stage SSBO writes");
434			else
435				stages = SHADER_STAGE_FRAGMENT_BIT;
436		}
437
438		if ((ShaderStageFlags)0u == stages)
439			TCU_THROW(NotSupportedError, "Subgroup operations are not supported for any graphic shader");
440
441		return subgroups::allStages(context, FORMAT_R32_UINT, DE_NULL, 0, checkVertexPipelineStages, stages);
442	}
443}
444
445deqp::TestCaseGroup* createSubgroupsBuiltinMaskVarTests(deqp::Context& testCtx)
446{
447	de::MovePtr<deqp::TestCaseGroup> graphicGroup(new deqp::TestCaseGroup(
448		testCtx, "graphics", "Subgroup builtin mask category	tests: graphics"));
449	de::MovePtr<deqp::TestCaseGroup> computeGroup(new deqp::TestCaseGroup(
450		testCtx, "compute", "Subgroup builtin mask category tests: compute"));
451	de::MovePtr<deqp::TestCaseGroup> framebufferGroup(new deqp::TestCaseGroup(
452		testCtx, "framebuffer", "Subgroup builtin mask category tests: framebuffer"));
453
454	const char* const all_stages_vars[] =
455	{
456		"SubgroupEqMask",
457		"SubgroupGeMask",
458		"SubgroupGtMask",
459		"SubgroupLeMask",
460		"SubgroupLtMask",
461	};
462
463	const subgroups::ShaderStageFlags stages[] =
464	{
465		SHADER_STAGE_VERTEX_BIT,
466		SHADER_STAGE_TESS_EVALUATION_BIT,
467		SHADER_STAGE_TESS_CONTROL_BIT,
468		SHADER_STAGE_GEOMETRY_BIT,
469	};
470
471
472	for (int a = 0; a < DE_LENGTH_OF_ARRAY(all_stages_vars); ++a)
473	{
474		const std::string var = all_stages_vars[a];
475		const std::string varLower = de::toLower(var);
476
477		{
478			const CaseDefinition caseDef = {"gl_" + var, SHADER_STAGE_ALL_GRAPHICS};
479			SubgroupFactory<CaseDefinition>::addFunctionCaseWithPrograms(graphicGroup.get(),
480										varLower, "",
481										supportedCheck, initPrograms, test, caseDef);
482		}
483
484		{
485			const CaseDefinition caseDef = {"gl_" + var, SHADER_STAGE_COMPUTE_BIT};
486			SubgroupFactory<CaseDefinition>::addFunctionCaseWithPrograms(computeGroup.get(),
487										varLower, "",
488										supportedCheck, initPrograms, test, caseDef);
489		}
490
491		for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(stages); ++stageIndex)
492		{
493			const CaseDefinition caseDef = {"gl_" + var, stages[stageIndex]};
494			SubgroupFactory<CaseDefinition>::addFunctionCaseWithPrograms(framebufferGroup.get(),
495						varLower + "_" +
496						getShaderStageName(caseDef.shaderStage), "",
497						supportedCheck, initFrameBufferPrograms, noSSBOtest, caseDef);
498		}
499	}
500
501	de::MovePtr<deqp::TestCaseGroup> group(new deqp::TestCaseGroup(
502		testCtx, "builtin_mask_var", "Subgroup builtin mask variable tests"));
503
504	group->addChild(graphicGroup.release());
505	group->addChild(computeGroup.release());
506	group->addChild(framebufferGroup.release());
507
508	return group.release();
509}
510} // subgroups
511} // glc
512