blob: 0e53935e91d35b272dbcf7d4efc19a06e6272172 [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 super::{write_file, IndexMap, VkRegistryData};
use heck::{ToSnakeCase, ToUpperCamelCase};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use vk_parse::{Extension, ExtensionChild, InterfaceItem};
pub fn write(vk_data: &VkRegistryData) {
let entry_fns_output = fns_output(
&[],
"Entry",
"Raw Vulkan global entry point-level functions.\n\nTo use these, you need to include the Ash crate, using the same version Vulkano uses.",
);
let instance_fns_output = fns_output(
&instance_extension_fns_members(&vk_data.extensions),
"Instance",
"Raw Vulkan instance-level functions.\n\nTo use these, you need to include the Ash crate, using the same version Vulkano uses.",
);
let device_fns_output = fns_output(
&device_extension_fns_members(&vk_data.extensions),
"Device",
"Raw Vulkan device-level functions.\n\nTo use these, you need to include the Ash crate, using the same version Vulkano uses.",
);
write_file(
"fns.rs",
format!(
"vk.xml header version {}.{}.{}",
vk_data.header_version.0, vk_data.header_version.1, vk_data.header_version.2
),
quote! {
#entry_fns_output
#instance_fns_output
#device_fns_output
},
);
}
#[derive(Clone, Debug)]
struct FnsMember {
name: Ident,
fn_struct: Ident,
}
fn fns_output(extension_members: &[FnsMember], fns_level: &str, doc: &str) -> TokenStream {
let struct_name = format_ident!("{}Functions", fns_level);
let members = ["1_0", "1_1", "1_2", "1_3"]
.into_iter()
.map(|version| FnsMember {
name: format_ident!("v{}", version),
fn_struct: format_ident!("{}FnV{}", fns_level, version),
})
.chain(extension_members.iter().cloned())
.collect::<Vec<_>>();
let struct_items = members.iter().map(|FnsMember { name, fn_struct }| {
quote! { pub #name: ash::vk::#fn_struct, }
});
let load_items = members.iter().map(|FnsMember { name, fn_struct }| {
quote! { #name: ash::vk::#fn_struct::load(&mut load_fn), }
});
quote! {
#[doc = #doc]
#[allow(missing_docs)]
pub struct #struct_name {
#(#struct_items)*
pub _ne: crate::NonExhaustive,
}
impl #struct_name {
pub(crate) fn load<F>(mut load_fn: F) -> #struct_name
where F: FnMut(&CStr) -> *const c_void
{
#struct_name {
#(#load_items)*
_ne: crate::NonExhaustive(()),
}
}
}
impl std::fmt::Debug for #struct_name {
#[inline]
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
Ok(())
}
}
}
}
fn device_extension_fns_members(extensions: &IndexMap<&str, &Extension>) -> Vec<FnsMember> {
extensions
.values()
// Include any device extensions that have functions.
.filter(|ext| ext.ext_type.as_ref().unwrap() == "device")
.filter(|ext| {
ext.children.iter().any(|ch| {
if let ExtensionChild::Require { items, .. } = ch {
items
.iter()
.any(|i| matches!(i, InterfaceItem::Command { .. }))
} else {
false
}
})
})
.map(|ext| {
let base = ext.name.strip_prefix("VK_").unwrap().to_snake_case();
let name = format_ident!("{}", base);
let fn_struct = format_ident!("{}Fn", base.to_upper_camel_case());
FnsMember { name, fn_struct }
})
.collect()
}
fn instance_extension_fns_members(extensions: &IndexMap<&str, &Extension>) -> Vec<FnsMember> {
extensions
.values()
.filter(|ext| {
match ext.ext_type.as_deref().unwrap() {
// Include any instance extensions that have functions.
"instance" => ext.children.iter().any(|ch| {
if let ExtensionChild::Require { items, .. } = ch {
items
.iter()
.any(|i| matches!(i, InterfaceItem::Command { .. }))
} else {
false
}
}),
// Include device extensions that have functions containing "PhysicalDevice".
// Note: this test might not be sufficient in the long run...
"device" => ext.children.iter().any(|ch| {
if let ExtensionChild::Require { items, .. } = ch {
items
.iter()
.any(|i| matches!(i, InterfaceItem::Command { name, .. } if name.contains("PhysicalDevice")))
} else {
false
}
}),
_ => unreachable!(),
}
})
.map(|ext| {
let base = ext.name.strip_prefix("VK_").unwrap().to_snake_case();
let name = format_ident!("{}", base);
let fn_struct = format_ident!("{}Fn", base.to_upper_camel_case());
FnsMember { name, fn_struct }
})
.collect()
}