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