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
checkComputeOrMesh(const void * internalData,vector<const void * > datas,const deUint32 numWorkgroups[3],const deUint32 localSize[3],deUint32)64 static bool checkComputeOrMesh (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::checkComputeOrMesh(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 #ifndef CTS_USES_VULKANSC
213 const bool spirv14required = (isAllRayTracingStages(caseDef.shaderStage) || isAllMeshShadingStages(caseDef.shaderStage));
214 #else
215 const bool spirv14required = false;
216 #endif // CTS_USES_VULKANSC
217 const SpirvVersion spirvVersion = spirv14required ? SPIRV_VERSION_1_4 : SPIRV_VERSION_1_3;
218 const ShaderBuildOptions buildOptions (programCollection.usedVulkanVersion, spirvVersion, 0u, spirv14required);
219 const string extHeader = getExtHeader(caseDef);
220 const string testSrc = getBodySource(caseDef);
221 const vector<string> headDeclarations = getPerStageHeadDeclarations(caseDef);
222 const bool pointSizeSupport = *caseDef.geometryPointSizeSupported;
223
224 subgroups::initStdPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT, pointSizeSupport, extHeader, testSrc, "", headDeclarations);
225 }
226
supportedCheck(Context & context,CaseDefinition caseDef)227 void supportedCheck (Context& context, CaseDefinition caseDef)
228 {
229 if (!subgroups::isSubgroupSupported(context))
230 TCU_THROW(NotSupportedError, "Subgroup operations are not supported");
231
232 if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_BALLOT_BIT))
233 {
234 TCU_THROW(NotSupportedError, "Device does not support subgroup ballot operations");
235 }
236
237 if (OPTYPE_CLUSTERED == caseDef.opType)
238 {
239 if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_CLUSTERED_BIT))
240 {
241 TCU_THROW(NotSupportedError, "Subgroup shape tests require that clustered operations are supported!");
242 }
243 }
244
245 if (OPTYPE_QUAD == caseDef.opType)
246 {
247 if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_QUAD_BIT))
248 {
249 TCU_THROW(NotSupportedError, "Subgroup shape tests require that quad operations are supported!");
250 }
251 }
252
253 if (caseDef.requiredSubgroupSize)
254 {
255 context.requireDeviceFunctionality("VK_EXT_subgroup_size_control");
256
257 #ifndef CTS_USES_VULKANSC
258 const VkPhysicalDeviceSubgroupSizeControlFeatures& subgroupSizeControlFeatures = context.getSubgroupSizeControlFeatures();
259 const VkPhysicalDeviceSubgroupSizeControlProperties& subgroupSizeControlProperties = context.getSubgroupSizeControlProperties();
260 #else
261 const VkPhysicalDeviceSubgroupSizeControlFeaturesEXT& subgroupSizeControlFeatures = context.getSubgroupSizeControlFeaturesEXT();
262 const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& subgroupSizeControlProperties = context.getSubgroupSizeControlPropertiesEXT();
263 #endif // CTS_USES_VULKANSC
264
265 if (subgroupSizeControlFeatures.subgroupSizeControl == DE_FALSE)
266 TCU_THROW(NotSupportedError, "Device does not support varying subgroup sizes nor required subgroup size");
267
268 if (subgroupSizeControlFeatures.computeFullSubgroups == DE_FALSE)
269 TCU_THROW(NotSupportedError, "Device does not support full subgroups in compute shaders");
270
271 if ((subgroupSizeControlProperties.requiredSubgroupSizeStages & caseDef.shaderStage) != caseDef.shaderStage)
272 TCU_THROW(NotSupportedError, "Required subgroup size is not supported for shader stage");
273 }
274
275 *caseDef.geometryPointSizeSupported = subgroups::isTessellationAndGeometryPointSizeSupported(context);
276
277 #ifndef CTS_USES_VULKANSC
278 if (isAllRayTracingStages(caseDef.shaderStage))
279 {
280 context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
281 }
282 else if (isAllMeshShadingStages(caseDef.shaderStage))
283 {
284 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
285 context.requireDeviceFunctionality("VK_EXT_mesh_shader");
286
287 if ((caseDef.shaderStage & VK_SHADER_STAGE_TASK_BIT_EXT) != 0u)
288 {
289 const auto& features = context.getMeshShaderFeaturesEXT();
290 if (!features.taskShader)
291 TCU_THROW(NotSupportedError, "Task shaders not supported");
292 }
293 }
294 #endif // CTS_USES_VULKANSC
295
296 subgroups::supportedCheckShader(context, caseDef.shaderStage);
297 }
298
noSSBOtest(Context & context,const CaseDefinition caseDef)299 TestStatus noSSBOtest (Context& context, const CaseDefinition caseDef)
300 {
301 switch (caseDef.shaderStage)
302 {
303 case VK_SHADER_STAGE_VERTEX_BIT: return subgroups::makeVertexFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
304 case VK_SHADER_STAGE_GEOMETRY_BIT: return subgroups::makeGeometryFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
305 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, caseDef.shaderStage);
306 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, caseDef.shaderStage);
307 default: TCU_THROW(InternalError, "Unhandled shader stage");
308 }
309 }
310
test(Context & context,const CaseDefinition caseDef)311 TestStatus test (Context& context, const CaseDefinition caseDef)
312 {
313 const bool isCompute = isAllComputeStages(caseDef.shaderStage);
314 #ifndef CTS_USES_VULKANSC
315 const bool isMesh = isAllMeshShadingStages(caseDef.shaderStage);
316 #else
317 const bool isMesh = false;
318 #endif // CTS_USES_VULKANSC
319 DE_ASSERT(!(isCompute && isMesh));
320
321 if (isCompute || isMesh)
322 {
323 #ifndef CTS_USES_VULKANSC
324 const VkPhysicalDeviceSubgroupSizeControlProperties& subgroupSizeControlProperties = context.getSubgroupSizeControlProperties();
325 #else
326 const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& subgroupSizeControlProperties = context.getSubgroupSizeControlPropertiesEXT();
327 #endif // CTS_USES_VULKANSC
328 TestLog& log = context.getTestContext().getLog();
329
330 if (caseDef.requiredSubgroupSize == DE_FALSE)
331 {
332 if (isCompute)
333 return subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkComputeOrMesh);
334 else
335 return subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkComputeOrMesh);
336 }
337
338 log << TestLog::Message << "Testing required subgroup size range [" << subgroupSizeControlProperties.minSubgroupSize << ", "
339 << subgroupSizeControlProperties.maxSubgroupSize << "]" << TestLog::EndMessage;
340
341 // According to the spec, requiredSubgroupSize must be a power-of-two integer.
342 for (deUint32 size = subgroupSizeControlProperties.minSubgroupSize; size <= subgroupSizeControlProperties.maxSubgroupSize; size *= 2)
343 {
344 TestStatus result (QP_TEST_RESULT_INTERNAL_ERROR, "Internal Error");
345
346 if (isCompute)
347 result = subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkComputeOrMesh, size);
348 else
349 result = subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkComputeOrMesh, size);
350
351 if (result.getCode() != QP_TEST_RESULT_PASS)
352 {
353 log << TestLog::Message << "subgroupSize " << size << " failed" << TestLog::EndMessage;
354 return result;
355 }
356 }
357
358 return TestStatus::pass("OK");
359 }
360 else if (isAllGraphicsStages(caseDef.shaderStage))
361 {
362 const VkShaderStageFlags stages = subgroups::getPossibleGraphicsSubgroupStages(context, caseDef.shaderStage);
363
364 return subgroups::allStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, stages);
365 }
366 #ifndef CTS_USES_VULKANSC
367 else if (isAllRayTracingStages(caseDef.shaderStage))
368 {
369 const VkShaderStageFlags stages = subgroups::getPossibleRayTracingSubgroupStages(context, caseDef.shaderStage);
370
371 return subgroups::allRayTracingStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, stages);
372 }
373 #endif // CTS_USES_VULKANSC
374 else
375 TCU_THROW(InternalError, "Unknown stage or invalid stage set");
376 }
377 }
378
379 namespace vkt
380 {
381 namespace subgroups
382 {
createSubgroupsShapeTests(TestContext & testCtx)383 TestCaseGroup* createSubgroupsShapeTests (TestContext& testCtx)
384 {
385 de::MovePtr<TestCaseGroup> group (new TestCaseGroup(testCtx, "shape", "Subgroup shape category tests"));
386 de::MovePtr<TestCaseGroup> graphicGroup (new TestCaseGroup(testCtx, "graphics", "Subgroup shape category tests: graphics"));
387 de::MovePtr<TestCaseGroup> computeGroup (new TestCaseGroup(testCtx, "compute", "Subgroup shape category tests: compute"));
388 de::MovePtr<TestCaseGroup> framebufferGroup (new TestCaseGroup(testCtx, "framebuffer", "Subgroup shape category tests: framebuffer"));
389 #ifndef CTS_USES_VULKANSC
390 de::MovePtr<TestCaseGroup> raytracingGroup (new TestCaseGroup(testCtx, "ray_tracing", "Subgroup shape category tests: ray tracing"));
391 de::MovePtr<TestCaseGroup> meshGroup (new TestCaseGroup(testCtx, "mesh", "Subgroup shape category tests: mesh shading"));
392 #endif // CTS_USES_VULKANSC
393 const VkShaderStageFlags fbStages[] =
394 {
395 VK_SHADER_STAGE_VERTEX_BIT,
396 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
397 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
398 VK_SHADER_STAGE_GEOMETRY_BIT,
399 };
400 #ifndef CTS_USES_VULKANSC
401 const VkShaderStageFlags meshStages[] =
402 {
403 VK_SHADER_STAGE_MESH_BIT_EXT,
404 VK_SHADER_STAGE_TASK_BIT_EXT,
405 };
406 #endif // CTS_USES_VULKANSC
407 const deBool boolValues[] =
408 {
409 DE_FALSE,
410 DE_TRUE
411 };
412
413 for (int opTypeIndex = 0; opTypeIndex < OPTYPE_LAST; ++opTypeIndex)
414 {
415 const OpType opType = static_cast<OpType>(opTypeIndex);
416 const string op = de::toLower(getOpTypeName(opType));
417
418 for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
419 {
420 const deBool requiredSubgroupSize = boolValues[groupSizeNdx];
421 const string testName = op + (requiredSubgroupSize ? "_requiredsubgroupsize" : "");
422 const CaseDefinition caseDef =
423 {
424 opType, // OpType opType;
425 VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlags shaderStage;
426 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
427 requiredSubgroupSize // deBool requiredSubgroupSize;
428 };
429
430 addFunctionCaseWithPrograms(computeGroup.get(), testName, "", supportedCheck, initPrograms, test, caseDef);
431 }
432
433 #ifndef CTS_USES_VULKANSC
434 for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
435 {
436 for (const auto& stage : meshStages)
437 {
438 const deBool requiredSubgroupSize = boolValues[groupSizeNdx];
439 const string testName = op + (requiredSubgroupSize ? "_requiredsubgroupsize" : "") + "_" + getShaderStageName(stage);
440 const CaseDefinition caseDef =
441 {
442 opType, // OpType opType;
443 stage, // VkShaderStageFlags shaderStage;
444 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
445 requiredSubgroupSize // deBool requiredSubgroupSize;
446 };
447
448 addFunctionCaseWithPrograms(meshGroup.get(), testName, "", supportedCheck, initPrograms, test, caseDef);
449 }
450 }
451 #endif // CTS_USES_VULKANSC
452
453 {
454 const CaseDefinition caseDef =
455 {
456 opType, // OpType opType;
457 VK_SHADER_STAGE_ALL_GRAPHICS, // VkShaderStageFlags shaderStage;
458 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
459 DE_FALSE // deBool requiredSubgroupSize;
460 };
461
462 addFunctionCaseWithPrograms(graphicGroup.get(), op, "", supportedCheck, initPrograms, test, caseDef);
463 }
464
465 #ifndef CTS_USES_VULKANSC
466 {
467 const CaseDefinition caseDef =
468 {
469 opType, // OpType opType;
470 SHADER_STAGE_ALL_RAY_TRACING, // VkShaderStageFlags shaderStage;
471 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
472 DE_FALSE // deBool requiredSubgroupSize;
473 };
474
475 addFunctionCaseWithPrograms(raytracingGroup.get(), op, "", supportedCheck, initPrograms, test, caseDef);
476 }
477 #endif // CTS_USES_VULKANSC
478
479 for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(fbStages); ++stageIndex)
480 {
481 const CaseDefinition caseDef =
482 {
483 opType, // OpType opType;
484 fbStages[stageIndex], // VkShaderStageFlags shaderStage;
485 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
486 DE_FALSE // deBool requiredSubgroupSize;
487 };
488 const string testName = op + "_" + getShaderStageName(caseDef.shaderStage);
489
490 addFunctionCaseWithPrograms(framebufferGroup.get(), testName, "", supportedCheck, initFrameBufferPrograms, noSSBOtest, caseDef);
491 }
492 }
493
494 group->addChild(graphicGroup.release());
495 group->addChild(computeGroup.release());
496 group->addChild(framebufferGroup.release());
497 #ifndef CTS_USES_VULKANSC
498 group->addChild(raytracingGroup.release());
499 group->addChild(meshGroup.release());
500 #endif // CTS_USES_VULKANSC
501
502 return group.release();
503 }
504
505 } // subgroups
506 } // vkt
507