// Copyright (c) 2021 The Vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use indexmap::IndexMap; use std::{collections::HashMap, io::Write, path::Path}; use vk_parse::{ Extension, ExtensionChild, Feature, InterfaceItem, Registry, RegistryChild, Type, TypeCodeMarkup, TypeSpec, TypesChild, }; mod extensions; mod features; mod fns; mod properties; pub fn write(writer: &mut W) { let registry = get_registry("vk.xml"); let aliases = get_aliases(®istry); let extensions = get_extensions(®istry); let features = get_features(®istry); let types = get_types(®istry, &aliases, &features, &extensions); let header_version = get_header_version(®istry); write!( writer, "\ // This file is auto-generated by vulkano-gen from vk.xml header version {}.\n\ // It should not be edited manually. Changes should be made by editing vulkano-gen.\n\ \n", header_version ) .unwrap(); extensions::write(writer, &extensions); write!(writer, "\n\n").unwrap(); fns::write(writer, &extensions); write!(writer, "\n\n").unwrap(); features::write(writer, &types, &extensions); write!(writer, "\n\n").unwrap(); properties::write(writer, &types, &extensions); write!(writer, "\n").unwrap(); } fn get_registry + ?Sized>(path: &P) -> Registry { let (registry, errors) = vk_parse::parse_file(path.as_ref()).unwrap(); if !errors.is_empty() { eprintln!("The following errors were found while parsing the file:"); for error in errors { eprintln!("{:?}", error); } } registry } fn get_aliases(registry: &Registry) -> HashMap<&str, &str> { registry .0 .iter() .filter_map(|child| { if let RegistryChild::Types(types) = child { return Some(types.children.iter().filter_map(|ty| { if let TypesChild::Type(ty) = ty { if let Some(alias) = ty.alias.as_ref().map(|s| s.as_str()) { return Some((ty.name.as_ref().unwrap().as_str(), alias)); } } None })); } None }) .flatten() .collect() } fn get_extensions(registry: &Registry) -> IndexMap<&str, &Extension> { let iter = registry .0 .iter() .filter_map(|child| { if let RegistryChild::Extensions(ext) = child { return Some(ext.children.iter().filter_map(|ext| { if ext.supported.as_ref().map(|s| s.as_str()) == Some("vulkan") && ext.obsoletedby.is_none() { return Some(ext); } None })); } None }) .flatten(); let extensions: HashMap<&str, &Extension> = iter.clone().map(|ext| (ext.name.as_str(), ext)).collect(); let mut names: Vec<_> = iter.map(|ext| ext.name.as_str()).collect(); names.sort_unstable_by_key(|name| { if name.starts_with("VK_KHR_") { (0, name.to_owned()) } else if name.starts_with("VK_EXT_") { (1, name.to_owned()) } else { (2, name.to_owned()) } }); names.iter().map(|&name| (name, extensions[name])).collect() } fn get_features(registry: &Registry) -> IndexMap<&str, &Feature> { registry .0 .iter() .filter_map(|child| { if let RegistryChild::Feature(feat) = child { return Some((feat.name.as_str(), feat)); } None }) .collect() } fn get_types<'a>( registry: &'a Registry, aliases: &'a HashMap<&str, &str>, features: &'a IndexMap<&str, &Feature>, extensions: &'a IndexMap<&str, &Extension>, ) -> HashMap<&'a str, (&'a Type, Vec<&'a str>)> { let mut types: HashMap<&str, (&Type, Vec<&str>)> = registry .0 .iter() .filter_map(|child| { if let RegistryChild::Types(types) = child { return Some(types.children.iter().filter_map(|ty| { if let TypesChild::Type(ty) = ty { if ty.alias.is_none() { return ty.name.as_ref().map(|name| (name.as_str(), (ty, vec![]))); } } None })); } None }) .flatten() .collect(); features .iter() .map(|(name, feature)| (name, &feature.children)) .chain(extensions.iter().map(|(name, ext)| (name, &ext.children))) .for_each(|(provided_by, children)| { children .iter() .filter_map(|child| { if let ExtensionChild::Require { items, .. } = child { return Some(items.iter()); } None }) .flatten() .filter_map(|item| { if let InterfaceItem::Type { name, .. } = item { return Some(name.as_str()); } None }) .for_each(|item_name| { let item_name = aliases.get(item_name).unwrap_or(&item_name); if let Some(ty) = types.get_mut(item_name) { if !ty.1.contains(provided_by) { ty.1.push(provided_by); } } }); }); types .into_iter() .filter(|(_key, val)| !val.1.is_empty()) .collect() } fn get_header_version(registry: &Registry) -> u16 { registry.0.iter() .find_map(|child| -> Option { if let RegistryChild::Types(types) = child { return types.children.iter().find_map(|ty| -> Option { if let TypesChild::Type(ty) = ty { if let TypeSpec::Code(code) = &ty.spec { if code.markup.iter().any(|mkup| matches!(mkup, TypeCodeMarkup::Name(name) if name == "VK_HEADER_VERSION")) { return Some(code.code.rsplit_once(' ').unwrap().1.parse().unwrap()); } } } None }); } None }) .unwrap() }