1 // Copyright (c) 2021 The Vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9
10 use super::{write_file, IndexMap, VkRegistryData};
11 use heck::{ToSnakeCase, ToUpperCamelCase};
12 use proc_macro2::{Ident, TokenStream};
13 use quote::{format_ident, quote};
14 use vk_parse::{Extension, ExtensionChild, InterfaceItem};
15
write(vk_data: &VkRegistryData)16 pub fn write(vk_data: &VkRegistryData) {
17 let entry_fns_output = fns_output(
18 &[],
19 "Entry",
20 "Raw Vulkan global entry point-level functions.\n\nTo use these, you need to include the Ash crate, using the same version Vulkano uses.",
21 );
22 let instance_fns_output = fns_output(
23 &instance_extension_fns_members(&vk_data.extensions),
24 "Instance",
25 "Raw Vulkan instance-level functions.\n\nTo use these, you need to include the Ash crate, using the same version Vulkano uses.",
26 );
27 let device_fns_output = fns_output(
28 &device_extension_fns_members(&vk_data.extensions),
29 "Device",
30 "Raw Vulkan device-level functions.\n\nTo use these, you need to include the Ash crate, using the same version Vulkano uses.",
31 );
32 write_file(
33 "fns.rs",
34 format!(
35 "vk.xml header version {}.{}.{}",
36 vk_data.header_version.0, vk_data.header_version.1, vk_data.header_version.2
37 ),
38 quote! {
39 #entry_fns_output
40 #instance_fns_output
41 #device_fns_output
42 },
43 );
44 }
45
46 #[derive(Clone, Debug)]
47 struct FnsMember {
48 name: Ident,
49 fn_struct: Ident,
50 }
51
fns_output(extension_members: &[FnsMember], fns_level: &str, doc: &str) -> TokenStream52 fn fns_output(extension_members: &[FnsMember], fns_level: &str, doc: &str) -> TokenStream {
53 let struct_name = format_ident!("{}Functions", fns_level);
54 let members = ["1_0", "1_1", "1_2", "1_3"]
55 .into_iter()
56 .map(|version| FnsMember {
57 name: format_ident!("v{}", version),
58 fn_struct: format_ident!("{}FnV{}", fns_level, version),
59 })
60 .chain(extension_members.iter().cloned())
61 .collect::<Vec<_>>();
62
63 let struct_items = members.iter().map(|FnsMember { name, fn_struct }| {
64 quote! { pub #name: ash::vk::#fn_struct, }
65 });
66
67 let load_items = members.iter().map(|FnsMember { name, fn_struct }| {
68 quote! { #name: ash::vk::#fn_struct::load(&mut load_fn), }
69 });
70
71 quote! {
72 #[doc = #doc]
73 #[allow(missing_docs)]
74 pub struct #struct_name {
75 #(#struct_items)*
76 pub _ne: crate::NonExhaustive,
77 }
78
79 impl #struct_name {
80 pub(crate) fn load<F>(mut load_fn: F) -> #struct_name
81 where F: FnMut(&CStr) -> *const c_void
82 {
83 #struct_name {
84 #(#load_items)*
85 _ne: crate::NonExhaustive(()),
86 }
87 }
88 }
89
90 impl std::fmt::Debug for #struct_name {
91 #[inline]
92 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
93 Ok(())
94 }
95 }
96 }
97 }
98
device_extension_fns_members(extensions: &IndexMap<&str, &Extension>) -> Vec<FnsMember>99 fn device_extension_fns_members(extensions: &IndexMap<&str, &Extension>) -> Vec<FnsMember> {
100 extensions
101 .values()
102 // Include any device extensions that have functions.
103 .filter(|ext| ext.ext_type.as_ref().unwrap() == "device")
104 .filter(|ext| {
105 ext.children.iter().any(|ch| {
106 if let ExtensionChild::Require { items, .. } = ch {
107 items
108 .iter()
109 .any(|i| matches!(i, InterfaceItem::Command { .. }))
110 } else {
111 false
112 }
113 })
114 })
115 .map(|ext| {
116 let base = ext.name.strip_prefix("VK_").unwrap().to_snake_case();
117 let name = format_ident!("{}", base);
118 let fn_struct = format_ident!("{}Fn", base.to_upper_camel_case());
119 FnsMember { name, fn_struct }
120 })
121 .collect()
122 }
123
instance_extension_fns_members(extensions: &IndexMap<&str, &Extension>) -> Vec<FnsMember>124 fn instance_extension_fns_members(extensions: &IndexMap<&str, &Extension>) -> Vec<FnsMember> {
125 extensions
126 .values()
127 .filter(|ext| {
128 match ext.ext_type.as_deref().unwrap() {
129 // Include any instance extensions that have functions.
130 "instance" => ext.children.iter().any(|ch| {
131 if let ExtensionChild::Require { items, .. } = ch {
132 items
133 .iter()
134 .any(|i| matches!(i, InterfaceItem::Command { .. }))
135 } else {
136 false
137 }
138 }),
139 // Include device extensions that have functions containing "PhysicalDevice".
140 // Note: this test might not be sufficient in the long run...
141 "device" => ext.children.iter().any(|ch| {
142 if let ExtensionChild::Require { items, .. } = ch {
143 items
144 .iter()
145 .any(|i| matches!(i, InterfaceItem::Command { name, .. } if name.contains("PhysicalDevice")))
146 } else {
147 false
148 }
149 }),
150 _ => unreachable!(),
151 }
152 })
153 .map(|ext| {
154 let base = ext.name.strip_prefix("VK_").unwrap().to_snake_case();
155 let name = format_ident!("{}", base);
156 let fn_struct = format_ident!("{}Fn", base.to_upper_camel_case());
157 FnsMember { name, fn_struct }
158 })
159 .collect()
160 }
161