/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2020 Google LLC * Copyright (c) 2020 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Test new features in VK_KHR_shader_subgroup_uniform_control_flow *//*--------------------------------------------------------------------*/ #include #include "tcuDefs.hpp" #include "vkDefs.hpp" #include "vkDeviceUtil.hpp" #include "vktTestGroupUtil.hpp" #include "vktAmberTestCase.hpp" #include "vktSubgroupUniformControlFlowTests.hpp" #include "vktTestGroupUtil.hpp" namespace vkt { namespace subgroups { namespace { struct Case { Case(const char* b, const char* d, bool sw, bool use_ssc, vk::VkShaderStageFlagBits s, vk::VkSubgroupFeatureFlagBits o) : basename(b), description(d), small_workgroups(sw), use_subgroup_size_control(use_ssc), stage(s) { operation = (vk::VkSubgroupFeatureFlagBits)(o | vk::VK_SUBGROUP_FEATURE_BASIC_BIT); } const char* basename; const char* description; bool small_workgroups; bool use_subgroup_size_control; vk::VkShaderStageFlagBits stage; vk::VkSubgroupFeatureFlagBits operation; }; struct CaseGroup { CaseGroup(const char* the_data_dir, const char* the_subdir) : data_dir(the_data_dir), subdir(the_subdir) { } void add(const char* basename, const char* description, bool small_workgroups, bool use_subgroup_size_control, vk::VkShaderStageFlagBits stage, vk::VkSubgroupFeatureFlagBits operation = vk::VK_SUBGROUP_FEATURE_BASIC_BIT) { cases.push_back(Case(basename, description, small_workgroups, use_subgroup_size_control, stage, operation)); } const char* data_dir; const char* subdir; std::vector cases; }; class SubgroupUniformControlFlowTestCase : public cts_amber::AmberTestCase { public: SubgroupUniformControlFlowTestCase(tcu::TestContext& testCtx, const char* name, const char* description, const std::string& readFilename, bool small_workgroups, bool use_subgroup_size_control, vk::VkShaderStageFlagBits stage, vk::VkSubgroupFeatureFlagBits operation) : cts_amber::AmberTestCase(testCtx, name, description, readFilename), m_small_workgroups(small_workgroups), m_use_subgroup_size_control(use_subgroup_size_control), m_stage(stage), m_operation(operation) { } virtual void checkSupport(Context& ctx) const; // override private: bool m_small_workgroups; bool m_use_subgroup_size_control; vk::VkShaderStageFlagBits m_stage; vk::VkSubgroupFeatureFlagBits m_operation; }; void SubgroupUniformControlFlowTestCase::checkSupport(Context& ctx) const { // Check required extensions. ctx.requireDeviceFunctionality("VK_KHR_shader_subgroup_uniform_control_flow"); if (m_use_subgroup_size_control) { ctx.requireDeviceFunctionality("VK_EXT_subgroup_size_control"); } vk::VkPhysicalDeviceSubgroupProperties subgroupProperties; subgroupProperties.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; subgroupProperties.pNext = DE_NULL; vk::VkPhysicalDeviceProperties2 properties2; properties2.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties2.pNext = &subgroupProperties; ctx.getInstanceInterface().getPhysicalDeviceProperties2(ctx.getPhysicalDevice(), &properties2); vk::VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroupSizeControlFeatures; subgroupSizeControlFeatures.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT; subgroupSizeControlFeatures.pNext = DE_NULL; vk::VkPhysicalDeviceFeatures2 features2; features2.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features2.pNext = &subgroupSizeControlFeatures; ctx.getInstanceInterface().getPhysicalDeviceFeatures2(ctx.getPhysicalDevice(), &features2); // Check that the stage supports the required subgroup operations. if ((m_stage & subgroupProperties.supportedStages) == 0) { TCU_THROW(NotSupportedError, "Device does not support subgroup operations in this stage"); } if ((m_operation & subgroupProperties.supportedOperations) != m_operation) { TCU_THROW(NotSupportedError, "Device does not support required operations"); } // For the compute shader tests, there are variants for implementations // that support the subgroup size control extension and variants for those // that do not. It is expected that computeFullSubgroups must be set for // these tests if the extension is supported so tests are only supported // for the extension appropriate version. if (m_stage == vk::VK_SHADER_STAGE_COMPUTE_BIT) { if (m_use_subgroup_size_control) { if (subgroupSizeControlFeatures.computeFullSubgroups != VK_TRUE) { TCU_THROW(NotSupportedError, "Implementation does not support subgroup size control"); } } else { if (subgroupSizeControlFeatures.computeFullSubgroups == VK_TRUE) { TCU_THROW(NotSupportedError, "These tests are not enabled for subgroup size control implementations"); } } } // The are large and small variants of the tests. The large variants // require 256 invocations in a workgroup. if (!m_small_workgroups) { vk::VkPhysicalDeviceProperties properties; ctx.getInstanceInterface().getPhysicalDeviceProperties(ctx.getPhysicalDevice(), &properties); if (properties.limits.maxComputeWorkGroupInvocations < 256) { TCU_THROW(NotSupportedError, "Device supported fewer than 256 invocations per workgroup"); } } } template void addTestsForAmberFiles(tcu::TestCaseGroup* tests, CaseGroup group) { tcu::TestContext& testCtx = tests->getTestContext(); const std::string data_dir(group.data_dir); const std::string subdir(group.subdir); const std::string category = data_dir + "/" + subdir; std::vector cases(group.cases); for (unsigned i = 0; i < cases.size(); ++i) { const std::string file = std::string(cases[i].basename) + ".amber"; std::string readFilename("vulkan/amber/"); readFilename.append(category); readFilename.append("/"); readFilename.append(file); SubgroupUniformControlFlowTestCase* testCase = new SubgroupUniformControlFlowTestCase(testCtx, cases[i].basename, cases[i].description, readFilename, cases[i].small_workgroups, cases[i].use_subgroup_size_control, cases[i].stage, cases[i].operation); DE_ASSERT(testCase != DE_NULL); if (requirements) { testCase->addRequirement("SubgroupSizeControl.computeFullSubgroups"); testCase->addRequirement("SubgroupSizeControl.subgroupSizeControl"); } tests->addChild(testCase); } } } // anonymous tcu::TestCaseGroup* createSubgroupUniformControlFlowTests(tcu::TestContext& testCtx) { // There are four main groups of tests. Each group runs the same set of base // shaders with minor variations. The groups are with or without compute full // subgroups and a larger or smaller number of invocations. For each group of // tests, shaders test either odd or even subgroups reconverge after // diverging, without reconverging the whole workgroup. For the _partial // tests, the workgroup is launched without a full final subgroup (not enough // invocations). // // It is assumed that if an implementation does not support the compute full // subgroups feature, that it will always launch full subgroups. Therefore, // any given implementation only runs half of the tests. Implementations that // do not support compute full subgroups cannot support the tests that enable // it, while implementations that do support the feature will (likely) not // pass the tests that do not enable the feature. de::MovePtr uniformControlFlowTests(new tcu::TestCaseGroup(testCtx, "subgroup_uniform_control_flow", "VK_KHR_shader_subgroup_uniform_control_flow tests")); // Location of the Amber script files under data/vulkan/amber source tree. const char* data_dir = "subgroup_uniform_control_flow"; const char* large_dir = "large"; const char* small_dir = "small"; const char* large_control_dir = "large_control"; const char* small_control_dir = "small_control"; std::vector controls = {false, true}; for (unsigned c = 0; c < controls.size(); ++c) { // Full subgroups. bool small = false; bool control = controls[c]; vk::VkShaderStageFlagBits stage = vk::VK_SHADER_STAGE_COMPUTE_BIT; const char* subdir = (control ? large_control_dir : large_dir); CaseGroup group(data_dir, subdir); group.add("subgroup_reconverge00", "if/else diverge", small, control, stage); group.add("subgroup_reconverge01", "do while diverge", small, control, stage); group.add("subgroup_reconverge02", "while true with break", small, control, stage); group.add("subgroup_reconverge03", "if/else diverge, volatile", small, control, stage); group.add("subgroup_reconverge04", "early return and if/else diverge", small, control, stage); group.add("subgroup_reconverge05", "early return and if/else volatile", small, control, stage); group.add("subgroup_reconverge06", "while true with volatile conditional break and early return", small, control, stage); group.add("subgroup_reconverge07", "while true return and break", small, control, stage); group.add("subgroup_reconverge08", "for loop atomics with conditional break", small, control, stage); group.add("subgroup_reconverge09", "diverge in for loop", small, control, stage); group.add("subgroup_reconverge10", "diverge in for loop and break", small, control, stage); group.add("subgroup_reconverge11", "diverge in for loop and continue", small, control, stage); group.add("subgroup_reconverge12", "early return, divergent switch", small, control, stage); group.add("subgroup_reconverge13", "early return, divergent switch more cases", small, control, stage); group.add("subgroup_reconverge14", "divergent switch, some subgroups terminate", small, control, stage); group.add("subgroup_reconverge15", "switch in switch", small, control, stage); group.add("subgroup_reconverge16", "for loop unequal iterations", small, control, stage); group.add("subgroup_reconverge17", "if/else with nested returns", small, control, stage); group.add("subgroup_reconverge18", "if/else subgroup all equal", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT); group.add("subgroup_reconverge19", "if/else subgroup any nested return", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT); group.add("subgroup_reconverge20", "deeply nested", small, control, stage); const char* group_name = (control ? "large_full_control" : "large_full"); uniformControlFlowTests->addChild(createTestGroup(testCtx, group_name, "Large Full subgroups", control?addTestsForAmberFiles:addTestsForAmberFiles, group)); // Partial subgroup. group = CaseGroup(data_dir, subdir); group.add("subgroup_reconverge_partial00", "if/else diverge", small, control, stage); group.add("subgroup_reconverge_partial01", "do while diverge", small, control, stage); group.add("subgroup_reconverge_partial02", "while true with break", small, control, stage); group.add("subgroup_reconverge_partial03", "if/else diverge, volatile", small, control, stage); group.add("subgroup_reconverge_partial04", "early return and if/else diverge", small, control, stage); group.add("subgroup_reconverge_partial05", "early return and if/else volatile", small, control, stage); group.add("subgroup_reconverge_partial06", "while true with volatile conditional break and early return", small, control, stage); group.add("subgroup_reconverge_partial07", "while true return and break", small, control, stage); group.add("subgroup_reconverge_partial08", "for loop atomics with conditional break", small, control, stage); group.add("subgroup_reconverge_partial09", "diverge in for loop", small, control, stage); group.add("subgroup_reconverge_partial10", "diverge in for loop and break", small, control, stage); group.add("subgroup_reconverge_partial11", "diverge in for loop and continue", small, control, stage); group.add("subgroup_reconverge_partial12", "early return, divergent switch", small, control, stage); group.add("subgroup_reconverge_partial13", "early return, divergent switch more cases", small, control, stage); group.add("subgroup_reconverge_partial14", "divergent switch, some subgroups terminate", small, control, stage); group.add("subgroup_reconverge_partial15", "switch in switch", small, control, stage); group.add("subgroup_reconverge_partial16", "for loop unequal iterations", small, control, stage); group.add("subgroup_reconverge_partial17", "if/else with nested returns", small, control, stage); group.add("subgroup_reconverge_partial18", "if/else subgroup all equal", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT); group.add("subgroup_reconverge_partial19", "if/else subgroup any nested return", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT); group.add("subgroup_reconverge_partial20", "deeply nested", small, control, stage); group_name = (control ? "large_partial_control" : "large_partial"); uniformControlFlowTests->addChild(createTestGroup(testCtx, group_name, "Large Partial subgroups", control?addTestsForAmberFiles:addTestsForAmberFiles, group)); } for (unsigned c = 0; c < controls.size(); ++c) { // Full subgroups. bool small = true; bool control = controls[c]; vk::VkShaderStageFlagBits stage = vk::VK_SHADER_STAGE_COMPUTE_BIT; const char* subdir = (control ? small_control_dir : small_dir); CaseGroup group(data_dir, subdir); group.add("small_subgroup_reconverge00", "if/else diverge", small, control, stage); group.add("small_subgroup_reconverge01", "do while diverge", small, control, stage); group.add("small_subgroup_reconverge02", "while true with break", small, control, stage); group.add("small_subgroup_reconverge03", "if/else diverge, volatile", small, control, stage); group.add("small_subgroup_reconverge04", "early return and if/else diverge", small, control, stage); group.add("small_subgroup_reconverge05", "early return and if/else volatile", small, control, stage); group.add("small_subgroup_reconverge06", "while true with volatile conditional break and early return", small, control, stage); group.add("small_subgroup_reconverge07", "while true return and break", small, control, stage); group.add("small_subgroup_reconverge08", "for loop atomics with conditional break", small, control, stage); group.add("small_subgroup_reconverge09", "diverge in for loop", small, control, stage); group.add("small_subgroup_reconverge10", "diverge in for loop and break", small, control, stage); group.add("small_subgroup_reconverge11", "diverge in for loop and continue", small, control, stage); group.add("small_subgroup_reconverge12", "early return, divergent switch", small, control, stage); group.add("small_subgroup_reconverge13", "early return, divergent switch more cases", small, control, stage); group.add("small_subgroup_reconverge14", "divergent switch, some subgroups terminate", small, control, stage); group.add("small_subgroup_reconverge15", "switch in switch", small, control, stage); group.add("small_subgroup_reconverge16", "for loop unequal iterations", small, control, stage); group.add("small_subgroup_reconverge17", "if/else with nested returns", small, control, stage); group.add("small_subgroup_reconverge18", "if/else subgroup all equal", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT); group.add("small_subgroup_reconverge19", "if/else subgroup any nested return", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT); group.add("small_subgroup_reconverge20", "deeply nested", small, control, stage); const char* group_name = (control ? "small_full_control" : "small_full"); uniformControlFlowTests->addChild(createTestGroup(testCtx, group_name, "Small Full subgroups", control?addTestsForAmberFiles:addTestsForAmberFiles, group)); // Partial subgroup. group = CaseGroup(data_dir, subdir); group.add("small_subgroup_reconverge_partial00", "if/else diverge", small, control, stage); group.add("small_subgroup_reconverge_partial01", "do while diverge", small, control, stage); group.add("small_subgroup_reconverge_partial02", "while true with break", small, control, stage); group.add("small_subgroup_reconverge_partial03", "if/else diverge, volatile", small, control, stage); group.add("small_subgroup_reconverge_partial04", "early return and if/else diverge", small, control, stage); group.add("small_subgroup_reconverge_partial05", "early return and if/else volatile", small, control, stage); group.add("small_subgroup_reconverge_partial06", "while true with volatile conditional break and early return", small, control, stage); group.add("small_subgroup_reconverge_partial07", "while true return and break", small, control, stage); group.add("small_subgroup_reconverge_partial08", "for loop atomics with conditional break", small, control, stage); group.add("small_subgroup_reconverge_partial09", "diverge in for loop", small, control, stage); group.add("small_subgroup_reconverge_partial10", "diverge in for loop and break", small, control, stage); group.add("small_subgroup_reconverge_partial11", "diverge in for loop and continue", small, control, stage); group.add("small_subgroup_reconverge_partial12", "early return, divergent switch", small, control, stage); group.add("small_subgroup_reconverge_partial13", "early return, divergent switch more cases", small, control, stage); group.add("small_subgroup_reconverge_partial14", "divergent switch, some subgroups terminate", small, control, stage); group.add("small_subgroup_reconverge_partial15", "switch in switch", small, control, stage); group.add("small_subgroup_reconverge_partial16", "for loop unequal iterations", small, control, stage); group.add("small_subgroup_reconverge_partial17", "if/else with nested returns", small, control, stage); group.add("small_subgroup_reconverge_partial18", "if/else subgroup all equal", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT); group.add("small_subgroup_reconverge_partial19", "if/else subgroup any nested return", small, control, stage, vk::VK_SUBGROUP_FEATURE_VOTE_BIT); group.add("small_subgroup_reconverge_partial20", "deeply nested", small, control, stage); group_name = (control ? "small_partial_control" : "small_partial"); uniformControlFlowTests->addChild(createTestGroup(testCtx, group_name, "Small Partial subgroups", control?addTestsForAmberFiles:addTestsForAmberFiles, group)); } // Discard test CaseGroup group(data_dir, "discard"); group.add("subgroup_reconverge_discard00", "discard test", true, false, vk::VK_SHADER_STAGE_FRAGMENT_BIT); uniformControlFlowTests->addChild(createTestGroup(testCtx, "discard", "Discard tests", addTestsForAmberFiles, group)); return uniformControlFlowTests.release(); } } // subgroups } // vkt