1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2020 Google LLC
6 * Copyright (c) 2020 The Khronos Group Inc.
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 Test new features in VK_KHR_shader_subgroup_uniform_control_flow
23 *//*--------------------------------------------------------------------*/
24
25 #include <amber/amber.h>
26
27 #include "tcuDefs.hpp"
28
29 #include "vkDefs.hpp"
30 #include "vkDeviceUtil.hpp"
31 #include "vktTestGroupUtil.hpp"
32 #include "vktAmberTestCase.hpp"
33 #include "vktSubgroupUniformControlFlowTests.hpp"
34 #include "vktTestGroupUtil.hpp"
35
36 namespace vkt
37 {
38 namespace subgroups
39 {
40 namespace
41 {
42
43 struct Case
44 {
Casevkt::subgroups::__anon2ae118660111::Case45 Case(const char *b, bool sw, bool use_ssc, vk::VkShaderStageFlagBits s, vk::VkSubgroupFeatureFlagBits o)
46 : basename(b)
47 , small_workgroups(sw)
48 , use_subgroup_size_control(use_ssc)
49 , stage(s)
50 {
51 operation = (vk::VkSubgroupFeatureFlagBits)(o | vk::VK_SUBGROUP_FEATURE_BASIC_BIT);
52 }
53 const char *basename;
54 bool small_workgroups;
55 bool use_subgroup_size_control;
56 vk::VkShaderStageFlagBits stage;
57 vk::VkSubgroupFeatureFlagBits operation;
58 };
59
60 struct CaseGroup
61 {
CaseGroupvkt::subgroups::__anon2ae118660111::CaseGroup62 CaseGroup(const char *the_data_dir, const char *the_subdir) : data_dir(the_data_dir), subdir(the_subdir)
63 {
64 }
addvkt::subgroups::__anon2ae118660111::CaseGroup65 void add(const char *basename, bool small_workgroups, bool use_subgroup_size_control,
66 vk::VkShaderStageFlagBits stage,
67 vk::VkSubgroupFeatureFlagBits operation = vk::VK_SUBGROUP_FEATURE_BASIC_BIT)
68 {
69 cases.push_back(Case(basename, small_workgroups, use_subgroup_size_control, stage, operation));
70 }
71
72 const char *data_dir;
73 const char *subdir;
74 std::vector<Case> cases;
75 };
76
77 class SubgroupUniformControlFlowTestCase : public cts_amber::AmberTestCase
78 {
79 public:
SubgroupUniformControlFlowTestCase(tcu::TestContext & testCtx,const char * name,const std::string & readFilename,bool small_workgroups,bool use_subgroup_size_control,vk::VkShaderStageFlagBits stage,vk::VkSubgroupFeatureFlagBits operation)80 SubgroupUniformControlFlowTestCase(tcu::TestContext &testCtx, const char *name, const std::string &readFilename,
81 bool small_workgroups, bool use_subgroup_size_control,
82 vk::VkShaderStageFlagBits stage, vk::VkSubgroupFeatureFlagBits operation)
83 : cts_amber::AmberTestCase(testCtx, name, "", readFilename)
84 , m_small_workgroups(small_workgroups)
85 , m_use_subgroup_size_control(use_subgroup_size_control)
86 , m_stage(stage)
87 , m_operation(operation)
88 {
89 }
90
91 virtual void checkSupport(Context &ctx) const; // override
92 private:
93 bool m_small_workgroups;
94 bool m_use_subgroup_size_control;
95 vk::VkShaderStageFlagBits m_stage;
96 vk::VkSubgroupFeatureFlagBits m_operation;
97 };
98
checkSupport(Context & ctx) const99 void SubgroupUniformControlFlowTestCase::checkSupport(Context &ctx) const
100 {
101 // Check required extensions.
102 ctx.requireInstanceFunctionality("VK_KHR_get_physical_device_properties2");
103 ctx.requireDeviceFunctionality("VK_KHR_shader_subgroup_uniform_control_flow");
104 if (m_use_subgroup_size_control)
105 {
106 ctx.requireDeviceFunctionality("VK_EXT_subgroup_size_control");
107 }
108
109 vk::VkPhysicalDeviceSubgroupProperties subgroupProperties;
110 subgroupProperties.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
111 subgroupProperties.pNext = DE_NULL;
112
113 vk::VkPhysicalDeviceProperties2 properties2;
114 properties2.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
115 properties2.pNext = &subgroupProperties;
116
117 ctx.getInstanceInterface().getPhysicalDeviceProperties2(ctx.getPhysicalDevice(), &properties2);
118
119 vk::VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroupSizeControlFeatures;
120 subgroupSizeControlFeatures.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT;
121 subgroupSizeControlFeatures.pNext = DE_NULL;
122
123 vk::VkPhysicalDeviceFeatures2 features2;
124 features2.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
125 features2.pNext = &subgroupSizeControlFeatures;
126
127 ctx.getInstanceInterface().getPhysicalDeviceFeatures2(ctx.getPhysicalDevice(), &features2);
128
129 // Check that the stage supports the required subgroup operations.
130 if ((m_stage & subgroupProperties.supportedStages) == 0)
131 {
132 TCU_THROW(NotSupportedError, "Device does not support subgroup operations in this stage");
133 }
134 if ((m_operation & subgroupProperties.supportedOperations) != m_operation)
135 {
136 TCU_THROW(NotSupportedError, "Device does not support required operations");
137 }
138
139 // For the compute shader tests, there are variants for implementations
140 // that support the subgroup size control extension and variants for those
141 // that do not. It is expected that computeFullSubgroups must be set for
142 // these tests if the extension is supported so tests are only supported
143 // for the extension appropriate version.
144 if (m_stage == vk::VK_SHADER_STAGE_COMPUTE_BIT)
145 {
146 if (m_use_subgroup_size_control)
147 {
148 if (subgroupSizeControlFeatures.computeFullSubgroups != VK_TRUE)
149 {
150 TCU_THROW(NotSupportedError, "Implementation does not support subgroup size control");
151 }
152 }
153 else
154 {
155 if (subgroupSizeControlFeatures.computeFullSubgroups == VK_TRUE)
156 {
157 TCU_THROW(NotSupportedError, "These tests are not enabled for subgroup size control implementations");
158 }
159 }
160 }
161
162 // The are large and small variants of the tests. The large variants
163 // require 256 invocations in a workgroup.
164 if (!m_small_workgroups)
165 {
166 vk::VkPhysicalDeviceProperties properties;
167 ctx.getInstanceInterface().getPhysicalDeviceProperties(ctx.getPhysicalDevice(), &properties);
168 if (properties.limits.maxComputeWorkGroupInvocations < 256)
169 {
170 TCU_THROW(NotSupportedError, "Device supported fewer than 256 invocations per workgroup");
171 }
172 }
173 }
174
175 template <bool requirements>
addTestsForAmberFiles(tcu::TestCaseGroup * tests,CaseGroup group)176 void addTestsForAmberFiles(tcu::TestCaseGroup *tests, CaseGroup group)
177 {
178 tcu::TestContext &testCtx = tests->getTestContext();
179 const std::string data_dir(group.data_dir);
180 const std::string subdir(group.subdir);
181 const std::string category = data_dir + "/" + subdir;
182 std::vector<Case> cases(group.cases);
183
184 for (unsigned i = 0; i < cases.size(); ++i)
185 {
186 const std::string file = std::string(cases[i].basename) + ".amber";
187 std::string readFilename("vulkan/amber/");
188 readFilename.append(category);
189 readFilename.append("/");
190 readFilename.append(file);
191 SubgroupUniformControlFlowTestCase *testCase = new SubgroupUniformControlFlowTestCase(
192 testCtx, cases[i].basename, readFilename, cases[i].small_workgroups, cases[i].use_subgroup_size_control,
193 cases[i].stage, cases[i].operation);
194 DE_ASSERT(testCase != DE_NULL);
195 if (requirements)
196 {
197 testCase->addRequirement("SubgroupSizeControl.computeFullSubgroups");
198 testCase->addRequirement("SubgroupSizeControl.subgroupSizeControl");
199 }
200 tests->addChild(testCase);
201 }
202 }
203
204 } // namespace
205
createSubgroupUniformControlFlowTests(tcu::TestContext & testCtx)206 tcu::TestCaseGroup *createSubgroupUniformControlFlowTests(tcu::TestContext &testCtx)
207 {
208 // There are four main groups of tests. Each group runs the same set of base
209 // shaders with minor variations. The groups are with or without compute full
210 // subgroups and a larger or smaller number of invocations. For each group of
211 // tests, shaders test either odd or even subgroups reconverge after
212 // diverging, without reconverging the whole workgroup. For the _partial
213 // tests, the workgroup is launched without a full final subgroup (not enough
214 // invocations).
215 //
216 // It is assumed that if an implementation does not support the compute full
217 // subgroups feature, that it will always launch full subgroups. Therefore,
218 // any given implementation only runs half of the tests. Implementations that
219 // do not support compute full subgroups cannot support the tests that enable
220 // it, while implementations that do support the feature will (likely) not
221 // pass the tests that do not enable the feature.
222
223 de::MovePtr<tcu::TestCaseGroup> uniformControlFlowTests(
224 new tcu::TestCaseGroup(testCtx, "subgroup_uniform_control_flow"));
225
226 // Location of the Amber script files under data/vulkan/amber source tree.
227 const char *data_dir = "subgroup_uniform_control_flow";
228 const char *large_dir = "large";
229 const char *small_dir = "small";
230 const char *large_control_dir = "large_control";
231 const char *small_control_dir = "small_control";
232
233 std::vector<bool> controls = {false, true};
234 for (unsigned c = 0; c < controls.size(); ++c)
235 {
236 // Full subgroups.
237 bool small = false;
238 bool control = controls[c];
239 vk::VkShaderStageFlagBits stage = vk::VK_SHADER_STAGE_COMPUTE_BIT;
240 const char *subdir = (control ? large_control_dir : large_dir);
241 CaseGroup group(data_dir, subdir);
242 // if/else diverge
243 group.add("subgroup_reconverge00", small, control, stage);
244 // do while diverge
245 group.add("subgroup_reconverge01", small, control, stage);
246 // while true with break
247 group.add("subgroup_reconverge02", small, control, stage);
248 // if/else diverge, volatile
249 group.add("subgroup_reconverge03", small, control, stage);
250 // early return and if/else diverge
251 group.add("subgroup_reconverge04", small, control, stage);
252 // early return and if/else volatile
253 group.add("subgroup_reconverge05", small, control, stage);
254 // while true with volatile conditional break and early return
255 group.add("subgroup_reconverge06", small, control, stage);
256 // while true return and break
257 group.add("subgroup_reconverge07", small, control, stage);
258 // for loop atomics with conditional break
259 group.add("subgroup_reconverge08", small, control, stage);
260 // diverge in for loop
261 group.add("subgroup_reconverge09", small, control, stage);
262 // diverge in for loop and break
263 group.add("subgroup_reconverge10", small, control, stage);
264 // diverge in for loop and continue
265 group.add("subgroup_reconverge11", small, control, stage);
266 // early return, divergent switch
267 group.add("subgroup_reconverge12", small, control, stage);
268 // early return, divergent switch more cases
269 group.add("subgroup_reconverge13", small, control, stage);
270 // divergent switch, some subgroups terminate
271 group.add("subgroup_reconverge14", small, control, stage);
272 // switch in switch
273 group.add("subgroup_reconverge15", small, control, stage);
274 // for loop unequal iterations
275 group.add("subgroup_reconverge16", small, control, stage);
276 // if/else with nested returns
277 group.add("subgroup_reconverge17", small, control, stage);
278 // if/else subgroup all equal
279 group.add("subgroup_reconverge18", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT);
280 // if/else subgroup any nested return
281 group.add("subgroup_reconverge19", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT);
282 // deeply nested
283 group.add("subgroup_reconverge20", small, control, stage);
284 const char *group_name = (control ? "large_full_control" : "large_full");
285 // Large Full subgroups
286 uniformControlFlowTests->addChild(createTestGroup(
287 testCtx, group_name, control ? addTestsForAmberFiles<true> : addTestsForAmberFiles<false>, group));
288
289 // Partial subgroup.
290 group = CaseGroup(data_dir, subdir);
291 // if/else diverge
292 group.add("subgroup_reconverge_partial00", small, control, stage);
293 // do while diverge
294 group.add("subgroup_reconverge_partial01", small, control, stage);
295 // while true with break
296 group.add("subgroup_reconverge_partial02", small, control, stage);
297 // if/else diverge, volatile
298 group.add("subgroup_reconverge_partial03", small, control, stage);
299 // early return and if/else diverge
300 group.add("subgroup_reconverge_partial04", small, control, stage);
301 // early return and if/else volatile
302 group.add("subgroup_reconverge_partial05", small, control, stage);
303 // while true with volatile conditional break and early return
304 group.add("subgroup_reconverge_partial06", small, control, stage);
305 // while true return and break
306 group.add("subgroup_reconverge_partial07", small, control, stage);
307 // for loop atomics with conditional break
308 group.add("subgroup_reconverge_partial08", small, control, stage);
309 // diverge in for loop
310 group.add("subgroup_reconverge_partial09", small, control, stage);
311 // diverge in for loop and break
312 group.add("subgroup_reconverge_partial10", small, control, stage);
313 // diverge in for loop and continue
314 group.add("subgroup_reconverge_partial11", small, control, stage);
315 // early return, divergent switch
316 group.add("subgroup_reconverge_partial12", small, control, stage);
317 // early return, divergent switch more cases
318 group.add("subgroup_reconverge_partial13", small, control, stage);
319 // divergent switch, some subgroups terminate
320 group.add("subgroup_reconverge_partial14", small, control, stage);
321 // switch in switch
322 group.add("subgroup_reconverge_partial15", small, control, stage);
323 // for loop unequal iterations
324 group.add("subgroup_reconverge_partial16", small, control, stage);
325 // if/else with nested returns
326 group.add("subgroup_reconverge_partial17", small, control, stage);
327 // if/else subgroup all equal
328 group.add("subgroup_reconverge_partial18", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT);
329 // if/else subgroup any nested return
330 group.add("subgroup_reconverge_partial19", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT);
331 // deeply nested
332 group.add("subgroup_reconverge_partial20", small, control, stage);
333 group_name = (control ? "large_partial_control" : "large_partial");
334 // Large Partial subgroups
335 uniformControlFlowTests->addChild(createTestGroup(
336 testCtx, group_name, control ? addTestsForAmberFiles<true> : addTestsForAmberFiles<false>, group));
337 }
338
339 for (unsigned c = 0; c < controls.size(); ++c)
340 {
341 // Full subgroups.
342 bool small = true;
343 bool control = controls[c];
344 vk::VkShaderStageFlagBits stage = vk::VK_SHADER_STAGE_COMPUTE_BIT;
345 const char *subdir = (control ? small_control_dir : small_dir);
346 CaseGroup group(data_dir, subdir);
347 // if/else diverge
348 group.add("small_subgroup_reconverge00", small, control, stage);
349 // do while diverge
350 group.add("small_subgroup_reconverge01", small, control, stage);
351 // while true with break
352 group.add("small_subgroup_reconverge02", small, control, stage);
353 // if/else diverge, volatile
354 group.add("small_subgroup_reconverge03", small, control, stage);
355 // early return and if/else diverge
356 group.add("small_subgroup_reconverge04", small, control, stage);
357 // early return and if/else volatile
358 group.add("small_subgroup_reconverge05", small, control, stage);
359 // while true with volatile conditional break and early return
360 group.add("small_subgroup_reconverge06", small, control, stage);
361 // while true return and break
362 group.add("small_subgroup_reconverge07", small, control, stage);
363 // for loop atomics with conditional break
364 group.add("small_subgroup_reconverge08", small, control, stage);
365 // diverge in for loop
366 group.add("small_subgroup_reconverge09", small, control, stage);
367 // diverge in for loop and break
368 group.add("small_subgroup_reconverge10", small, control, stage);
369 // diverge in for loop and continue
370 group.add("small_subgroup_reconverge11", small, control, stage);
371 // early return, divergent switch
372 group.add("small_subgroup_reconverge12", small, control, stage);
373 // early return, divergent switch more cases
374 group.add("small_subgroup_reconverge13", small, control, stage);
375 // divergent switch, some subgroups terminate
376 group.add("small_subgroup_reconverge14", small, control, stage);
377 // switch in switch
378 group.add("small_subgroup_reconverge15", small, control, stage);
379 // for loop unequal iterations
380 group.add("small_subgroup_reconverge16", small, control, stage);
381 // if/else with nested returns
382 group.add("small_subgroup_reconverge17", small, control, stage);
383 // if/else subgroup all equal
384 group.add("small_subgroup_reconverge18", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT);
385 // if/else subgroup any nested return
386 group.add("small_subgroup_reconverge19", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT);
387 // deeply nested
388 group.add("small_subgroup_reconverge20", small, control, stage);
389 const char *group_name = (control ? "small_full_control" : "small_full");
390 // Small Full subgroups
391 uniformControlFlowTests->addChild(createTestGroup(
392 testCtx, group_name, control ? addTestsForAmberFiles<true> : addTestsForAmberFiles<false>, group));
393
394 // Partial subgroup.
395 group = CaseGroup(data_dir, subdir);
396 // if/else diverge
397 group.add("small_subgroup_reconverge_partial00", small, control, stage);
398 // do while diverge
399 group.add("small_subgroup_reconverge_partial01", small, control, stage);
400 // while true with break
401 group.add("small_subgroup_reconverge_partial02", small, control, stage);
402 // if/else diverge, volatile
403 group.add("small_subgroup_reconverge_partial03", small, control, stage);
404 // early return and if/else diverge
405 group.add("small_subgroup_reconverge_partial04", small, control, stage);
406 // early return and if/else volatile
407 group.add("small_subgroup_reconverge_partial05", small, control, stage);
408 // while true with volatile conditional break and early return
409 group.add("small_subgroup_reconverge_partial06", small, control, stage);
410 // while true return and break
411 group.add("small_subgroup_reconverge_partial07", small, control, stage);
412 // for loop atomics with conditional break
413 group.add("small_subgroup_reconverge_partial08", small, control, stage);
414 // diverge in for loop
415 group.add("small_subgroup_reconverge_partial09", small, control, stage);
416 // diverge in for loop and break
417 group.add("small_subgroup_reconverge_partial10", small, control, stage);
418 // diverge in for loop and continue
419 group.add("small_subgroup_reconverge_partial11", small, control, stage);
420 // early return, divergent switch
421 group.add("small_subgroup_reconverge_partial12", small, control, stage);
422 // early return, divergent switch more cases
423 group.add("small_subgroup_reconverge_partial13", small, control, stage);
424 // divergent switch, some subgroups terminate
425 group.add("small_subgroup_reconverge_partial14", small, control, stage);
426 // switch in switch
427 group.add("small_subgroup_reconverge_partial15", small, control, stage);
428 // for loop unequal iterations
429 group.add("small_subgroup_reconverge_partial16", small, control, stage);
430 // if/else with nested returns
431 group.add("small_subgroup_reconverge_partial17", small, control, stage);
432 // if/else subgroup all equal
433 group.add("small_subgroup_reconverge_partial18", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT);
434 // if/else subgroup any nested return
435 group.add("small_subgroup_reconverge_partial19", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT);
436 // deeply nested
437 group.add("small_subgroup_reconverge_partial20", small, control, stage);
438 group_name = (control ? "small_partial_control" : "small_partial");
439 // Small Partial subgroups
440 uniformControlFlowTests->addChild(createTestGroup(
441 testCtx, group_name, control ? addTestsForAmberFiles<true> : addTestsForAmberFiles<false>, group));
442 }
443
444 // Discard test
445 CaseGroup group(data_dir, "discard");
446 // discard test
447 group.add("subgroup_reconverge_discard00", true, false, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
448 // Discard tests
449 uniformControlFlowTests->addChild(createTestGroup(testCtx, "discard", addTestsForAmberFiles<false>, group));
450
451 return uniformControlFlowTests.release();
452 }
453
454 } // namespace subgroups
455 } // namespace vkt
456