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