diff options
Diffstat (limited to 'autogen/features.rs')
-rw-r--r-- | autogen/features.rs | 393 |
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![] + } +} |