| // 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 std::io::Write; |
| use vk_parse::Extension; |
| |
| // This is not included in vk.xml, so it's added here manually |
| fn required_if_supported(name: &str) -> bool { |
| match name { |
| "VK_KHR_portability_subset" => true, |
| _ => false, |
| } |
| } |
| |
| fn conflicts_extensions(name: &str) -> &'static [&'static str] { |
| match name { |
| "VK_KHR_buffer_device_address" => &["VK_EXT_buffer_device_address"], |
| "VK_EXT_buffer_device_address" => &["VK_KHR_buffer_device_address"], |
| _ => &[], |
| } |
| } |
| |
| pub fn write<W: Write>(writer: &mut W, extensions: &IndexMap<&str, &Extension>) { |
| write_device_extensions(writer, make_vulkano_extensions("device", &extensions)); |
| write!(writer, "\n\n").unwrap(); |
| write_instance_extensions(writer, make_vulkano_extensions("instance", &extensions)); |
| } |
| |
| #[derive(Clone, Debug)] |
| struct VulkanoExtension { |
| member: String, |
| raw: String, |
| requires_core: (u16, u16), |
| requires_device_extensions: Vec<String>, |
| requires_instance_extensions: Vec<String>, |
| required_if_supported: bool, |
| conflicts_device_extensions: Vec<String>, |
| status: Option<ExtensionStatus>, |
| } |
| |
| #[derive(Clone, Debug)] |
| enum Replacement { |
| Core((u16, u16)), |
| DeviceExtension(String), |
| InstanceExtension(String), |
| } |
| |
| #[derive(Clone, Debug)] |
| enum ExtensionStatus { |
| Promoted(Replacement), |
| Deprecated(Option<Replacement>), |
| } |
| |
| fn make_vulkano_extensions( |
| ty: &str, |
| extensions: &IndexMap<&str, &Extension>, |
| ) -> Vec<VulkanoExtension> { |
| extensions |
| .values() |
| .filter(|ext| ext.ext_type.as_ref().unwrap() == ty) |
| .map(|ext| { |
| let raw = ext.name.to_owned(); |
| let member = raw.strip_prefix("VK_").unwrap().to_snake_case(); |
| let (major, minor) = ext |
| .requires_core |
| .as_ref() |
| .map(|s| s.as_str()) |
| .unwrap_or("1.0") |
| .split_once('.') |
| .unwrap(); |
| let requires_extensions: Vec<_> = ext |
| .requires |
| .as_ref() |
| .map(|s| s.split(',').collect()) |
| .unwrap_or_default(); |
| let conflicts_extensions = conflicts_extensions(&ext.name); |
| |
| VulkanoExtension { |
| member: member.clone(), |
| raw, |
| requires_core: (major.parse().unwrap(), minor.parse().unwrap()), |
| requires_device_extensions: requires_extensions |
| .iter() |
| .filter(|&&vk_name| extensions[vk_name].ext_type.as_ref().unwrap() == "device") |
| .map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case()) |
| .collect(), |
| requires_instance_extensions: requires_extensions |
| .iter() |
| .filter(|&&vk_name| { |
| extensions[vk_name].ext_type.as_ref().unwrap() == "instance" |
| }) |
| .map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case()) |
| .collect(), |
| required_if_supported: required_if_supported(ext.name.as_str()), |
| conflicts_device_extensions: conflicts_extensions |
| .iter() |
| .filter(|&&vk_name| extensions[vk_name].ext_type.as_ref().unwrap() == "device") |
| .map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case()) |
| .collect(), |
| status: ext |
| .promotedto |
| .as_ref() |
| .map(|s| s.as_str()) |
| .and_then(|pr| { |
| if let Some(version) = pr.strip_prefix("VK_VERSION_") { |
| let (major, minor) = version.split_once('_').unwrap(); |
| Some(ExtensionStatus::Promoted(Replacement::Core(( |
| major.parse().unwrap(), |
| minor.parse().unwrap(), |
| )))) |
| } else { |
| let member = pr.strip_prefix("VK_").unwrap().to_snake_case(); |
| match extensions[pr].ext_type.as_ref().unwrap().as_str() { |
| "device" => Some(ExtensionStatus::Promoted( |
| Replacement::DeviceExtension(member), |
| )), |
| "instance" => Some(ExtensionStatus::Promoted( |
| Replacement::InstanceExtension(member), |
| )), |
| _ => unreachable!(), |
| } |
| } |
| }) |
| .or_else(|| { |
| ext.deprecatedby |
| .as_ref() |
| .map(|s| s.as_str()) |
| .and_then(|depr| { |
| if depr.is_empty() { |
| Some(ExtensionStatus::Deprecated(None)) |
| } else if let Some(version) = depr.strip_prefix("VK_VERSION_") { |
| let (major, minor) = version.split_once('_').unwrap(); |
| Some(ExtensionStatus::Deprecated(Some(Replacement::Core(( |
| major.parse().unwrap(), |
| minor.parse().unwrap(), |
| ))))) |
| } else { |
| let member = depr.strip_prefix("VK_").unwrap().to_snake_case(); |
| match extensions[depr].ext_type.as_ref().unwrap().as_str() { |
| "device" => Some(ExtensionStatus::Deprecated(Some( |
| Replacement::DeviceExtension(member), |
| ))), |
| "instance" => Some(ExtensionStatus::Deprecated(Some( |
| Replacement::InstanceExtension(member), |
| ))), |
| _ => unreachable!(), |
| } |
| } |
| }) |
| }), |
| } |
| }) |
| .collect() |
| } |
| |
| fn write_device_extensions<W, I>(writer: &mut W, extensions: I) |
| where |
| W: Write, |
| I: IntoIterator<Item = VulkanoExtension>, |
| { |
| write!(writer, "crate::device::extensions::device_extensions! {{").unwrap(); |
| for ext in extensions { |
| write!(writer, "\n\t{} => {{", ext.member).unwrap(); |
| write_doc(writer, &ext); |
| write!(writer, "\n\t\traw: b\"{}\",", ext.raw).unwrap(); |
| write!( |
| writer, |
| "\n\t\trequires_core: crate::Version::V{}_{},", |
| ext.requires_core.0, ext.requires_core.1 |
| ) |
| .unwrap(); |
| write!( |
| writer, |
| "\n\t\trequires_device_extensions: [{}],", |
| ext.requires_device_extensions.join(", ") |
| ) |
| .unwrap(); |
| write!( |
| writer, |
| "\n\t\trequires_instance_extensions: [{}],", |
| ext.requires_instance_extensions.join(", ") |
| ) |
| .unwrap(); |
| write!( |
| writer, |
| "\n\t\trequired_if_supported: {},", |
| ext.required_if_supported |
| ) |
| .unwrap(); |
| write!( |
| writer, |
| "\n\t\tconflicts_device_extensions: [{}],", |
| ext.conflicts_device_extensions.join(", ") |
| ) |
| .unwrap(); |
| |
| /*if let Some(promoted_to_core) = ext.promoted_to_core { |
| write!( |
| writer, |
| "\n\t\tpromoted_to_core: Some(Version::V{}_{}),", |
| promoted_to_core.0, promoted_to_core.1 |
| ) |
| .unwrap(); |
| } else { |
| write!(writer, "\n\t\tpromoted_to_core: None,",).unwrap(); |
| }*/ |
| |
| write!(writer, "\n\t}},").unwrap(); |
| } |
| write!(writer, "\n}}").unwrap(); |
| } |
| |
| fn write_instance_extensions<W, I>(writer: &mut W, extensions: I) |
| where |
| W: Write, |
| I: IntoIterator<Item = VulkanoExtension>, |
| { |
| write!( |
| writer, |
| "crate::instance::extensions::instance_extensions! {{" |
| ) |
| .unwrap(); |
| for ext in extensions { |
| write!(writer, "\n\t{} => {{", ext.member).unwrap(); |
| write_doc(writer, &ext); |
| write!(writer, "\n\t\traw: b\"{}\",", ext.raw).unwrap(); |
| write!( |
| writer, |
| "\n\t\trequires_core: crate::Version::V{}_{},", |
| ext.requires_core.0, ext.requires_core.1 |
| ) |
| .unwrap(); |
| write!( |
| writer, |
| "\n\t\trequires_instance_extensions: [{}],", |
| ext.requires_instance_extensions.join(", ") |
| ) |
| .unwrap(); |
| |
| /*if let Some(promoted_to_core) = ext.promoted_to_core { |
| write!( |
| writer, |
| "\n\t\tpromoted_to_core: Some(crate::Version::V{}_{}),", |
| promoted_to_core.0, promoted_to_core.1 |
| ) |
| .unwrap(); |
| } else { |
| write!(writer, "\n\t\tpromoted_to_core: None,",).unwrap(); |
| }*/ |
| |
| write!(writer, "\n\t}},").unwrap(); |
| } |
| write!(writer, "\n}}").unwrap(); |
| } |
| |
| fn write_doc<W>(writer: &mut W, ext: &VulkanoExtension) |
| 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/{}.html)", ext.raw).unwrap(); |
| |
| if ext.requires_core != (1, 0) { |
| write!( |
| writer, |
| "\n\t\t\t- Requires Vulkan {}.{}", |
| ext.requires_core.0, ext.requires_core.1 |
| ) |
| .unwrap(); |
| } |
| |
| if !ext.requires_device_extensions.is_empty() { |
| let links: Vec<_> = ext |
| .requires_device_extensions |
| .iter() |
| .map(|ext| format!("[`{}`](crate::device::DeviceExtensions::{0})", ext)) |
| .collect(); |
| write!( |
| writer, |
| "\n\t\t\t- Requires device extension{}: {}", |
| if ext.requires_device_extensions.len() > 1 { |
| "s" |
| } else { |
| "" |
| }, |
| links.join(", ") |
| ) |
| .unwrap(); |
| } |
| |
| if !ext.requires_instance_extensions.is_empty() { |
| let links: Vec<_> = ext |
| .requires_instance_extensions |
| .iter() |
| .map(|ext| format!("[`{}`](crate::instance::InstanceExtensions::{0})", ext)) |
| .collect(); |
| write!( |
| writer, |
| "\n\t\t\t- Requires instance extension{}: {}", |
| if ext.requires_instance_extensions.len() > 1 { |
| "s" |
| } else { |
| "" |
| }, |
| links.join(", ") |
| ) |
| .unwrap(); |
| } |
| |
| if ext.required_if_supported { |
| write!( |
| writer, |
| "\n\t\t\t- Must be enabled if it is supported by the physical device", |
| ) |
| .unwrap(); |
| } |
| |
| if !ext.conflicts_device_extensions.is_empty() { |
| let links: Vec<_> = ext |
| .conflicts_device_extensions |
| .iter() |
| .map(|ext| format!("[`{}`](crate::device::DeviceExtensions::{0})", ext)) |
| .collect(); |
| write!( |
| writer, |
| "\n\t\t\t- Conflicts with device extension{}: {}", |
| if ext.conflicts_device_extensions.len() > 1 { |
| "s" |
| } else { |
| "" |
| }, |
| links.join(", ") |
| ) |
| .unwrap(); |
| } |
| |
| if let Some(status) = ext.status.as_ref() { |
| match status { |
| ExtensionStatus::Promoted(replacement) => { |
| write!(writer, "\n\t\t\t- Promoted to ",).unwrap(); |
| |
| match replacement { |
| Replacement::Core(version) => { |
| write!(writer, "Vulkan {}.{}", version.0, version.1).unwrap(); |
| } |
| Replacement::DeviceExtension(ext) => { |
| write!(writer, "[`{}`](crate::device::DeviceExtensions::{0})", ext) |
| .unwrap(); |
| } |
| Replacement::InstanceExtension(ext) => { |
| write!( |
| writer, |
| "[`{}`](crate::instance::InstanceExtensions::{0})", |
| ext |
| ) |
| .unwrap(); |
| } |
| } |
| } |
| ExtensionStatus::Deprecated(replacement) => { |
| write!(writer, "\n\t\t\t- Deprecated ",).unwrap(); |
| |
| match replacement { |
| Some(Replacement::Core(version)) => { |
| write!(writer, "by Vulkan {}.{}", version.0, version.1).unwrap(); |
| } |
| Some(Replacement::DeviceExtension(ext)) => { |
| write!( |
| writer, |
| "by [`{}`](crate::device::DeviceExtensions::{0})", |
| ext |
| ) |
| .unwrap(); |
| } |
| Some(Replacement::InstanceExtension(ext)) => { |
| write!( |
| writer, |
| "by [`{}`](crate::instance::InstanceExtensions::{0})", |
| ext |
| ) |
| .unwrap(); |
| } |
| None => { |
| write!(writer, "without a replacement").unwrap(); |
| } |
| } |
| } |
| } |
| } |
| |
| write!(writer, "\n\t\t\",").unwrap(); |
| } |