blob: 1dfb6953b21011756414c018c082e8305888bd15 [file] [log] [blame]
// 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};
pub fn write<W: Write>(
writer: &mut W,
types: &HashMap<&str, (&Type, Vec<&str>)>,
extensions: &IndexMap<&str, &Extension>,
) {
write!(writer, "crate::device::properties::properties! {{").unwrap();
for feat in make_vulkano_properties(&types) {
write!(writer, "\n\t{} => {{", feat.member).unwrap();
write_doc(writer, &feat);
write!(writer, "\n\t\tty: {},", feat.ty).unwrap();
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\trequired: {},", feat.required).unwrap();
write!(writer, "\n\t}},").unwrap();
}
write!(
writer,
"\n}}\n\ncrate::device::properties::properties_ffi! {{\n\tapi_version,\n\tdevice_extensions,\n\tinstance_extensions,"
)
.unwrap();
for ffi in make_vulkano_properties_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 VulkanoProperty {
member: String,
ty: String,
vulkan_doc: String,
ffi_name: String,
ffi_members: Vec<String>,
required: bool,
}
fn make_vulkano_properties(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<VulkanoProperty> {
let mut properties = HashMap::new();
std::array::IntoIter::new([
&types["VkPhysicalDeviceProperties"],
&types["VkPhysicalDeviceLimits"],
&types["VkPhysicalDeviceSparseProperties"],
])
.chain(sorted_structs(types).into_iter())
.filter(|(ty, _)| {
let name = ty.name.as_ref().map(|s| s.as_str());
name == Some("VkPhysicalDeviceProperties")
|| name == Some("VkPhysicalDeviceLimits")
|| name == Some("VkPhysicalDeviceSparseProperties")
|| ty.structextends.as_ref().map(|s| s.as_str()) == Some("VkPhysicalDeviceProperties2")
})
.for_each(|(ty, _)| {
let vulkan_ty_name = ty.name.as_ref().unwrap();
let required = vulkan_ty_name == "VkPhysicalDeviceProperties"
|| vulkan_ty_name == "VkPhysicalDeviceLimits"
|| vulkan_ty_name == "VkPhysicalDeviceSparseProperties"
;
let ty_name = if vulkan_ty_name == "VkPhysicalDeviceProperties" {
"properties_vulkan10.properties".to_owned()
} else if vulkan_ty_name == "VkPhysicalDeviceLimits" {
"properties_vulkan10.properties.limits".to_owned()
} else if vulkan_ty_name == "VkPhysicalDeviceSparseProperties" {
"properties_vulkan10.properties.sparse_properties".to_owned()
} else {
ffi_member(vulkan_ty_name)
};
members(ty)
.into_iter()
.for_each(|Member { name, ty, len }| {
if ty == "VkPhysicalDeviceLimits" || ty == "VkPhysicalDeviceSparseProperties" {
return;
}
let vulkano_member = name.to_snake_case();
let vulkano_ty = match name {
"apiVersion" => "crate::Version",
_ => vulkano_type(ty, len),
};
match properties.entry(vulkano_member.clone()) {
Entry::Vacant(entry) => {
entry.insert(VulkanoProperty {
member: vulkano_member.clone(),
ty: vulkano_ty.to_owned(),
vulkan_doc: format!("{}.html#limits-{}", vulkan_ty_name, name),
ffi_name: vulkano_member,
ffi_members: vec![ty_name.to_owned()],
required: required,
});
}
Entry::Occupied(entry) => {
entry.into_mut().ffi_members.push(ty_name.to_owned());
}
};
});
});
let mut names: Vec<_> = properties
.values()
.map(|feat| feat.member.clone())
.collect();
names.sort_unstable();
names
.into_iter()
.map(|name| properties.remove(&name).unwrap())
.collect()
}
fn write_doc<W>(writer: &mut W, feat: &VulkanoProperty)
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();
write!(writer, "\n\t\t\",").unwrap();
}
#[derive(Clone, Debug)]
struct VulkanoPropertyFfi {
member: String,
ty: String,
provided_by: Vec<String>,
conflicts: Vec<String>,
}
fn make_vulkano_properties_ffi<'a>(
types: &'a HashMap<&str, (&Type, Vec<&str>)>,
extensions: &IndexMap<&'a str, &Extension>,
) -> Vec<VulkanoPropertyFfi> {
let mut property_included_in: HashMap<&str, Vec<&str>> = HashMap::new();
sorted_structs(types)
.into_iter()
.map(|(ty, provided_by)| {
let ty_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 { name, .. }| {
match property_included_in.entry(name) {
Entry::Vacant(entry) => {
entry.insert(vec![ty_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(ty_name);
}
}
});
VulkanoPropertyFfi {
member: ffi_member(ty_name),
ty: 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("VkPhysicalDeviceProperties2")
})
.collect();
let regex = Regex::new(r"^VkPhysicalDeviceVulkan\d+Properties$").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("_properties").unwrap();
format!("properties_{}{}", base, suffix)
}
struct Member<'a> {
name: &'a str,
ty: &'a str,
len: Option<&'a str>,
}
fn members(ty: &Type) -> Vec<Member> {
let regex = Regex::new(r"\[([A-Za-z0-9_]+)\]\s*$").unwrap();
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,
});
let ty = def.markup.iter().find_map(|markup| match markup {
TypeMemberMarkup::Type(ty) => Some(ty.as_str()),
_ => None,
});
let len = def
.markup
.iter()
.find_map(|markup| match markup {
TypeMemberMarkup::Enum(len) => Some(len.as_str()),
_ => None,
})
.or_else(|| {
regex
.captures(&def.code)
.and_then(|cap| cap.get(1))
.map(|m| m.as_str())
});
if name != Some("sType") && name != Some("pNext") {
return name.map(|name| Member {
name,
ty: ty.unwrap(),
len,
});
}
}
None
})
.collect()
} else {
vec![]
}
}
fn vulkano_type(ty: &str, len: Option<&str>) -> &'static str {
if let Some(len) = len {
match ty {
"char" => "String",
"uint8_t" if len == "VK_LUID_SIZE" => "[u8; 8]",
"uint8_t" if len == "VK_UUID_SIZE" => "[u8; 16]",
"uint32_t" if len == "2" => "[u32; 2]",
"uint32_t" if len == "3" => "[u32; 3]",
"float" if len == "2" => "[f32; 2]",
_ => unimplemented!("{}[{}]", ty, len),
}
} else {
match ty {
"float" => "f32",
"int32_t" => "i32",
"int64_t" => "i64",
"size_t" => "usize",
"uint8_t" => "u8",
"uint32_t" => "u32",
"uint64_t" => "u64",
"VkBool32" => "bool",
"VkConformanceVersion" => "crate::device::physical::ConformanceVersion",
"VkDeviceSize" => "crate::DeviceSize",
"VkDriverId" => "crate::device::physical::DriverId",
"VkExtent2D" => "[u32; 2]",
"VkPhysicalDeviceType" => "crate::device::physical::PhysicalDeviceType",
"VkPointClippingBehavior" => "crate::device::physical::PointClippingBehavior",
"VkResolveModeFlags" => "crate::render_pass::ResolveModes",
"VkSampleCountFlags" => "crate::image::SampleCounts",
"VkSampleCountFlagBits" => "crate::image::SampleCount",
"VkShaderCorePropertiesFlagsAMD" => "crate::device::physical::ShaderCoreProperties",
"VkShaderFloatControlsIndependence" => {
"crate::device::physical::ShaderFloatControlsIndependence"
}
"VkShaderStageFlags" => "crate::pipeline::shader::ShaderStages",
"VkSubgroupFeatureFlags" => "crate::device::physical::SubgroupFeatures",
_ => unimplemented!("{}", ty),
}
}
}