Add spvParseVulkanEnv (#3142)
This new API lets clients request a minimal spv_target_env value
that supports a given Vulkan and SPIR-V version, by a generic
numbering scheme (as already defined by Vulkan and SPIR-V specs).
This breaks a formal source dependency from Glslang to SPIRV-Tools.
When a new API is rolled out, such as Vulkan 1.2, Glslang currently
needs to reference a specific SPIRV-Tools enum by name.
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index e14e2e7..f6af78f 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -473,6 +473,19 @@
// 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 c4730e8..e2ff99c 100644
--- a/source/spirv_target_env.cpp
+++ b/source/spirv_target_env.cpp
@@ -153,6 +153,34 @@
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 5dc7415..9c86e2d 100644
--- a/test/target_env_test.cpp
+++ b/test/target_env_test.cpp
@@ -63,11 +63,13 @@
using TargetParseTest = ::testing::TestWithParam<ParseCase>;
-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 @@
{"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<ParseVulkanCase>;
+
+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<ParseVulkanCase>{
+ // 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