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