400 lines
14 KiB
Rust
400 lines
14 KiB
Rust
// 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();
|
|
}
|