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