1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2019 The Khronos Group Inc.
6 * Copyright (c) 2019 Google Inc.
7 * Copyright (c) 2017 Codeplay Software Ltd.
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 "vktSubgroupsShapeTests.hpp"
27 #include "vktSubgroupsTestsUtils.hpp"
28
29 #include <string>
30 #include <vector>
31
32 using namespace tcu;
33 using namespace std;
34 using namespace vk;
35 using namespace vkt;
36
37 namespace
38 {
39 enum OpType
40 {
41 OPTYPE_CLUSTERED = 0,
42 OPTYPE_QUAD,
43 OPTYPE_LAST
44 };
45
46 struct CaseDefinition
47 {
48 OpType opType;
49 VkShaderStageFlags shaderStage;
50 de::SharedPtr<bool> geometryPointSizeSupported;
51 deBool requiredSubgroupSize;
52 };
53
checkVertexPipelineStages(const void * internalData,vector<const void * > datas,deUint32 width,deUint32)54 static bool checkVertexPipelineStages (const void* internalData,
55 vector<const void*> datas,
56 deUint32 width,
57 deUint32)
58 {
59 DE_UNREF(internalData);
60
61 return subgroups::check(datas, width, 1);
62 }
63
checkCompute(const void * internalData,vector<const void * > datas,const deUint32 numWorkgroups[3],const deUint32 localSize[3],deUint32)64 static bool checkCompute (const void* internalData,
65 vector<const void*> datas,
66 const deUint32 numWorkgroups[3],
67 const deUint32 localSize[3],
68 deUint32)
69 {
70 DE_UNREF(internalData);
71
72 return subgroups::checkCompute(datas, numWorkgroups, localSize, 1);
73 }
74
getOpTypeName(const OpType opType)75 string getOpTypeName (const OpType opType)
76 {
77 switch (opType)
78 {
79 case OPTYPE_CLUSTERED: return "clustered";
80 case OPTYPE_QUAD: return "quad";
81 default: TCU_THROW(InternalError, "Unsupported op type");
82 }
83 }
84
getExtHeader(const CaseDefinition & caseDef)85 string getExtHeader (const CaseDefinition& caseDef)
86 {
87 const string testExtensions = (OPTYPE_CLUSTERED == caseDef.opType)
88 ? "#extension GL_KHR_shader_subgroup_clustered: enable\n"
89 : "#extension GL_KHR_shader_subgroup_quad: enable\n";
90 const string extensions = testExtensions
91 + "#extension GL_KHR_shader_subgroup_ballot: enable\n";
92
93 return extensions;
94 }
95
getBodySource(const CaseDefinition & caseDef)96 string getBodySource (const CaseDefinition& caseDef)
97 {
98 ostringstream bdy;
99
100 bdy << " uint tempResult = 0x1;\n"
101 << " uvec4 mask = subgroupBallot(true);\n";
102
103 if (OPTYPE_CLUSTERED == caseDef.opType)
104 {
105 for (deUint32 i = 1; i <= subgroups::maxSupportedSubgroupSize(); i *= 2)
106 {
107 bdy << " if (gl_SubgroupSize >= " << i << ")\n"
108 << " {\n"
109 << " uvec4 contribution = uvec4(0);\n"
110 << " const uint modID = gl_SubgroupInvocationID % 32;\n"
111 << " switch (gl_SubgroupInvocationID / 32)\n"
112 << " {\n"
113 << " case 0: contribution.x = 1 << modID; break;\n"
114 << " case 1: contribution.y = 1 << modID; break;\n"
115 << " case 2: contribution.z = 1 << modID; break;\n"
116 << " case 3: contribution.w = 1 << modID; break;\n"
117 << " }\n"
118 << " uvec4 result = subgroupClusteredOr(contribution, " << i << ");\n"
119 << " uint rootID = gl_SubgroupInvocationID & ~(" << i - 1 << ");\n"
120 << " for (uint i = 0; i < " << i << "; i++)\n"
121 << " {\n"
122 << " uint nextID = rootID + i;\n"
123 << " if (subgroupBallotBitExtract(mask, nextID) ^^ subgroupBallotBitExtract(result, nextID))\n"
124 << " {\n"
125 << " tempResult = 0;\n"
126 << " }\n"
127 << " }\n"
128 << " }\n";
129 }
130 }
131 else
132 {
133 bdy << " uint cluster[4] =\n"
134 << " {\n"
135 << " subgroupQuadBroadcast(gl_SubgroupInvocationID, 0),\n"
136 << " subgroupQuadBroadcast(gl_SubgroupInvocationID, 1),\n"
137 << " subgroupQuadBroadcast(gl_SubgroupInvocationID, 2),\n"
138 << " subgroupQuadBroadcast(gl_SubgroupInvocationID, 3)\n"
139 << " };\n"
140 << " uint rootID = gl_SubgroupInvocationID & ~0x3;\n"
141 << " for (uint i = 0; i < 4; i++)\n"
142 << " {\n"
143 << " uint nextID = rootID + i;\n"
144 << " if (subgroupBallotBitExtract(mask, nextID) && (cluster[i] != nextID))\n"
145 << " {\n"
146 << " tempResult = mask.x;\n"
147 << " }\n"
148 << " }\n";
149 }
150
151 bdy << " tempRes = tempResult;\n";
152
153 return bdy.str();
154 }
155
getFramebufferPerStageHeadDeclarations(const CaseDefinition & caseDef)156 vector<string> getFramebufferPerStageHeadDeclarations (const CaseDefinition& caseDef)
157 {
158 vector<string> result;
159
160 DE_UNREF(caseDef);
161
162 result.push_back("layout(location = 0) out float result;\n");
163 result.push_back("layout(location = 0) out float out_color;\n");
164 result.push_back("layout(location = 0) out float out_color[];\n");
165 result.push_back("layout(location = 0) out float out_color;\n");
166
167 return result;
168 }
169
initFrameBufferPrograms(SourceCollections & programCollection,CaseDefinition caseDef)170 void initFrameBufferPrograms (SourceCollections& programCollection, CaseDefinition caseDef)
171 {
172 const ShaderBuildOptions buildOptions (programCollection.usedVulkanVersion, SPIRV_VERSION_1_3, 0u);
173 const string extHeader = getExtHeader(caseDef);
174 const string testSrc = getBodySource(caseDef);
175 const vector<string> headDeclarations = getFramebufferPerStageHeadDeclarations(caseDef);
176 const bool pointSizeSupported = *caseDef.geometryPointSizeSupported;
177
178 subgroups::initStdFrameBufferPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT, pointSizeSupported, extHeader, testSrc, "", headDeclarations);
179 }
180
getPerStageHeadDeclarations(const CaseDefinition & caseDef)181 vector<string> getPerStageHeadDeclarations (const CaseDefinition& caseDef)
182 {
183 const deUint32 stageCount = subgroups::getStagesCount(caseDef.shaderStage);
184 const bool fragment = (caseDef.shaderStage & VK_SHADER_STAGE_FRAGMENT_BIT) != 0;
185 vector<string> result (stageCount, string());
186
187 if (fragment)
188 result.reserve(result.size() + 1);
189
190 for (size_t i = 0; i < result.size(); ++i)
191 {
192 result[i] =
193 "layout(set = 0, binding = " + de::toString(i) + ", std430) buffer Buffer1\n"
194 "{\n"
195 " uint result[];\n"
196 "};\n";
197 }
198
199 if (fragment)
200 {
201 const string fragPart =
202 "layout(location = 0) out uint result;\n";
203
204 result.push_back(fragPart);
205 }
206
207 return result;
208 }
209
initPrograms(SourceCollections & programCollection,CaseDefinition caseDef)210 void initPrograms (SourceCollections& programCollection, CaseDefinition caseDef)
211 {
212 const SpirvVersion spirvVersion = isAllRayTracingStages(caseDef.shaderStage) ? SPIRV_VERSION_1_4 : SPIRV_VERSION_1_3;
213 const ShaderBuildOptions buildOptions (programCollection.usedVulkanVersion, spirvVersion, 0u);
214 const string extHeader = getExtHeader(caseDef);
215 const string testSrc = getBodySource(caseDef);
216 const vector<string> headDeclarations = getPerStageHeadDeclarations(caseDef);
217 const bool pointSizeSupport = *caseDef.geometryPointSizeSupported;
218
219 subgroups::initStdPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT, pointSizeSupport, extHeader, testSrc, "", headDeclarations);
220 }
221
supportedCheck(Context & context,CaseDefinition caseDef)222 void supportedCheck (Context& context, CaseDefinition caseDef)
223 {
224 if (!subgroups::isSubgroupSupported(context))
225 TCU_THROW(NotSupportedError, "Subgroup operations are not supported");
226
227 if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_BALLOT_BIT))
228 {
229 TCU_THROW(NotSupportedError, "Device does not support subgroup ballot operations");
230 }
231
232 if (OPTYPE_CLUSTERED == caseDef.opType)
233 {
234 if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_CLUSTERED_BIT))
235 {
236 TCU_THROW(NotSupportedError, "Subgroup shape tests require that clustered operations are supported!");
237 }
238 }
239
240 if (OPTYPE_QUAD == caseDef.opType)
241 {
242 if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_QUAD_BIT))
243 {
244 TCU_THROW(NotSupportedError, "Subgroup shape tests require that quad operations are supported!");
245 }
246 }
247
248 if (caseDef.requiredSubgroupSize)
249 {
250 context.requireDeviceFunctionality("VK_EXT_subgroup_size_control");
251
252 const VkPhysicalDeviceSubgroupSizeControlFeaturesEXT& subgroupSizeControlFeatures = context.getSubgroupSizeControlFeaturesEXT();
253 const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& subgroupSizeControlProperties = context.getSubgroupSizeControlPropertiesEXT();
254
255 if (subgroupSizeControlFeatures.subgroupSizeControl == DE_FALSE)
256 TCU_THROW(NotSupportedError, "Device does not support varying subgroup sizes nor required subgroup size");
257
258 if (subgroupSizeControlFeatures.computeFullSubgroups == DE_FALSE)
259 TCU_THROW(NotSupportedError, "Device does not support full subgroups in compute shaders");
260
261 if ((subgroupSizeControlProperties.requiredSubgroupSizeStages & caseDef.shaderStage) != caseDef.shaderStage)
262 TCU_THROW(NotSupportedError, "Required subgroup size is not supported for shader stage");
263 }
264
265 *caseDef.geometryPointSizeSupported = subgroups::isTessellationAndGeometryPointSizeSupported(context);
266
267 if (isAllRayTracingStages(caseDef.shaderStage))
268 {
269 context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
270 }
271
272 subgroups::supportedCheckShader(context, caseDef.shaderStage);
273 }
274
noSSBOtest(Context & context,const CaseDefinition caseDef)275 TestStatus noSSBOtest (Context& context, const CaseDefinition caseDef)
276 {
277 switch (caseDef.shaderStage)
278 {
279 case VK_SHADER_STAGE_VERTEX_BIT: return subgroups::makeVertexFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
280 case VK_SHADER_STAGE_GEOMETRY_BIT: return subgroups::makeGeometryFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
281 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, caseDef.shaderStage);
282 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, caseDef.shaderStage);
283 default: TCU_THROW(InternalError, "Unhandled shader stage");
284 }
285 }
286
test(Context & context,const CaseDefinition caseDef)287 TestStatus test (Context& context, const CaseDefinition caseDef)
288 {
289 if (isAllComputeStages(caseDef.shaderStage))
290 {
291 const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& subgroupSizeControlProperties = context.getSubgroupSizeControlPropertiesEXT();
292 TestLog& log = context.getTestContext().getLog();
293
294 if (caseDef.requiredSubgroupSize == DE_FALSE)
295 return subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkCompute);
296
297 log << TestLog::Message << "Testing required subgroup size range [" << subgroupSizeControlProperties.minSubgroupSize << ", "
298 << subgroupSizeControlProperties.maxSubgroupSize << "]" << TestLog::EndMessage;
299
300 // According to the spec, requiredSubgroupSize must be a power-of-two integer.
301 for (deUint32 size = subgroupSizeControlProperties.minSubgroupSize; size <= subgroupSizeControlProperties.maxSubgroupSize; size *= 2)
302 {
303 TestStatus result = subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkCompute,
304 size, VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT);
305 if (result.getCode() != QP_TEST_RESULT_PASS)
306 {
307 log << TestLog::Message << "subgroupSize " << size << " failed" << TestLog::EndMessage;
308 return result;
309 }
310 }
311
312 return TestStatus::pass("OK");
313 }
314 else if (isAllGraphicsStages(caseDef.shaderStage))
315 {
316 const VkShaderStageFlags stages = subgroups::getPossibleGraphicsSubgroupStages(context, caseDef.shaderStage);
317
318 return subgroups::allStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, stages);
319 }
320 else if (isAllRayTracingStages(caseDef.shaderStage))
321 {
322 const VkShaderStageFlags stages = subgroups::getPossibleRayTracingSubgroupStages(context, caseDef.shaderStage);
323
324 return subgroups::allRayTracingStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, stages);
325 }
326 else
327 TCU_THROW(InternalError, "Unknown stage or invalid stage set");
328 }
329 }
330
331 namespace vkt
332 {
333 namespace subgroups
334 {
createSubgroupsShapeTests(TestContext & testCtx)335 TestCaseGroup* createSubgroupsShapeTests (TestContext& testCtx)
336 {
337 de::MovePtr<TestCaseGroup> group (new TestCaseGroup(testCtx, "shape", "Subgroup shape category tests"));
338 de::MovePtr<TestCaseGroup> graphicGroup (new TestCaseGroup(testCtx, "graphics", "Subgroup shape category tests: graphics"));
339 de::MovePtr<TestCaseGroup> computeGroup (new TestCaseGroup(testCtx, "compute", "Subgroup shape category tests: compute"));
340 de::MovePtr<TestCaseGroup> framebufferGroup (new TestCaseGroup(testCtx, "framebuffer", "Subgroup shape category tests: framebuffer"));
341 de::MovePtr<TestCaseGroup> raytracingGroup (new TestCaseGroup(testCtx, "ray_tracing", "Subgroup shape category tests: ray tracing"));
342 const VkShaderStageFlags stages[] =
343 {
344 VK_SHADER_STAGE_VERTEX_BIT,
345 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
346 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
347 VK_SHADER_STAGE_GEOMETRY_BIT,
348 };
349 const deBool boolValues[] =
350 {
351 DE_FALSE,
352 DE_TRUE
353 };
354
355 for (int opTypeIndex = 0; opTypeIndex < OPTYPE_LAST; ++opTypeIndex)
356 {
357 const OpType opType = static_cast<OpType>(opTypeIndex);
358 const string op = de::toLower(getOpTypeName(opType));
359
360 for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
361 {
362 const deBool requiredSubgroupSize = boolValues[groupSizeNdx];
363 const string testName = op + (requiredSubgroupSize ? "_requiredsubgroupsize" : "");
364 const CaseDefinition caseDef =
365 {
366 opType, // OpType opType;
367 VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlags shaderStage;
368 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
369 requiredSubgroupSize // deBool requiredSubgroupSize;
370 };
371
372 addFunctionCaseWithPrograms(computeGroup.get(), testName, "", supportedCheck, initPrograms, test, caseDef);
373 }
374
375 {
376 const CaseDefinition caseDef =
377 {
378 opType, // OpType opType;
379 VK_SHADER_STAGE_ALL_GRAPHICS, // VkShaderStageFlags shaderStage;
380 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
381 DE_FALSE // deBool requiredSubgroupSize;
382 };
383
384 addFunctionCaseWithPrograms(graphicGroup.get(), op, "", supportedCheck, initPrograms, test, caseDef);
385 }
386
387 {
388 const CaseDefinition caseDef =
389 {
390 opType, // OpType opType;
391 SHADER_STAGE_ALL_RAY_TRACING, // VkShaderStageFlags shaderStage;
392 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
393 DE_FALSE // deBool requiredSubgroupSize;
394 };
395
396 addFunctionCaseWithPrograms(raytracingGroup.get(), op, "", supportedCheck, initPrograms, test, caseDef);
397 }
398
399 for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(stages); ++stageIndex)
400 {
401 const CaseDefinition caseDef =
402 {
403 opType, // OpType opType;
404 stages[stageIndex], // VkShaderStageFlags shaderStage;
405 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
406 DE_FALSE // deBool requiredSubgroupSize;
407 };
408 const string testName = op + "_" + getShaderStageName(caseDef.shaderStage);
409
410 addFunctionCaseWithPrograms(framebufferGroup.get(), testName, "", supportedCheck, initFrameBufferPrograms, noSSBOtest, caseDef);
411 }
412 }
413
414 group->addChild(graphicGroup.release());
415 group->addChild(computeGroup.release());
416 group->addChild(framebufferGroup.release());
417 group->addChild(raytracingGroup.release());
418
419 return group.release();
420 }
421
422 } // subgroups
423 } // vkt
424