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
32 using namespace tcu;
33 using namespace std;
34
35 namespace glc
36 {
37 namespace subgroups
38 {
39
checkVertexPipelineStages(std::vector<const void * > datas,deUint32 width,deUint32)40 static bool checkVertexPipelineStages(std::vector<const void*> datas,
41 deUint32 width, deUint32)
42 {
43 return check(datas, width, 1);
44 }
45
checkComputeStage(std::vector<const void * > datas,const deUint32 numWorkgroups[3],const deUint32 localSize[3],deUint32)46 static 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
53 namespace
54 {
55 struct CaseDefinition
56 {
57 std::string varName;
58 ShaderStageFlags shaderStage;
59 };
60 }
61
subgroupMask(const CaseDefinition & caseDef)62 std::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
initFrameBufferPrograms(SourceCollections & programCollection,CaseDefinition caseDef)138 void 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 "#extension GL_EXT_tessellation_shader : require\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 "#extension GL_EXT_tessellation_shader : require\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
initPrograms(SourceCollections & programCollection,CaseDefinition caseDef)232 void 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
supportedCheck(Context & context,CaseDefinition caseDef)369 void 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
noSSBOtest(Context & context,const CaseDefinition caseDef)376 tcu::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
test(Context & context,const CaseDefinition caseDef)407 tcu::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
createSubgroupsBuiltinMaskVarTests(deqp::Context & testCtx)445 deqp::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