1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2019 The Khronos Group Inc.
6 * Copyright (c) 2019 Valve Corporation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 */ /*!
21 * \file
22 * \brief Subgroups Tests
23 */ /*--------------------------------------------------------------------*/
24
25 #include "vktSubgroupsBallotMasksTests.hpp"
26 #include "vktSubgroupsTestsUtils.hpp"
27
28 #include <string>
29 #include <vector>
30
31 using namespace tcu;
32 using namespace std;
33 using namespace vk;
34 using namespace vkt;
35
36 namespace
37 {
38
39 enum MaskType
40 {
41 MASKTYPE_EQ = 0,
42 MASKTYPE_GE,
43 MASKTYPE_GT,
44 MASKTYPE_LE,
45 MASKTYPE_LT,
46 MASKTYPE_LAST
47 };
48
49 struct CaseDefinition
50 {
51 MaskType maskType;
52 VkShaderStageFlags shaderStage;
53 de::SharedPtr<bool> geometryPointSizeSupported;
54 deBool requiredSubgroupSize;
55 };
56
checkVertexPipelineStages(const void* internalData, vector<const void*> datas, deUint32 width, deUint32)57 static bool checkVertexPipelineStages (const void* internalData,
58 vector<const void*> datas,
59 deUint32 width,
60 deUint32)
61 {
62 DE_UNREF(internalData);
63
64 return subgroups::check(datas, width, 0xf);
65 }
66
checkComputeOrMesh(const void* internalData, vector<const void*> datas, const deUint32 numWorkgroups[3], const deUint32 localSize[3], deUint32)67 static bool checkComputeOrMesh (const void* internalData,
68 vector<const void*> datas,
69 const deUint32 numWorkgroups[3],
70 const deUint32 localSize[3],
71 deUint32)
72 {
73 DE_UNREF(internalData);
74
75 return subgroups::checkComputeOrMesh(datas, numWorkgroups, localSize, 0xf);
76 }
77
getMaskTypeName(const MaskType maskType)78 string getMaskTypeName (const MaskType maskType)
79 {
80 switch (maskType)
81 {
82 case MASKTYPE_EQ: return "gl_SubGroupEqMaskARB";
83 case MASKTYPE_GE: return "gl_SubGroupGeMaskARB";
84 case MASKTYPE_GT: return "gl_SubGroupGtMaskARB";
85 case MASKTYPE_LE: return "gl_SubGroupLeMaskARB";
86 case MASKTYPE_LT: return "gl_SubGroupLtMaskARB";
87 default: TCU_THROW(InternalError, "Unsupported mask type");
88 }
89 }
90
getBodySource(const CaseDefinition& caseDef)91 string getBodySource (const CaseDefinition& caseDef)
92 {
93 string body =
94 " uint64_t value = " + getMaskTypeName(caseDef.maskType) + ";\n"
95 " bool temp = true;\n";
96
97 switch(caseDef.maskType)
98 {
99 case MASKTYPE_EQ:
100 body += " uint64_t mask = uint64_t(1) << gl_SubGroupInvocationARB;\n"
101 " temp = (value & mask) != 0;\n";
102 break;
103 case MASKTYPE_GE:
104 body += " for (uint i = 0; i < gl_SubGroupSizeARB; i++) {\n"
105 " uint64_t mask = uint64_t(1) << i;\n"
106 " if (i >= gl_SubGroupInvocationARB && (value & mask) == 0)\n"
107 " temp = false;\n"
108 " if (i < gl_SubGroupInvocationARB && (value & mask) != 0)\n"
109 " temp = false;\n"
110 " };\n";
111 break;
112 case MASKTYPE_GT:
113 body += " for (uint i = 0; i < gl_SubGroupSizeARB; i++) {\n"
114 " uint64_t mask = uint64_t(1) << i;\n"
115 " if (i > gl_SubGroupInvocationARB && (value & mask) == 0)\n"
116 " temp = false;\n"
117 " if (i <= gl_SubGroupInvocationARB && (value & mask) != 0)\n"
118 " temp = false;\n"
119 " };\n";
120 break;
121 case MASKTYPE_LE:
122 body += " for (uint i = 0; i < gl_SubGroupSizeARB; i++) {\n"
123 " uint64_t mask = uint64_t(1) << i;\n"
124 " if (i <= gl_SubGroupInvocationARB && (value & mask) == 0)\n"
125 " temp = false;\n"
126 " if (i > gl_SubGroupInvocationARB && (value & mask) != 0)\n"
127 " temp = false;\n"
128 " };\n";
129 break;
130 case MASKTYPE_LT:
131 body += " for (uint i = 0; i < gl_SubGroupSizeARB; i++) {\n"
132 " uint64_t mask = uint64_t(1) << i;\n"
133 " if (i < gl_SubGroupInvocationARB && (value & mask) == 0)\n"
134 " temp = false;\n"
135 " if (i >= gl_SubGroupInvocationARB && (value & mask) != 0)\n"
136 " temp = false;\n"
137 " };\n";
138 break;
139 default:
140 TCU_THROW(InternalError, "Unknown mask type");
141 }
142
143 body += " uint tempResult = temp ? 0xf : 0x2;\n";
144 body += " tempRes = tempResult;\n";
145
146 return body;
147 }
148
getExtHeader(const CaseDefinition&)149 string getExtHeader (const CaseDefinition&)
150 {
151 return
152 "#extension GL_ARB_shader_ballot: enable\n"
153 "#extension GL_ARB_gpu_shader_int64: enable\n";
154 }
155
getPerStageHeadDeclarations(const CaseDefinition& caseDef)156 vector<string> getPerStageHeadDeclarations (const CaseDefinition& caseDef)
157 {
158 const deUint32 stageCount = subgroups::getStagesCount(caseDef.shaderStage);
159 const bool fragment = (caseDef.shaderStage & VK_SHADER_STAGE_FRAGMENT_BIT) != 0;
160 vector<string> result (stageCount, string());
161
162 if (fragment)
163 result.reserve(result.size() + 1);
164
165 for (size_t i = 0; i < result.size(); ++i)
166 {
167 result[i] =
168 "layout(set = 0, binding = " + de::toString(i) + ", std430) buffer Buffer1\n"
169 "{\n"
170 " uint result[];\n"
171 "};\n";
172 }
173
174 if (fragment)
175 {
176 const string fragPart =
177 "layout(location = 0) out uint result;\n";
178
179 result.push_back(fragPart);
180 }
181
182 return result;
183 }
184
getFramebufferPerStageHeadDeclarations(const CaseDefinition& caseDef)185 vector<string> getFramebufferPerStageHeadDeclarations (const CaseDefinition& caseDef)
186 {
187 vector<string> result;
188
189 DE_UNREF(caseDef);
190
191 result.push_back("layout(location = 0) out float result;\n");
192 result.push_back("layout(location = 0) out float out_color;\n");
193 result.push_back("layout(location = 0) out float out_color[];\n");
194 result.push_back("layout(location = 0) out float out_color;\n");
195
196 return result;
197 }
198
initFrameBufferPrograms(SourceCollections& programCollection, CaseDefinition caseDef)199 void initFrameBufferPrograms (SourceCollections& programCollection, CaseDefinition caseDef)
200 {
201 const ShaderBuildOptions buildOptions (programCollection.usedVulkanVersion, SPIRV_VERSION_1_3, 0u);
202 const string extHeader = getExtHeader(caseDef);
203 const string testSrc = getBodySource(caseDef);
204 const vector<string> headDeclarations = getFramebufferPerStageHeadDeclarations(caseDef);
205 const bool pointSizeSupported = *caseDef.geometryPointSizeSupported;
206
207 subgroups::initStdFrameBufferPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT, pointSizeSupported, extHeader, testSrc, "", headDeclarations);
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 (!context.requireDeviceFunctionality("VK_EXT_shader_subgroup_ballot"))
233 {
234 TCU_THROW(NotSupportedError, "Device does not support VK_EXT_shader_subgroup_ballot extension");
235 }
236
237 if (!subgroups::isInt64SupportedForDevice(context))
238 TCU_THROW(NotSupportedError, "Int64 is not supported");
239
240 if (caseDef.requiredSubgroupSize)
241 {
242 context.requireDeviceFunctionality("VK_EXT_subgroup_size_control");
243
244 #ifndef CTS_USES_VULKANSC
245 const VkPhysicalDeviceSubgroupSizeControlFeatures& subgroupSizeControlFeatures = context.getSubgroupSizeControlFeatures();
246 const VkPhysicalDeviceSubgroupSizeControlProperties& subgroupSizeControlProperties = context.getSubgroupSizeControlProperties();
247 #else
248 const VkPhysicalDeviceSubgroupSizeControlFeaturesEXT& subgroupSizeControlFeatures = context.getSubgroupSizeControlFeaturesEXT();
249 const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& subgroupSizeControlProperties = context.getSubgroupSizeControlPropertiesEXT();
250 #endif // CTS_USES_VULKANSC
251
252 if (subgroupSizeControlFeatures.subgroupSizeControl == DE_FALSE)
253 TCU_THROW(NotSupportedError, "Device does not support varying subgroup sizes nor required subgroup size");
254
255 if (subgroupSizeControlFeatures.computeFullSubgroups == DE_FALSE)
256 TCU_THROW(NotSupportedError, "Device does not support full subgroups in compute shaders");
257
258 if ((subgroupSizeControlProperties.requiredSubgroupSizeStages & caseDef.shaderStage) != caseDef.shaderStage)
259 TCU_THROW(NotSupportedError, "Required subgroup size is not supported for shader stage");
260 }
261
262 *caseDef.geometryPointSizeSupported = subgroups::isTessellationAndGeometryPointSizeSupported(context);
263
264 #ifndef CTS_USES_VULKANSC
265 if (isAllRayTracingStages(caseDef.shaderStage))
266 {
267 context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
268 }
269 else if (isAllMeshShadingStages(caseDef.shaderStage))
270 {
271 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
272 context.requireDeviceFunctionality("VK_EXT_mesh_shader");
273
274 if ((caseDef.shaderStage & VK_SHADER_STAGE_TASK_BIT_EXT) != 0u)
275 {
276 const auto& features = context.getMeshShaderFeaturesEXT();
277 if (!features.taskShader)
278 TCU_THROW(NotSupportedError, "Task shaders not supported");
279 }
280 }
281 #endif // CTS_USES_VULKANSC
282
283 subgroups::supportedCheckShader(context, caseDef.shaderStage);
284 }
285
noSSBOtest(Context& context, const CaseDefinition caseDef)286 TestStatus noSSBOtest (Context& context, const CaseDefinition caseDef)
287 {
288 switch (caseDef.shaderStage)
289 {
290 case VK_SHADER_STAGE_VERTEX_BIT: return subgroups::makeVertexFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
291 case VK_SHADER_STAGE_GEOMETRY_BIT: return subgroups::makeGeometryFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
292 case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
293 case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
294 default: TCU_THROW(InternalError, "Unhandled shader stage");
295 }
296 }
297
test(Context& context, const CaseDefinition caseDef)298 TestStatus test (Context& context, const CaseDefinition caseDef)
299 {
300 const bool isCompute = isAllComputeStages(caseDef.shaderStage);
301 #ifndef CTS_USES_VULKANSC
302 const bool isMesh = isAllMeshShadingStages(caseDef.shaderStage);
303 #else
304 const bool isMesh = false;
305 #endif // CTS_USES_VULKANSC
306 DE_ASSERT(!(isCompute && isMesh));
307
308 if (isCompute || isMesh)
309 {
310 #ifndef CTS_USES_VULKANSC
311 const VkPhysicalDeviceSubgroupSizeControlProperties& subgroupSizeControlProperties = context.getSubgroupSizeControlProperties();
312 #else
313 const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& subgroupSizeControlProperties = context.getSubgroupSizeControlPropertiesEXT();
314 #endif // CTS_USES_VULKANSC
315 TestLog& log = context.getTestContext().getLog();
316
317 if (caseDef.requiredSubgroupSize == DE_FALSE)
318 {
319 if (isCompute)
320 return subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkComputeOrMesh);
321 else
322 return subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, nullptr, 0, nullptr, checkComputeOrMesh);
323 }
324
325 log << TestLog::Message << "Testing required subgroup size range [" << subgroupSizeControlProperties.minSubgroupSize << ", "
326 << subgroupSizeControlProperties.maxSubgroupSize << "]" << TestLog::EndMessage;
327
328 // According to the spec, requiredSubgroupSize must be a power-of-two integer.
329 for (deUint32 size = subgroupSizeControlProperties.minSubgroupSize; size <= subgroupSizeControlProperties.maxSubgroupSize; size *= 2)
330 {
331 TestStatus result (QP_TEST_RESULT_INTERNAL_ERROR, "Internal Error");
332
333 if (isCompute)
334 result = subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0u, DE_NULL, checkComputeOrMesh, size);
335 else
336 result = subgroups::makeMeshTest(context, VK_FORMAT_R32_UINT, nullptr, 0u, nullptr, checkComputeOrMesh, size);
337
338 if (result.getCode() != QP_TEST_RESULT_PASS)
339 {
340 log << TestLog::Message << "subgroupSize " << size << " failed" << TestLog::EndMessage;
341 return result;
342 }
343 }
344
345 return TestStatus::pass("OK");
346 }
347 else if (isAllGraphicsStages(caseDef.shaderStage))
348 {
349 const VkShaderStageFlags stages = subgroups::getPossibleGraphicsSubgroupStages(context, caseDef.shaderStage);
350
351 return subgroups::allStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, stages);
352 }
353 #ifndef CTS_USES_VULKANSC
354 else if (isAllRayTracingStages(caseDef.shaderStage))
355 {
356 const VkShaderStageFlags stages = subgroups::getPossibleRayTracingSubgroupStages(context, caseDef.shaderStage);
357
358 return subgroups::allRayTracingStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, stages);
359 }
360 #endif // CTS_USES_VULKANSC
361 else
362 TCU_THROW(InternalError, "Unknown stage or invalid stage set");
363 }
364 }
365
366 namespace vkt
367 {
368 namespace subgroups
369 {
createSubgroupsBallotMasksTests(TestContext& testCtx)370 TestCaseGroup* createSubgroupsBallotMasksTests (TestContext& testCtx)
371 {
372 de::MovePtr<TestCaseGroup> group (new TestCaseGroup(testCtx, "ballot_mask", "VK_EXT_shader_subgroup_ballot mask category tests"));
373 de::MovePtr<TestCaseGroup> groupARB (new TestCaseGroup(testCtx, "ext_shader_subgroup_ballot", "VK_EXT_shader_subgroup_ballot masks category tests"));
374 de::MovePtr<TestCaseGroup> graphicGroup (new TestCaseGroup(testCtx, "graphics", "VK_EXT_shader_subgroup_ballot masks category tests: graphics"));
375 de::MovePtr<TestCaseGroup> computeGroup (new TestCaseGroup(testCtx, "compute", "VK_EXT_shader_subgroup_ballot masks category tests: compute"));
376 de::MovePtr<TestCaseGroup> framebufferGroup (new TestCaseGroup(testCtx, "framebuffer", "VK_EXT_shader_subgroup_ballot masks category tests: framebuffer"));
377 #ifndef CTS_USES_VULKANSC
378 de::MovePtr<TestCaseGroup> raytracingGroup (new TestCaseGroup(testCtx, "ray_tracing", "VK_EXT_shader_subgroup_ballot masks category tests: ray tracing"));
379 de::MovePtr<TestCaseGroup> meshGroup (new TestCaseGroup(testCtx, "mesh", "VK_EXT_shader_subgroup_ballot masks category tests: mesh shaders"));
380 #endif // CTS_USES_VULKANSC
381 const VkShaderStageFlags fbStages[] =
382 {
383 VK_SHADER_STAGE_VERTEX_BIT,
384 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
385 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
386 VK_SHADER_STAGE_GEOMETRY_BIT,
387 };
388 #ifndef CTS_USES_VULKANSC
389 const VkShaderStageFlags meshStages[] =
390 {
391 VK_SHADER_STAGE_MESH_BIT_EXT,
392 VK_SHADER_STAGE_TASK_BIT_EXT,
393 };
394 #endif // CTS_USES_VULKANSC
395 const deBool boolValues[] =
396 {
397 DE_FALSE,
398 DE_TRUE
399 };
400
401 for (int maskTypeIndex = 0; maskTypeIndex < MASKTYPE_LAST; ++maskTypeIndex)
402 {
403 const MaskType maskType = static_cast<MaskType>(maskTypeIndex);
404 const string mask = de::toLower(getMaskTypeName(maskType));
405
406 for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
407 {
408 const deBool requiredSubgroupSize = boolValues[groupSizeNdx];
409 const string testName = mask + (requiredSubgroupSize ? "_requiredsubgroupsize" : "");
410 const CaseDefinition caseDef =
411 {
412 maskType, // MaskType maskType;
413 VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlags shaderStage;
414 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
415 requiredSubgroupSize, // deBool requiredSubgroupSize;
416 };
417
418 addFunctionCaseWithPrograms(computeGroup.get(), testName, "", supportedCheck, initPrograms, test, caseDef);
419 }
420
421 #ifndef CTS_USES_VULKANSC
422 for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
423 {
424 for (const auto& stage : meshStages)
425 {
426 const deBool requiredSubgroupSize = boolValues[groupSizeNdx];
427 const string testName = mask + (requiredSubgroupSize ? "_requiredsubgroupsize" : "");
428 const CaseDefinition caseDef =
429 {
430 maskType, // MaskType maskType;
431 stage, // VkShaderStageFlags shaderStage;
432 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
433 requiredSubgroupSize, // deBool requiredSubgroupSize;
434 };
435
436 addFunctionCaseWithPrograms(meshGroup.get(), testName + "_" + getShaderStageName(stage), "", supportedCheck, initPrograms, test, caseDef);
437 }
438 }
439 #endif // CTS_USES_VULKANSC
440
441 {
442 const CaseDefinition caseDef =
443 {
444 maskType, // MaskType maskType;
445 VK_SHADER_STAGE_ALL_GRAPHICS, // VkShaderStageFlags shaderStage;
446 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
447 DE_FALSE // deBool requiredSubgroupSize;
448 };
449
450 addFunctionCaseWithPrograms(graphicGroup.get(), mask, "", supportedCheck, initPrograms, test, caseDef);
451 }
452
453 #ifndef CTS_USES_VULKANSC
454 {
455 const CaseDefinition caseDef =
456 {
457 maskType, // MaskType maskType;
458 SHADER_STAGE_ALL_RAY_TRACING, // VkShaderStageFlags shaderStage;
459 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
460 DE_FALSE // deBool requiredSubgroupSize;
461 };
462
463 addFunctionCaseWithPrograms(raytracingGroup.get(), mask, "", supportedCheck, initPrograms, test, caseDef);
464 }
465 #endif // CTS_USES_VULKANSC
466
467 for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(fbStages); ++stageIndex)
468 {
469 const CaseDefinition caseDef =
470 {
471 maskType, // MaskType maskType;
472 fbStages[stageIndex], // VkShaderStageFlags shaderStage;
473 de::SharedPtr<bool>(new bool), // de::SharedPtr<bool> geometryPointSizeSupported;
474 DE_FALSE // deBool requiredSubgroupSize;
475 };
476 const string testName = mask + "_" + getShaderStageName(caseDef.shaderStage);
477
478 addFunctionCaseWithPrograms(framebufferGroup.get(), testName, "", supportedCheck, initFrameBufferPrograms, noSSBOtest, caseDef);
479 }
480 }
481
482 groupARB->addChild(graphicGroup.release());
483 groupARB->addChild(computeGroup.release());
484 groupARB->addChild(framebufferGroup.release());
485 #ifndef CTS_USES_VULKANSC
486 groupARB->addChild(raytracingGroup.release());
487 groupARB->addChild(meshGroup.release());
488 #endif // CTS_USES_VULKANSC
489 group->addChild(groupARB.release());
490
491 return group.release();
492 }
493
494 } // subgroups
495 } // vkt
496