diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index e14e2e751..f6af78ff8 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -473,6 +473,19 @@ SPIRV_TOOLS_EXPORT const char* spvTargetEnvDescription(spv_target_env env); // false and sets *env to SPV_ENV_UNIVERSAL_1_0. SPIRV_TOOLS_EXPORT bool spvParseTargetEnv(const char* s, spv_target_env* env); +// Determines the target env value with the least features but which enables +// the given Vulkan and SPIR-V versions. If such a target is supported, returns +// true and writes the value to |env|, otherwise returns false. +// +// The Vulkan version is given as an unsigned 32-bit number as specified in +// Vulkan section "29.2.1 Version Numbers": the major version number appears +// in bits 22 to 21, and the minor version is in bits 12 to 21. The SPIR-V +// version is given in the SPIR-V version header word: major version in bits +// 16 to 23, and minor version in bits 8 to 15. +SPIRV_TOOLS_EXPORT bool spvParseVulkanEnv(uint32_t vulkan_ver, + uint32_t spirv_ver, + spv_target_env* env); + // Creates a context object. Returns null if env is invalid. SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env); diff --git a/source/spirv_target_env.cpp b/source/spirv_target_env.cpp index c4730e84a..e2ff99cb0 100644 --- a/source/spirv_target_env.cpp +++ b/source/spirv_target_env.cpp @@ -153,6 +153,34 @@ bool spvParseTargetEnv(const char* s, spv_target_env* env) { return false; } +#define VULKAN_VER(MAJOR, MINOR) ((MAJOR << 22) | (MINOR << 12)) +#define SPIRV_VER(MAJOR, MINOR) ((MAJOR << 16) | (MINOR << 8)) + +struct VulkanEnv { + spv_target_env vulkan_env; + uint32_t vulkan_ver; + uint32_t spirv_ver; +}; +// Maps each Vulkan target environment enum to the Vulkan version, and the +// maximum supported SPIR-V version for that Vulkan environment. +// Keep this ordered from least capable to most capable. +static const VulkanEnv ordered_vulkan_envs[] = { + {SPV_ENV_VULKAN_1_0, VULKAN_VER(1, 0), SPIRV_VER(1, 0)}, + {SPV_ENV_VULKAN_1_1, VULKAN_VER(1, 1), SPIRV_VER(1, 3)}, + {SPV_ENV_VULKAN_1_1_SPIRV_1_4, VULKAN_VER(1, 1), SPIRV_VER(1, 4)}, + {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}}; + +bool spvParseVulkanEnv(uint32_t vulkan_ver, uint32_t spirv_ver, + spv_target_env* env) { + for (auto triple : ordered_vulkan_envs) { + if (triple.vulkan_ver >= vulkan_ver && triple.spirv_ver >= spirv_ver) { + *env = triple.vulkan_env; + return true; + } + } + return false; +} + bool spvIsVulkanEnv(spv_target_env env) { switch (env) { case SPV_ENV_UNIVERSAL_1_0: diff --git a/test/target_env_test.cpp b/test/target_env_test.cpp index 5dc7415cd..9c86e2da6 100644 --- a/test/target_env_test.cpp +++ b/test/target_env_test.cpp @@ -63,11 +63,13 @@ struct ParseCase { using TargetParseTest = ::testing::TestWithParam; -TEST_P(TargetParseTest, InvalidTargetEnvProducesNull) { +TEST_P(TargetParseTest, Samples) { spv_target_env env; bool parsed = spvParseTargetEnv(GetParam().input, &env); EXPECT_THAT(parsed, Eq(GetParam().success)); - EXPECT_THAT(env, Eq(GetParam().env)); + if (parsed) { + EXPECT_THAT(env, Eq(GetParam().env)); + } } INSTANTIATE_TEST_SUITE_P( @@ -103,5 +105,61 @@ INSTANTIATE_TEST_SUITE_P( {"abc", false, SPV_ENV_UNIVERSAL_1_0}, })); +// A test case for parsing an environment string. +struct ParseVulkanCase { + uint32_t vulkan; + uint32_t spirv; + bool success; // Expect to successfully parse? + spv_target_env env; // The parsed environment, if successful. +}; + +using TargetParseVulkanTest = ::testing::TestWithParam; + +TEST_P(TargetParseVulkanTest, Samples) { + spv_target_env env; + bool parsed = spvParseVulkanEnv(GetParam().vulkan, GetParam().spirv, &env); + EXPECT_THAT(parsed, Eq(GetParam().success)); + if (parsed) { + EXPECT_THAT(env, Eq(GetParam().env)); + } +} + +#define VK(MAJ, MIN) ((MAJ << 22) | (MIN << 12)) +#define SPV(MAJ, MIN) ((MAJ << 16) | (MIN << 8)) +INSTANTIATE_TEST_SUITE_P( + TargetVulkanParsing, TargetParseVulkanTest, + ValuesIn(std::vector{ + // Vulkan 1.0 cases + {VK(1, 0), SPV(1, 0), true, SPV_ENV_VULKAN_1_0}, + {VK(1, 0), SPV(1, 1), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 0), SPV(1, 2), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 0), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 0), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, + {VK(1, 0), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 0), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 1.1 cases + {VK(1, 1), SPV(1, 0), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 1), SPV(1, 1), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 1), SPV(1, 2), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 1), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 1), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, + {VK(1, 1), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 1), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 1.2 cases + {VK(1, 2), SPV(1, 0), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 1), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 2), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 3), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 4), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 1.3 cases + {VK(1, 3), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 2.0 cases + {VK(2, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 99.0 cases + {VK(99, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, + })); + } // namespace } // namespace spvtools