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(®istry);
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