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