aboutsummaryrefslogtreecommitdiff
path: root/autogen/features.rs
diff options
context:
space:
mode:
Diffstat (limited to 'autogen/features.rs')
-rw-r--r--autogen/features.rs393
1 files changed, 393 insertions, 0 deletions
diff --git a/autogen/features.rs b/autogen/features.rs
new file mode 100644
index 0000000..4c49747
--- /dev/null
+++ b/autogen/features.rs
@@ -0,0 +1,393 @@
+// Copyright (c) 2021 The Vulkano developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
+// at your option. All files in the project carrying such
+// notice may not be copied, modified, or distributed except
+// according to those terms.
+
+use heck::SnakeCase;
+use indexmap::IndexMap;
+use regex::Regex;
+use std::{
+ collections::{hash_map::Entry, HashMap},
+ io::Write,
+};
+use vk_parse::{Extension, Type, TypeMember, TypeMemberMarkup, TypeSpec};
+
+// This is not included in vk.xml, so it's added here manually
+fn requires_features(name: &str) -> &'static [&'static str] {
+ match name {
+ "sparseImageInt64Atomics" => &["shaderImageInt64Atomics"],
+ "sparseImageFloat32Atomics" => &["shaderImageFloat32Atomics"],
+ "sparseImageFloat32AtomicAdd" => &["shaderImageFloat32AtomicAdd"],
+ _ => &[],
+ }
+}
+
+fn conflicts_features(name: &str) -> &'static [&'static str] {
+ match name {
+ "shadingRateImage" => &[
+ "pipelineFragmentShadingRate",
+ "primitiveFragmentShadingRate",
+ "attachmentFragmentShadingRate",
+ ],
+ "fragmentDensityMap" => &[
+ "pipelineFragmentShadingRate",
+ "primitiveFragmentShadingRate",
+ "attachmentFragmentShadingRate",
+ ],
+ "pipelineFragmentShadingRate" => &["shadingRateImage", "fragmentDensityMap"],
+ "primitiveFragmentShadingRate" => &["shadingRateImage", "fragmentDensityMap"],
+ "attachmentFragmentShadingRate" => &["shadingRateImage", "fragmentDensityMap"],
+ _ => &[],
+ }
+}
+
+fn required_by_extensions(name: &str) -> &'static [&'static str] {
+ match name {
+ "shaderDrawParameters" => &["VK_KHR_shader_draw_parameters"],
+ "drawIndirectCount" => &["VK_KHR_draw_indirect_count"],
+ "samplerMirrorClampToEdge" => &["VK_KHR_sampler_mirror_clamp_to_edge"],
+ "descriptorIndexing" => &["VK_EXT_descriptor_indexing"],
+ "samplerFilterMinmax" => &["VK_EXT_sampler_filter_minmax"],
+ "shaderOutputViewportIndex" => &["VK_EXT_shader_viewport_index_layer"],
+ "shaderOutputLayer" => &["VK_EXT_shader_viewport_index_layer"],
+ _ => &[],
+ }
+}
+
+pub fn write<W: Write>(
+ writer: &mut W,
+ types: &HashMap<&str, (&Type, Vec<&str>)>,
+ extensions: &IndexMap<&str, &Extension>,
+) {
+ write!(writer, "crate::device::features::features! {{").unwrap();
+
+ for feat in make_vulkano_features(types) {
+ write!(writer, "\n\t{} => {{", feat.member).unwrap();
+ write_doc(writer, &feat);
+ write!(writer, "\n\t\tffi_name: {},", feat.ffi_name).unwrap();
+ write!(
+ writer,
+ "\n\t\tffi_members: [{}],",
+ feat.ffi_members.join(", ")
+ )
+ .unwrap();
+ write!(
+ writer,
+ "\n\t\trequires_features: [{}],",
+ feat.requires_features.join(", ")
+ )
+ .unwrap();
+ write!(
+ writer,
+ "\n\t\tconflicts_features: [{}],",
+ feat.conflicts_features.join(", ")
+ )
+ .unwrap();
+ write!(
+ writer,
+ "\n\t\trequired_by_extensions: [{}],",
+ feat.required_by_extensions.join(", ")
+ )
+ .unwrap();
+ write!(writer, "\n\t}},").unwrap();
+ }
+
+ write!(
+ writer,
+ "\n}}\n\ncrate::device::features::features_ffi! {{\n\tapi_version,\n\tdevice_extensions,\n\tinstance_extensions,"
+ )
+ .unwrap();
+
+ for ffi in make_vulkano_features_ffi(types, extensions) {
+ write!(writer, "\n\t{} => {{", ffi.member).unwrap();
+ write!(writer, "\n\t\tty: {},", ffi.ty).unwrap();
+ write!(
+ writer,
+ "\n\t\tprovided_by: [{}],",
+ ffi.provided_by.join(", ")
+ )
+ .unwrap();
+ write!(writer, "\n\t\tconflicts: [{}],", ffi.conflicts.join(", ")).unwrap();
+ write!(writer, "\n\t}},").unwrap();
+ }
+
+ write!(writer, "\n}}").unwrap();
+}
+
+#[derive(Clone, Debug)]
+struct VulkanoFeature {
+ member: String,
+ vulkan_doc: String,
+ ffi_name: String,
+ ffi_members: Vec<String>,
+ requires_features: Vec<String>,
+ conflicts_features: Vec<String>,
+ required_by_extensions: Vec<String>,
+}
+
+fn make_vulkano_features(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<VulkanoFeature> {
+ let mut features = HashMap::new();
+ std::iter::once(&types["VkPhysicalDeviceFeatures"])
+ .chain(sorted_structs(types).into_iter())
+ .filter(|(ty, _)| {
+ ty.name.as_ref().map(|s| s.as_str()) == Some("VkPhysicalDeviceFeatures")
+ || ty.structextends.as_ref().map(|s| s.as_str())
+ == Some("VkPhysicalDeviceFeatures2,VkDeviceCreateInfo")
+ })
+ .for_each(|(ty, _)| {
+ let vulkan_ty_name = ty.name.as_ref().unwrap();
+
+ let ty_name = if vulkan_ty_name == "VkPhysicalDeviceFeatures" {
+ "features_vulkan10.features".to_owned()
+ } else {
+ ffi_member(vulkan_ty_name)
+ };
+
+ members(ty).into_iter().for_each(|name| {
+ let member = name.to_snake_case();
+ match features.entry(member.clone()) {
+ Entry::Vacant(entry) => {
+ let requires_features = requires_features(name);
+ let conflicts_features = conflicts_features(name);
+ let required_by_extensions = required_by_extensions(name);
+
+ entry.insert(VulkanoFeature {
+ member: member.clone(),
+ vulkan_doc: format!("{}.html#features-{}", vulkan_ty_name, name),
+ ffi_name: member,
+ ffi_members: vec![ty_name.to_owned()],
+ requires_features: requires_features
+ .into_iter()
+ .map(|&s| s.to_snake_case())
+ .collect(),
+ conflicts_features: conflicts_features
+ .into_iter()
+ .map(|&s| s.to_snake_case())
+ .collect(),
+ required_by_extensions: required_by_extensions
+ .iter()
+ .map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case())
+ .collect(),
+ });
+ }
+ Entry::Occupied(entry) => {
+ entry.into_mut().ffi_members.push(ty_name.to_owned());
+ }
+ };
+ });
+ });
+
+ let mut names: Vec<_> = features.values().map(|feat| feat.member.clone()).collect();
+ names.sort_unstable();
+ names
+ .into_iter()
+ .map(|name| features.remove(&name).unwrap())
+ .collect()
+}
+
+fn write_doc<W>(writer: &mut W, feat: &VulkanoFeature)
+where
+ W: Write,
+{
+ write!(writer, "\n\t\tdoc: \"\n\t\t\t- [Vulkan documentation](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/{})", feat.vulkan_doc).unwrap();
+
+ if !feat.requires_features.is_empty() {
+ let links: Vec<_> = feat
+ .requires_features
+ .iter()
+ .map(|ext| format!("[`{}`](crate::device::Features::{0})", ext))
+ .collect();
+ write!(
+ writer,
+ "\n\t\t\t- Requires feature{}: {}",
+ if feat.requires_features.len() > 1 {
+ "s"
+ } else {
+ ""
+ },
+ links.join(", ")
+ )
+ .unwrap();
+ }
+
+ if !feat.required_by_extensions.is_empty() {
+ let links: Vec<_> = feat
+ .required_by_extensions
+ .iter()
+ .map(|ext| format!("[`{}`](crate::device::DeviceExtensions::{0})", ext))
+ .collect();
+ write!(
+ writer,
+ "\n\t\t\t- Required by device extension{}: {}",
+ if feat.required_by_extensions.len() > 1 {
+ "s"
+ } else {
+ ""
+ },
+ links.join(", ")
+ )
+ .unwrap();
+ }
+
+ if !feat.conflicts_features.is_empty() {
+ let links: Vec<_> = feat
+ .conflicts_features
+ .iter()
+ .map(|ext| format!("[`{}`](crate::device::Features::{0})", ext))
+ .collect();
+ write!(
+ writer,
+ "\n\t\t\t- Conflicts with feature{}: {}",
+ if feat.conflicts_features.len() > 1 {
+ "s"
+ } else {
+ ""
+ },
+ links.join(", ")
+ )
+ .unwrap();
+ }
+
+ write!(writer, "\n\t\t\",").unwrap();
+}
+
+#[derive(Clone, Debug)]
+struct VulkanoFeatureFfi {
+ member: String,
+ ty: String,
+ provided_by: Vec<String>,
+ conflicts: Vec<String>,
+}
+
+fn make_vulkano_features_ffi<'a>(
+ types: &'a HashMap<&str, (&Type, Vec<&str>)>,
+ extensions: &IndexMap<&'a str, &Extension>,
+) -> Vec<VulkanoFeatureFfi> {
+ let mut feature_included_in: HashMap<&str, Vec<&str>> = HashMap::new();
+ sorted_structs(types)
+ .into_iter()
+ .map(|(ty, provided_by)| {
+ let name = ty.name.as_ref().unwrap();
+ let provided_by = provided_by
+ .iter()
+ .map(|provided_by| {
+ if let Some(version) = provided_by.strip_prefix("VK_VERSION_") {
+ format!("api_version >= crate::Version::V{}", version)
+ } else {
+ format!(
+ "{}_extensions.{}",
+ extensions[provided_by].ext_type.as_ref().unwrap().as_str(),
+ provided_by
+ .strip_prefix("VK_")
+ .unwrap()
+ .to_ascii_lowercase()
+ )
+ }
+ })
+ .collect();
+ let mut conflicts = vec![];
+ members(ty)
+ .into_iter()
+ .for_each(|member| match feature_included_in.entry(member) {
+ Entry::Vacant(entry) => {
+ entry.insert(vec![name]);
+ }
+ Entry::Occupied(entry) => {
+ let conflicters = entry.into_mut();
+ conflicters.iter().for_each(|conflicter| {
+ let conflicter = ffi_member(conflicter);
+ if !conflicts.contains(&conflicter) {
+ conflicts.push(conflicter);
+ }
+ });
+ conflicters.push(name);
+ }
+ });
+
+ VulkanoFeatureFfi {
+ member: ffi_member(name),
+ ty: name.strip_prefix("Vk").unwrap().to_owned(),
+ provided_by,
+ conflicts,
+ }
+ })
+ .collect()
+}
+
+fn sorted_structs<'a>(
+ types: &'a HashMap<&str, (&'a Type, Vec<&'a str>)>,
+) -> Vec<&'a (&'a Type, Vec<&'a str>)> {
+ let mut structs: Vec<_> = types
+ .values()
+ .filter(|(ty, _)| {
+ ty.structextends.as_ref().map(|s| s.as_str())
+ == Some("VkPhysicalDeviceFeatures2,VkDeviceCreateInfo")
+ })
+ .collect();
+ let regex = Regex::new(r"^VkPhysicalDeviceVulkan\d+Features$").unwrap();
+ structs.sort_unstable_by_key(|&(ty, provided_by)| {
+ let name = ty.name.as_ref().unwrap();
+ (
+ !regex.is_match(name),
+ if let Some(version) = provided_by
+ .iter()
+ .find_map(|s| s.strip_prefix("VK_VERSION_"))
+ {
+ let (major, minor) = version.split_once('_').unwrap();
+ major.parse::<i32>().unwrap() << 22 | minor.parse::<i32>().unwrap() << 12
+ } else if provided_by
+ .iter()
+ .find(|s| s.starts_with("VK_KHR_"))
+ .is_some()
+ {
+ i32::MAX - 2
+ } else if provided_by
+ .iter()
+ .find(|s| s.starts_with("VK_EXT_"))
+ .is_some()
+ {
+ i32::MAX - 1
+ } else {
+ i32::MAX
+ },
+ name,
+ )
+ });
+
+ structs
+}
+
+fn ffi_member(ty_name: &str) -> String {
+ let ty_name = ty_name
+ .strip_prefix("VkPhysicalDevice")
+ .unwrap()
+ .to_snake_case();
+ let (base, suffix) = ty_name.rsplit_once("_features").unwrap();
+ format!("features_{}{}", base, suffix)
+}
+
+fn members(ty: &Type) -> Vec<&str> {
+ if let TypeSpec::Members(members) = &ty.spec {
+ members
+ .iter()
+ .filter_map(|member| {
+ if let TypeMember::Definition(def) = member {
+ let name = def.markup.iter().find_map(|markup| match markup {
+ TypeMemberMarkup::Name(name) => Some(name.as_str()),
+ _ => None,
+ });
+ if name != Some("sType") && name != Some("pNext") {
+ return name;
+ }
+ }
+ None
+ })
+ .collect()
+ } else {
+ vec![]
+ }
+}