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 bool requiredSubgroupSize;
52 };
53
checkVertexPipelineStages(const void * internalData,vector<const void * > datas,uint32_t width,uint32_t)54 static bool checkVertexPipelineStages(const void *internalData, vector<const void *> datas, uint32_t width, uint32_t)
55 {
56 DE_UNREF(internalData);
57
58 return subgroups::check(datas, width, 1);
59 }
60
checkComputeOrMesh(const void * internalData,vector<const void * > datas,const uint32_t numWorkgroups[3],const uint32_t localSize[3],uint32_t)61 static bool checkComputeOrMesh(const void *internalData, vector<const void *> datas, const uint32_t numWorkgroups[3],
62 const uint32_t localSize[3], uint32_t)
63 {
64 DE_UNREF(internalData);
65
66 return subgroups::checkComputeOrMesh(datas, numWorkgroups, localSize, 1);
67 }
68
getOpTypeName(const OpType opType)69 string getOpTypeName(const OpType opType)
70 {
71 switch (opType)
72 {
73 case OPTYPE_CLUSTERED:
74 return "clustered";
75 case OPTYPE_QUAD:
76 return "quad";
77 default:
78 TCU_THROW(InternalError, "Unsupported op type");
79 }
80 }
81
getExtHeader(const CaseDefinition & caseDef)82 string getExtHeader(const CaseDefinition &caseDef)
83 {
84 const string testExtensions = (OPTYPE_CLUSTERED == caseDef.opType) ?
85 "#extension GL_KHR_shader_subgroup_clustered: enable\n" :
86 "#extension GL_KHR_shader_subgroup_quad: enable\n";
87 const string extensions = testExtensions + "#extension GL_KHR_shader_subgroup_ballot: enable\n";
88
89 return extensions;
90 }
91
getBodySource(const CaseDefinition & caseDef)92 string getBodySource(const CaseDefinition &caseDef)
93 {
94 ostringstream bdy;
95
96 bdy << " uint tempResult = 0x1;\n"
97 << " uvec4 mask = subgroupBallot(true);\n";
98
99 if (OPTYPE_CLUSTERED == caseDef.opType)
100 {
101 for (uint32_t i = 1; i <= subgroups::maxSupportedSubgroupSize(); i *= 2)
102 {
103 bdy << " if (gl_SubgroupSize >= " << i << ")\n"
104 << " {\n"
105 << " uvec4 contribution = uvec4(0);\n"
106 << " const uint modID = gl_SubgroupInvocationID % 32;\n"
107 << " switch (gl_SubgroupInvocationID / 32)\n"
108 << " {\n"
109 << " case 0: contribution.x = 1 << modID; break;\n"
110 << " case 1: contribution.y = 1 << modID; break;\n"
111 << " case 2: contribution.z = 1 << modID; break;\n"
112 << " case 3: contribution.w = 1 << modID; break;\n"
113 << " }\n"
114 << " uvec4 result = subgroupClusteredOr(contribution, " << i << ");\n"
115 << " uint rootID = gl_SubgroupInvocationID & ~(" << i - 1 << ");\n"
116 << " for (uint i = 0; i < " << i << "; i++)\n"
117 << " {\n"
118 << " uint nextID = rootID + i;\n"
119 << " if (subgroupBallotBitExtract(mask, nextID) ^^ subgroupBallotBitExtract(result, nextID))\n"
120 << " {\n"
121 << " tempResult = 0;\n"
122 << " }\n"
123 << " }\n"
124 << " }\n";
125 }
126 }
127 else
128 {
129 bdy << " uint cluster[4] =\n"
130 << " {\n"
131 << " subgroupQuadBroadcast(gl_SubgroupInvocationID, 0),\n"
132 << " subgroupQuadBroadcast(gl_SubgroupInvocationID, 1),\n"
133 << " subgroupQuadBroadcast(gl_SubgroupInvocationID, 2),\n"
134 << " subgroupQuadBroadcast(gl_SubgroupInvocationID, 3)\n"
135 << " };\n"
136 << " uint rootID = gl_SubgroupInvocationID & ~0x3;\n"
137 << " for (uint i = 0; i < 4; i++)\n"
138 << " {\n"
139 << " uint nextID = rootID + i;\n"
140 << " if (subgroupBallotBitExtract(mask, nextID) && (cluster[i] != nextID))\n"
141 << " {\n"
142 << " tempResult = mask.x;\n"
143 << " }\n"
144 << " }\n";
145 }
146
147 bdy << " tempRes = tempResult;\n";
148
149 return bdy.str();
150 }
151
getFramebufferPerStageHeadDeclarations(const CaseDefinition & caseDef)152 vector<string> getFramebufferPerStageHeadDeclarations(const CaseDefinition &caseDef)
153 {
154 vector<string> result;
155
156 DE_UNREF(caseDef);
157
158 result.push_back("layout(location = 0) out float result;\n");
159 result.push_back("layout(location = 0) out float out_color;\n");
160 result.push_back("layout(location = 0) out float out_color[];\n");
161 result.push_back("layout(location = 0) out float out_color;\n");
162
163 return result;
164 }
165
initFrameBufferPrograms(SourceCollections & programCollection,CaseDefinition caseDef)166 void initFrameBufferPrograms(SourceCollections &programCollection, CaseDefinition caseDef)
167 {
168 const ShaderBuildOptions buildOptions(programCollection.usedVulkanVersion, SPIRV_VERSION_1_3, 0u);
169 const string extHeader = getExtHeader(caseDef);
170 const string testSrc = getBodySource(caseDef);
171 const vector<string> headDeclarations = getFramebufferPerStageHeadDeclarations(caseDef);
172 const bool pointSizeSupported = *caseDef.geometryPointSizeSupported;
173
174 subgroups::initStdFrameBufferPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT,
175 pointSizeSupported, extHeader, testSrc, "", headDeclarations);
176 }
177
getPerStageHeadDeclarations(const CaseDefinition & caseDef)178 vector<string> getPerStageHeadDeclarations(const CaseDefinition &caseDef)
179 {
180 const uint32_t stageCount = subgroups::getStagesCount(caseDef.shaderStage);
181 const bool fragment = (caseDef.shaderStage & VK_SHADER_STAGE_FRAGMENT_BIT) != 0;
182 vector<string> result(stageCount, string());
183
184 if (fragment)
185 result.reserve(result.size() + 1);
186
187 for (size_t i = 0; i < result.size(); ++i)
188 {
189 result[i] = "layout(set = 0, binding = " + de::toString(i) +
190 ", std430) buffer Buffer1\n"
191 "{\n"
192 " uint result[];\n"
193 "};\n";
194 }
195
196 if (fragment)
197 {
198 const string fragPart = "layout(location = 0) out uint result;\n";
199
200 result.push_back(fragPart);
201 }
202
203 return result;
204 }
205
initPrograms(SourceCollections & programCollection,CaseDefinition caseDef)206 void initPrograms(SourceCollections &programCollection, CaseDefinition caseDef)
207 {
208 #ifndef CTS_USES_VULKANSC
209 const bool spirv14required =
210 (isAllRayTracingStages(caseDef.shaderStage) || isAllMeshShadingStages(caseDef.shaderStage));
211 #else
212 const bool spirv14required = false;
213 #endif // CTS_USES_VULKANSC
214 const SpirvVersion spirvVersion = spirv14required ? SPIRV_VERSION_1_4 : SPIRV_VERSION_1_3;
215 const ShaderBuildOptions buildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, spirv14required);
216 const string extHeader = getExtHeader(caseDef);
217 const string testSrc = getBodySource(caseDef);
218 const vector<string> headDeclarations = getPerStageHeadDeclarations(caseDef);
219 const bool pointSizeSupport = *caseDef.geometryPointSizeSupported;
220
221 subgroups::initStdPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT,
222 pointSizeSupport, extHeader, testSrc, "", headDeclarations);
223 }
224
supportedCheck(Context & context,CaseDefinition caseDef)225 void supportedCheck(Context &context, CaseDefinition caseDef)
226 {
227 if (!subgroups::isSubgroupSupported(context))
228 TCU_THROW(NotSupportedError, "Subgroup operations are not supported");
229
230 if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_BALLOT_BIT))
231 {
232 TCU_THROW(NotSupportedError, "Device does not support subgroup ballot operations");
233 }
234
235 if (OPTYPE_CLUSTERED == caseDef.opType)
236 {
237 if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_CLUSTERED_BIT))
238 {
239 TCU_THROW(NotSupportedError, "Test requires that clustered operations are supported!");
240 }
241 }
242
243 if (OPTYPE_QUAD == caseDef.opType)
244 {
245 if (!subgroups::areQuadOperationsSupportedForStages(context, caseDef.shaderStage))
246 {
247 TCU_THROW(NotSupportedError, "Test requires that quad operations are supported!");
248 }
249 }
250
251 if (caseDef.requiredSubgroupSize)
252 {
253 context.requireDeviceFunctionality("VK_EXT_subgroup_size_control");
254
255 #ifndef CTS_USES_VULKANSC
256 const VkPhysicalDeviceSubgroupSizeControlFeatures &subgroupSizeControlFeatures =
257 context.getSubgroupSizeControlFeatures();
258 const VkPhysicalDeviceSubgroupSizeControlProperties &subgroupSizeControlProperties =
259 context.getSubgroupSizeControlProperties();
260 #else
261 const VkPhysicalDeviceSubgroupSizeControlFeaturesEXT &subgroupSizeControlFeatures =
262 context.getSubgroupSizeControlFeaturesEXT();
263 const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT &subgroupSizeControlProperties =
264 context.getSubgroupSizeControlPropertiesEXT();
265 #endif // CTS_USES_VULKANSC
266
267 if (subgroupSizeControlFeatures.subgroupSizeControl == false)
268 TCU_THROW(NotSupportedError, "Device does not support varying subgroup sizes nor required subgroup size");
269
270 if (subgroupSizeControlFeatures.computeFullSubgroups == false)
271 TCU_THROW(NotSupportedError, "Device does not support full subgroups in compute shaders");
272
273 if ((subgroupSizeControlProperties.requiredSubgroupSizeStages & caseDef.shaderStage) != caseDef.shaderStage)
274 TCU_THROW(NotSupportedError, "Required subgroup size is not supported for shader stage");
275 }
276
277 *caseDef.geometryPointSizeSupported = subgroups::isTessellationAndGeometryPointSizeSupported(context);
278
279 #ifndef CTS_USES_VULKANSC
280 if (isAllRayTracingStages(caseDef.shaderStage))
281 {
282 context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
283 }
284 else if (isAllMeshShadingStages(caseDef.shaderStage))
285 {
286 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
287 context.requireDeviceFunctionality("VK_EXT_mesh_shader");
288
289 if ((caseDef.shaderStage & VK_SHADER_STAGE_TASK_BIT_EXT) != 0u)
290 {
291 const auto &features = context.getMeshShaderFeaturesEXT();
292 if (!features.taskShader)
293 TCU_THROW(NotSupportedError, "Task shaders not supported");
294 }
295 }
296 #endif // CTS_USES_VULKANSC
297
298 subgroups::supportedCheckShader(context, caseDef.shaderStage);
299 }
300
noSSBOtest(Context & context,const CaseDefinition caseDef)301 TestStatus noSSBOtest(Context &context, const CaseDefinition caseDef)
302 {
303 switch (caseDef.shaderStage)
304 {
305 case VK_SHADER_STAGE_VERTEX_BIT:
306 return subgroups::makeVertexFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
307 checkVertexPipelineStages);
308 case VK_SHADER_STAGE_GEOMETRY_BIT:
309 return subgroups::makeGeometryFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
310 checkVertexPipelineStages);
311 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
312 return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
313 checkVertexPipelineStages, caseDef.shaderStage);
314 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
315 return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
316 checkVertexPipelineStages, caseDef.shaderStage);
317 default:
318 TCU_THROW(InternalError, "Unhandled shader stage");
319 }
320 }
321
test(Context & context,const CaseDefinition caseDef)322 TestStatus test(Context &context, const CaseDefinition caseDef)
323 {
324 const bool isCompute = isAllComputeStages(caseDef.shaderStage);
325 #ifndef CTS_USES_VULKANSC
326 const bool isMesh = isAllMeshShadingStages(caseDef.shaderStage);
327 #else
328 const bool isMesh = false;
329 #endif // CTS_USES_VULKANSC
330 DE_ASSERT(!(isCompute && isMesh));
331
332 if (isCompute || isMesh)
333 {
334 #ifndef CTS_USES_VULKANSC
335 const VkPhysicalDeviceSubgroupSizeControlProperties &subgroupSizeControlProperties =
336 context.getSubgroupSizeControlProperties();
337 #else
338 const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT &subgroupSizeControlProperties =
339 context.getSubgroupSizeControlPropertiesEXT();
340 #endif // CTS_USES_VULKANSC
341 TestLog &log = context.getTestContext().getLog();
342
343 if (caseDef.requiredSubgroupSize == false)
344 {
345 if (isCompute)
346 return subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkComputeOrMesh);
347 else
348 return subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkComputeOrMesh);
349 }
350
351 log << TestLog::Message << "Testing required subgroup size range ["
352 << subgroupSizeControlProperties.minSubgroupSize << ", " << subgroupSizeControlProperties.maxSubgroupSize
353 << "]" << TestLog::EndMessage;
354
355 // According to the spec, requiredSubgroupSize must be a power-of-two integer.
356 for (uint32_t size = subgroupSizeControlProperties.minSubgroupSize;
357 size <= subgroupSizeControlProperties.maxSubgroupSize; size *= 2)
358 {
359 TestStatus result(QP_TEST_RESULT_INTERNAL_ERROR, "Internal Error");
360
361 if (isCompute)
362 result = subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
363 checkComputeOrMesh, size);
364 else
365 result =
366 subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkComputeOrMesh, size);
367
368 if (result.getCode() != QP_TEST_RESULT_PASS)
369 {
370 log << TestLog::Message << "subgroupSize " << size << " failed" << TestLog::EndMessage;
371 return result;
372 }
373 }
374
375 return TestStatus::pass("OK");
376 }
377 else if (isAllGraphicsStages(caseDef.shaderStage))
378 {
379 const VkShaderStageFlags stages = subgroups::getPossibleGraphicsSubgroupStages(context, caseDef.shaderStage);
380
381 return subgroups::allStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages,
382 stages);
383 }
384 #ifndef CTS_USES_VULKANSC
385 else if (isAllRayTracingStages(caseDef.shaderStage))
386 {
387 const VkShaderStageFlags stages = subgroups::getPossibleRayTracingSubgroupStages(context, caseDef.shaderStage);
388
389 return subgroups::allRayTracingStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL,
390 checkVertexPipelineStages, stages);
391 }
392 #endif // CTS_USES_VULKANSC
393 else
394 TCU_THROW(InternalError, "Unknown stage or invalid stage set");
395 }
396 } // namespace
397
398 namespace vkt
399 {
400 namespace subgroups
401 {
createSubgroupsShapeTests(TestContext & testCtx)402 TestCaseGroup *createSubgroupsShapeTests(TestContext &testCtx)
403 {
404 de::MovePtr<TestCaseGroup> group(new TestCaseGroup(testCtx, "shape"));
405 de::MovePtr<TestCaseGroup> graphicGroup(new TestCaseGroup(testCtx, "graphics"));
406 de::MovePtr<TestCaseGroup> computeGroup(new TestCaseGroup(testCtx, "compute"));
407 de::MovePtr<TestCaseGroup> framebufferGroup(new TestCaseGroup(testCtx, "framebuffer"));
408 #ifndef CTS_USES_VULKANSC
409 de::MovePtr<TestCaseGroup> raytracingGroup(new TestCaseGroup(testCtx, "ray_tracing"));
410 de::MovePtr<TestCaseGroup> meshGroup(new TestCaseGroup(testCtx, "mesh"));
411 #endif // CTS_USES_VULKANSC
412 const VkShaderStageFlags fbStages[] = {
413 VK_SHADER_STAGE_VERTEX_BIT,
414 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
415 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
416 VK_SHADER_STAGE_GEOMETRY_BIT,
417 };
418 #ifndef CTS_USES_VULKANSC
419 const VkShaderStageFlags meshStages[] = {
420 VK_SHADER_STAGE_MESH_BIT_EXT,
421 VK_SHADER_STAGE_TASK_BIT_EXT,
422 };
423 #endif // CTS_USES_VULKANSC
424 const bool boolValues[] = {false, true};
425
426 for (int opTypeIndex = 0; opTypeIndex < OPTYPE_LAST; ++opTypeIndex)
427 {
428 const OpType opType = static_cast<OpType>(opTypeIndex);
429 const string op = de::toLower(getOpTypeName(opType));
430
431 for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
432 {
433 const bool requiredSubgroupSize = boolValues[groupSizeNdx];
434 const string testName = op + (requiredSubgroupSize ? "_requiredsubgroupsize" : "");
435 const CaseDefinition caseDef = {
436 opType, // OpType opType;
437 VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlags shaderStage;
438 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
439 requiredSubgroupSize // bool requiredSubgroupSize;
440 };
441
442 addFunctionCaseWithPrograms(computeGroup.get(), testName, supportedCheck, initPrograms, test, caseDef);
443 }
444
445 #ifndef CTS_USES_VULKANSC
446 for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
447 {
448 for (const auto &stage : meshStages)
449 {
450 const bool requiredSubgroupSize = boolValues[groupSizeNdx];
451 const string testName =
452 op + (requiredSubgroupSize ? "_requiredsubgroupsize" : "") + "_" + getShaderStageName(stage);
453 const CaseDefinition caseDef = {
454 opType, // OpType opType;
455 stage, // VkShaderStageFlags shaderStage;
456 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
457 requiredSubgroupSize // bool requiredSubgroupSize;
458 };
459
460 addFunctionCaseWithPrograms(meshGroup.get(), testName, supportedCheck, initPrograms, test, caseDef);
461 }
462 }
463 #endif // CTS_USES_VULKANSC
464
465 {
466 const CaseDefinition caseDef = {
467 opType, // OpType opType;
468 VK_SHADER_STAGE_ALL_GRAPHICS, // VkShaderStageFlags shaderStage;
469 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
470 false // bool requiredSubgroupSize;
471 };
472
473 addFunctionCaseWithPrograms(graphicGroup.get(), op, supportedCheck, initPrograms, test, caseDef);
474 }
475
476 #ifndef CTS_USES_VULKANSC
477 {
478 const CaseDefinition caseDef = {
479 opType, // OpType opType;
480 SHADER_STAGE_ALL_RAY_TRACING, // VkShaderStageFlags shaderStage;
481 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
482 false // bool requiredSubgroupSize;
483 };
484
485 addFunctionCaseWithPrograms(raytracingGroup.get(), op, supportedCheck, initPrograms, test, caseDef);
486 }
487 #endif // CTS_USES_VULKANSC
488
489 for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(fbStages); ++stageIndex)
490 {
491 const CaseDefinition caseDef = {
492 opType, // OpType opType;
493 fbStages[stageIndex], // VkShaderStageFlags shaderStage;
494 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
495 false // bool requiredSubgroupSize;
496 };
497 const string testName = op + "_" + getShaderStageName(caseDef.shaderStage);
498
499 addFunctionCaseWithPrograms(framebufferGroup.get(), testName, supportedCheck, initFrameBufferPrograms,
500 noSSBOtest, caseDef);
501 }
502 }
503
504 group->addChild(graphicGroup.release());
505 group->addChild(computeGroup.release());
506 group->addChild(framebufferGroup.release());
507 #ifndef CTS_USES_VULKANSC
508 group->addChild(raytracingGroup.release());
509 group->addChild(meshGroup.release());
510 #endif // CTS_USES_VULKANSC
511
512 return group.release();
513 }
514
515 } // namespace subgroups
516 } // namespace vkt
517