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 self::spirv_grammar::SpirvGrammar;
11 use ahash::HashMap;
12 use once_cell::sync::Lazy;
13 use regex::Regex;
14 use std::{
15     env,
16     fmt::Display,
17     fs::File,
18     io::{BufWriter, Write},
19     path::Path,
20     process::Command,
21 };
22 use vk_parse::{
23     Enum, EnumSpec, Enums, EnumsChild, Extension, ExtensionChild, Feature, Format, InterfaceItem,
24     Registry, RegistryChild, SpirvExtOrCap, Type, TypeSpec, TypesChild,
25 };
26 
27 mod errors;
28 mod extensions;
29 mod features;
30 mod fns;
31 mod formats;
32 mod properties;
33 mod spirv_grammar;
34 mod spirv_parse;
35 mod spirv_reqs;
36 mod version;
37 
38 pub type IndexMap<K, V> = indexmap::IndexMap<K, V, ahash::RandomState>;
39 
autogen()40 pub fn autogen() {
41     let registry = get_vk_registry("vk.xml");
42     let vk_data = VkRegistryData::new(&registry);
43     let spirv_grammar = get_spirv_grammar("spirv.core.grammar.json");
44 
45     errors::write(&vk_data);
46     extensions::write(&vk_data);
47     features::write(&vk_data);
48     formats::write(&vk_data);
49     fns::write(&vk_data);
50     properties::write(&vk_data);
51     spirv_parse::write(&spirv_grammar);
52     spirv_reqs::write(&vk_data, &spirv_grammar);
53     version::write(&vk_data);
54 }
55 
write_file(file: impl AsRef<Path>, source: impl AsRef<str>, content: impl Display)56 fn write_file(file: impl AsRef<Path>, source: impl AsRef<str>, content: impl Display) {
57     let path = Path::new(&env::var_os("OUT_DIR").unwrap()).join(file.as_ref());
58     let mut writer = BufWriter::new(File::create(&path).unwrap());
59 
60     write!(
61         writer,
62         "\
63         // This file is auto-generated by vulkano autogen from {}.\n\
64         // It should not be edited manually. Changes should be made by editing autogen.\n\
65         \n\n{}",
66         source.as_ref(),
67         content,
68     )
69     .unwrap();
70 
71     std::mem::drop(writer); // Ensure that the file is fully written
72     Command::new("rustfmt").arg(&path).status().ok();
73 }
74 
get_vk_registry<P: AsRef<Path> + ?Sized>(path: &P) -> Registry75 fn get_vk_registry<P: AsRef<Path> + ?Sized>(path: &P) -> Registry {
76     let (registry, errors) = vk_parse::parse_file(path.as_ref()).unwrap();
77 
78     if !errors.is_empty() {
79         eprintln!("The following errors were found while parsing the file:");
80 
81         for error in errors {
82             eprintln!("{:?}", error);
83         }
84     }
85 
86     registry
87 }
88 
89 pub struct VkRegistryData<'r> {
90     pub header_version: (u16, u16, u16),
91     pub errors: Vec<&'r str>,
92     pub extensions: IndexMap<&'r str, &'r Extension>,
93     pub features: IndexMap<&'r str, &'r Feature>,
94     pub formats: Vec<&'r Format>,
95     pub spirv_capabilities: Vec<&'r SpirvExtOrCap>,
96     pub spirv_extensions: Vec<&'r SpirvExtOrCap>,
97     pub types: HashMap<&'r str, (&'r Type, Vec<&'r str>)>,
98 }
99 
100 impl<'r> VkRegistryData<'r> {
new(registry: &'r Registry) -> Self101     fn new(registry: &'r Registry) -> Self {
102         let aliases = Self::get_aliases(registry);
103         let extensions = Self::get_extensions(registry);
104         let features = Self::get_features(registry);
105         let formats = Self::get_formats(registry);
106         let spirv_capabilities = Self::get_spirv_capabilities(registry);
107         let spirv_extensions = Self::get_spirv_extensions(registry);
108         let errors = Self::get_errors(registry, &features, &extensions);
109         let types = Self::get_types(registry, &aliases, &features, &extensions);
110         let header_version = Self::get_header_version(registry);
111 
112         VkRegistryData {
113             header_version,
114             errors,
115             extensions,
116             features,
117             formats,
118             spirv_capabilities,
119             spirv_extensions,
120             types,
121         }
122     }
123 
get_header_version(registry: &Registry) -> (u16, u16, u16)124     fn get_header_version(registry: &Registry) -> (u16, u16, u16) {
125         static VK_HEADER_VERSION: Lazy<Regex> =
126             Lazy::new(|| Regex::new(r"#define\s+VK_HEADER_VERSION\s+(\d+)\s*$").unwrap());
127         static VK_HEADER_VERSION_COMPLETE: Lazy<Regex> = Lazy::new(|| {
128             Regex::new(r"#define\s+VK_HEADER_VERSION_COMPLETE\s+VK_MAKE_API_VERSION\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*VK_HEADER_VERSION\s*\)").unwrap()
129         });
130 
131         let mut major = None;
132         let mut minor = None;
133         let mut patch = None;
134 
135         for child in registry.0.iter() {
136             if let RegistryChild::Types(types) = child {
137                 for ty in types.children.iter() {
138                     if let TypesChild::Type(ty) = ty {
139                         if let TypeSpec::Code(code) = &ty.spec {
140                             if let Some(captures) = VK_HEADER_VERSION.captures(&code.code) {
141                                 patch = Some(captures.get(1).unwrap().as_str().parse().unwrap());
142                             } else if let Some(captures) =
143                                 VK_HEADER_VERSION_COMPLETE.captures(&code.code)
144                             {
145                                 major = Some(captures.get(2).unwrap().as_str().parse().unwrap());
146                                 minor = Some(captures.get(3).unwrap().as_str().parse().unwrap());
147                             }
148                         }
149                     }
150                 }
151             }
152         }
153 
154         (major.unwrap(), minor.unwrap(), patch.unwrap())
155     }
156 
get_aliases(registry: &Registry) -> HashMap<&str, &str>157     fn get_aliases(registry: &Registry) -> HashMap<&str, &str> {
158         registry
159             .0
160             .iter()
161             .filter_map(|child| {
162                 if let RegistryChild::Types(types) = child {
163                     return Some(types.children.iter().filter_map(|ty| {
164                         if let TypesChild::Type(ty) = ty {
165                             if let Some(alias) = ty.alias.as_deref() {
166                                 return Some((ty.name.as_ref().unwrap().as_str(), alias));
167                             }
168                         }
169                         None
170                     }));
171                 }
172                 None
173             })
174             .flatten()
175             .collect()
176     }
177 
get_errors<'a>( registry: &'a Registry, features: &IndexMap<&'a str, &'a Feature>, extensions: &IndexMap<&'a str, &'a Extension>, ) -> Vec<&'a str>178     fn get_errors<'a>(
179         registry: &'a Registry,
180         features: &IndexMap<&'a str, &'a Feature>,
181         extensions: &IndexMap<&'a str, &'a Extension>,
182     ) -> Vec<&'a str> {
183         (registry
184             .0
185             .iter()
186             .filter_map(|child| match child {
187                 RegistryChild::Enums(Enums {
188                     name: Some(name),
189                     children,
190                     ..
191                 }) if name == "VkResult" => Some(children.iter().filter_map(|en| {
192                     if let EnumsChild::Enum(en) = en {
193                         if let EnumSpec::Value { value, .. } = &en.spec {
194                             if value.starts_with('-') {
195                                 return Some(en.name.as_str());
196                             }
197                         }
198                     }
199                     None
200                 })),
201                 _ => None,
202             })
203             .flatten())
204         .chain(
205             (features.values().map(|feature| feature.children.iter()))
206                 .chain(
207                     extensions
208                         .values()
209                         .map(|extension| extension.children.iter()),
210                 )
211                 .flatten()
212                 .filter_map(|child| {
213                     if let ExtensionChild::Require { items, .. } = child {
214                         return Some(items.iter().filter_map(|item| match item {
215                             InterfaceItem::Enum(Enum {
216                                 name,
217                                 spec:
218                                     EnumSpec::Offset {
219                                         extends,
220                                         dir: false,
221                                         ..
222                                     },
223                                 ..
224                             }) if extends == "VkResult" => Some(name.as_str()),
225                             _ => None,
226                         }));
227                     }
228                     None
229                 })
230                 .flatten(),
231         )
232         .collect()
233     }
234 
get_extensions(registry: &Registry) -> IndexMap<&str, &Extension>235     fn get_extensions(registry: &Registry) -> IndexMap<&str, &Extension> {
236         let iter = registry
237             .0
238             .iter()
239             .filter_map(|child| {
240                 if let RegistryChild::Extensions(ext) = child {
241                     return Some(ext.children.iter().filter(|ext| {
242                         if ext.supported.as_deref() == Some("vulkan") && ext.obsoletedby.is_none() {
243                             return true;
244                         }
245                         false
246                     }));
247                 }
248                 None
249             })
250             .flatten();
251 
252         let extensions: HashMap<&str, &Extension> =
253             iter.clone().map(|ext| (ext.name.as_str(), ext)).collect();
254         let mut names: Vec<_> = iter.map(|ext| ext.name.as_str()).collect();
255         names.sort_unstable_by_key(|name| {
256             if name.starts_with("VK_KHR_") {
257                 (0, name.to_owned())
258             } else if name.starts_with("VK_EXT_") {
259                 (1, name.to_owned())
260             } else {
261                 (2, name.to_owned())
262             }
263         });
264 
265         names.iter().map(|&name| (name, extensions[name])).collect()
266     }
267 
get_features(registry: &Registry) -> IndexMap<&str, &Feature>268     fn get_features(registry: &Registry) -> IndexMap<&str, &Feature> {
269         registry
270             .0
271             .iter()
272             .filter_map(|child| {
273                 if let RegistryChild::Feature(feat) = child {
274                     return Some((feat.name.as_str(), feat));
275                 }
276 
277                 None
278             })
279             .collect()
280     }
281 
get_formats(registry: &Registry) -> Vec<&Format>282     fn get_formats(registry: &Registry) -> Vec<&Format> {
283         registry
284             .0
285             .iter()
286             .filter_map(|child| {
287                 if let RegistryChild::Formats(formats) = child {
288                     return Some(formats.children.iter());
289                 }
290                 None
291             })
292             .flatten()
293             .collect()
294     }
295 
get_spirv_capabilities(registry: &Registry) -> Vec<&SpirvExtOrCap>296     fn get_spirv_capabilities(registry: &Registry) -> Vec<&SpirvExtOrCap> {
297         registry
298             .0
299             .iter()
300             .filter_map(|child| {
301                 if let RegistryChild::SpirvCapabilities(capabilities) = child {
302                     return Some(capabilities.children.iter());
303                 }
304                 None
305             })
306             .flatten()
307             .collect()
308     }
309 
get_spirv_extensions(registry: &Registry) -> Vec<&SpirvExtOrCap>310     fn get_spirv_extensions(registry: &Registry) -> Vec<&SpirvExtOrCap> {
311         registry
312             .0
313             .iter()
314             .filter_map(|child| {
315                 if let RegistryChild::SpirvExtensions(extensions) = child {
316                     return Some(extensions.children.iter());
317                 }
318                 None
319             })
320             .flatten()
321             .collect()
322     }
323 
get_types<'a>( registry: &'a Registry, aliases: &HashMap<&'a str, &'a str>, features: &IndexMap<&'a str, &'a Feature>, extensions: &IndexMap<&'a str, &'a Extension>, ) -> HashMap<&'a str, (&'a Type, Vec<&'a str>)>324     fn get_types<'a>(
325         registry: &'a Registry,
326         aliases: &HashMap<&'a str, &'a str>,
327         features: &IndexMap<&'a str, &'a Feature>,
328         extensions: &IndexMap<&'a str, &'a Extension>,
329     ) -> HashMap<&'a str, (&'a Type, Vec<&'a str>)> {
330         let mut types: HashMap<&str, (&Type, Vec<&str>)> = registry
331             .0
332             .iter()
333             .filter_map(|child| {
334                 if let RegistryChild::Types(types) = child {
335                     return Some(types.children.iter().filter_map(|ty| {
336                         if let TypesChild::Type(ty) = ty {
337                             if ty.alias.is_none() {
338                                 return ty.name.as_ref().map(|name| (name.as_str(), (ty, vec![])));
339                             }
340                         }
341                         None
342                     }));
343                 }
344                 None
345             })
346             .flatten()
347             .collect();
348 
349         features
350             .iter()
351             .map(|(name, feature)| (name, &feature.children))
352             .chain(extensions.iter().map(|(name, ext)| (name, &ext.children)))
353             .for_each(|(provided_by, children)| {
354                 children
355                     .iter()
356                     .filter_map(|child| {
357                         if let ExtensionChild::Require { items, .. } = child {
358                             return Some(items.iter());
359                         }
360                         None
361                     })
362                     .flatten()
363                     .filter_map(|item| {
364                         if let InterfaceItem::Type { name, .. } = item {
365                             return Some(name.as_str());
366                         }
367                         None
368                     })
369                     .for_each(|item_name| {
370                         let item_name = aliases.get(item_name).unwrap_or(&item_name);
371                         if let Some(ty) = types.get_mut(item_name) {
372                             if !ty.1.contains(provided_by) {
373                                 ty.1.push(provided_by);
374                             }
375                         }
376                     });
377             });
378 
379         types
380             .into_iter()
381             .filter(|(_key, val)| !val.1.is_empty())
382             .collect()
383     }
384 }
385 
get_spirv_grammar<P: AsRef<Path> + ?Sized>(path: &P) -> SpirvGrammar386 pub fn get_spirv_grammar<P: AsRef<Path> + ?Sized>(path: &P) -> SpirvGrammar {
387     let mut grammar = SpirvGrammar::new(path);
388 
389     // Remove duplicate opcodes and enum values, preferring "more official" suffixes
390     grammar
391         .instructions
392         .sort_by_key(|instruction| (instruction.opcode, suffix_key(&instruction.opname)));
393     grammar
394         .instructions
395         .dedup_by_key(|instruction| instruction.opcode);
396 
397     grammar
398         .operand_kinds
399         .iter_mut()
400         .filter(|operand_kind| operand_kind.category == "BitEnum")
401         .for_each(|operand_kind| {
402             operand_kind.enumerants.sort_by_key(|enumerant| {
403                 let value = enumerant
404                     .value
405                     .as_str()
406                     .unwrap()
407                     .strip_prefix("0x")
408                     .unwrap();
409                 (
410                     u32::from_str_radix(value, 16).unwrap(),
411                     suffix_key(&enumerant.enumerant),
412                 )
413             });
414         });
415 
416     grammar
417         .operand_kinds
418         .iter_mut()
419         .filter(|operand_kind| operand_kind.category == "ValueEnum")
420         .for_each(|operand_kind| {
421             operand_kind.enumerants.sort_by_key(|enumerant| {
422                 (enumerant.value.as_u64(), suffix_key(&enumerant.enumerant))
423             });
424         });
425 
426     grammar
427 }
428 
suffix_key(name: &str) -> u32429 fn suffix_key(name: &str) -> u32 {
430     static VENDOR_SUFFIXES: Lazy<Regex> =
431         Lazy::new(|| Regex::new(r"(?:AMD|GOOGLE|INTEL|NV)$").unwrap());
432 
433     #[allow(clippy::bool_to_int_with_if)]
434     if VENDOR_SUFFIXES.is_match(name) {
435         3
436     } else if name.ends_with("EXT") {
437         2
438     } else if name.ends_with("KHR") {
439         1
440     } else {
441         0
442     }
443 }
444