Add support for extension constants

This commit is contained in:
Maik Klein 2018-07-29 22:39:45 +02:00
parent 207f468f4b
commit 3cacb4a00a
3 changed files with 7575 additions and 5215 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,7 @@
#[macro_use]
extern crate generator; extern crate generator;
use generator::*; use generator::write_source_code;
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::path::Path; use std::path::Path;
fn main() { fn main() {
//let file = File::open("New-Vulkan-XML-Format/vk_new.xml").expect("vknew"); write_source_code(Path::new("Vulkan-Headers/registry/vk.xml"));
write_source_code(Path::new("vk.xml"));
} }

View file

@ -1,7 +1,4 @@
#![recursion_limit = "256"] #![recursion_limit = "256"]
// extern crate serde;
// #[macro_use]
// extern crate serde_derive;
#[macro_use] #[macro_use]
extern crate nom; extern crate nom;
extern crate heck; extern crate heck;
@ -15,9 +12,9 @@ pub extern crate vkxml;
use heck::{CamelCase, ShoutySnakeCase, SnakeCase}; use heck::{CamelCase, ShoutySnakeCase, SnakeCase};
use itertools::Itertools; use itertools::Itertools;
use proc_macro2::{Term, TokenTree}; use proc_macro2::Term;
use quote::Tokens; use quote::Tokens;
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::path::Path; use std::path::Path;
use syn::Ident; use syn::Ident;
pub trait ExtensionExt {} pub trait ExtensionExt {}
@ -380,6 +377,7 @@ impl ConstantExt for vkxml::ExtensionEnum {
self.notation.as_ref().map(|s| s.as_str()) self.notation.as_ref().map(|s| s.as_str())
} }
} }
impl ConstantExt for vkxml::Constant { impl ConstantExt for vkxml::Constant {
fn variant_ident(&self, enum_name: &str) -> Ident { fn variant_ident(&self, enum_name: &str) -> Ident {
variant_ident(enum_name, &self.name) variant_ident(enum_name, &self.name)
@ -392,6 +390,7 @@ impl ConstantExt for vkxml::Constant {
} }
} }
#[derive(Debug)]
pub enum Constant { pub enum Constant {
Number(i32), Number(i32),
Hex(String), Hex(String),
@ -459,7 +458,6 @@ impl Constant {
} }
pub fn from_extension_enum(constant: &vkxml::ExtensionEnum) -> Option<Self> { pub fn from_extension_enum(constant: &vkxml::ExtensionEnum) -> Option<Self> {
println!("{:#?}", constant);
let number = constant.number.map(|n| Constant::Number(n)); let number = constant.number.map(|n| Constant::Number(n));
let hex = constant.hex.as_ref().map(|hex| Constant::Hex(hex.clone())); let hex = constant.hex.as_ref().map(|hex| Constant::Hex(hex.clone()));
let bitpos = constant.bitpos.map(|bit| Constant::BitPos(bit)); let bitpos = constant.bitpos.map(|bit| Constant::BitPos(bit));
@ -593,7 +591,7 @@ impl ToTokens for vkxml::ReferenceType {
} }
} }
} }
fn name_to_tokens(type_name: &str) -> Tokens { fn name_to_tokens(type_name: &str) -> Ident {
let new_name = match type_name { let new_name = match type_name {
"int" => "c_int", "int" => "c_int",
"void" => "c_void", "void" => "c_void",
@ -609,10 +607,7 @@ fn name_to_tokens(type_name: &str) -> Tokens {
} }
}; };
let new_name = new_name.replace("FlagBits", "Flags"); let new_name = new_name.replace("FlagBits", "Flags");
let name = Term::intern(new_name.as_str()); Ident::from(new_name.as_str())
quote! {
#name
}
} }
fn to_type_tokens(type_name: &str, reference: Option<&vkxml::ReferenceType>) -> Tokens { fn to_type_tokens(type_name: &str, reference: Option<&vkxml::ReferenceType>) -> Tokens {
let new_name = name_to_tokens(type_name); let new_name = name_to_tokens(type_name);
@ -677,8 +672,6 @@ fn generate_function_pointers(ident: Ident, commands: &[&vkxml::Command]) -> quo
let params: Vec<Vec<(Ident, Tokens)>> = commands let params: Vec<Vec<(Ident, Tokens)>> = commands
.iter() .iter()
.map(|cmd| { .map(|cmd| {
let fn_name_raw = cmd.name.as_str();
let fn_name_snake = cmd.command_ident();
let params: Vec<_> = cmd let params: Vec<_> = cmd
.param .param
.iter() .iter()
@ -715,7 +708,6 @@ fn generate_function_pointers(ident: Ident, commands: &[&vkxml::Command]) -> quo
.collect(); .collect();
let expanded_params_ref = &expanded_params; let expanded_params_ref = &expanded_params;
let params_ref = &params;
let return_types: Vec<_> = commands let return_types: Vec<_> = commands
.iter() .iter()
.map(|cmd| cmd.return_type.type_tokens()) .map(|cmd| cmd.return_type.type_tokens())
@ -773,110 +765,122 @@ fn generate_function_pointers(ident: Ident, commands: &[&vkxml::Command]) -> quo
} }
} }
} }
pub fn generate_interface_item(items: &[vk_parse::InterfaceItem]) -> quote::Tokens { pub struct ExtensionConstant<'a> {
use vk_parse::InterfaceItem::*; pub name: &'a str,
for item in items { pub constant: Constant,
match item { }
Type { name,.. } => println!("type {}", name), impl<'a> ConstantExt for ExtensionConstant<'a> {
Enum(e) => println!("type {}", name), fn variant_ident(&self, enum_name: &str) -> Ident {
_ => () variant_ident(enum_name, self.name)
}
fn to_tokens(&self) -> Tokens {
self.constant.to_tokens()
}
fn notation(&self) -> Option<&str> {
None
}
}
pub fn generate_extension_constants<'a>(
extension: &'a vk_parse::Extension,
const_cache: &mut HashSet<&'a str>,
) -> quote::Tokens {
let items = extension
.items
.iter()
.filter_map(|item| match item {
vk_parse::ExtensionItem::Require { items, .. } => Some(items.iter()),
_ => None,
})
.flat_map(|iter| iter);
let enum_tokens = items.filter_map(|item| match item {
vk_parse::InterfaceItem::Enum(_enum) => {
use vk_parse::EnumSpec;
if const_cache.contains(_enum.name.as_str()) {
return None;
}
let (constant, extends) = match &_enum.spec {
EnumSpec::Alias { .. } => None,
EnumSpec::Value { .. } => None,
EnumSpec::Bitpos { bitpos, extends } => {
Some((Constant::BitPos(*bitpos as u32), extends.clone()))
}
EnumSpec::Offset {
offset,
extends,
extnumber,
..
} => {
let ext_base = 1000000000;
let ext_block_size = 1000;
let extnumber = extnumber.unwrap_or_else(|| extension.number.expect("number"));
let value = ext_base + (extnumber - 1) * ext_block_size + offset;
Some((Constant::Number(value as i32), Some(extends.clone())))
}
_ => None,
}?;
let extends = extends?;
let ext_constant = ExtensionConstant {
name: &_enum.name,
constant,
}; };
let ident = name_to_tokens(&extends);
let impl_block = bitflags_impl_block(&ident, &extends, &[&ext_constant]);
let doc_string = format!("Generated from '{}'", extension.name);
let q = quote!{
#[doc = #doc_string]
#impl_block
};
const_cache.insert(_enum.name.as_str());
Some(q)
} }
quote!{} _ => None,
});
quote!{
#(#enum_tokens)*
} }
pub fn generate_extension2( }
pub fn generate_extension_commands(
extension: &vk_parse::Extension, extension: &vk_parse::Extension,
cmd_map: &CommandMap, cmd_map: &CommandMap,
) -> Tokens {
let commands = extension
.items
.iter()
.filter_map(|ext_item| match ext_item {
vk_parse::ExtensionItem::Require { items, .. } => {
Some(items.iter().filter_map(|item| match item {
vk_parse::InterfaceItem::Command { name, .. } => {
cmd_map.get(name).map(|c| *c)
}
_ => None,
}))
}
_ => None,
})
.flat_map(|iter| iter)
.collect_vec();
let name = format!("{}Fn", extension.name.to_camel_case());
let ident = Ident::from(&name[2..]);
generate_function_pointers(ident, &commands)
}
pub fn generate_extension<'a>(
extension: &'a vk_parse::Extension,
cmd_map: &CommandMap,
const_cache: &mut HashSet<&'a str>,
) -> Option<quote::Tokens> { ) -> Option<quote::Tokens> {
let _ = extension let _ = extension
.supported .supported
.as_ref() .as_ref()
.filter(|s| s.as_str() == "vulkan")?; .filter(|s| s.as_str() == "vulkan")?;
extension let extension_tokens = generate_extension_constants(extension, const_cache);
.items let fp = generate_extension_commands(extension, cmd_map);
.iter() let q = quote!{
.filter_map(|item| match item {
vk_parse::ExtensionItem::Require {
extension: ext,
feature,
items,
..
} => {
//println!("feature {:?} - {:?}", feature, ext);
let t = generate_interface_item(items);
Some(t)
}
_ => None,
})
.collect_vec();
None
}
pub fn generate_extension(extension: &vkxml::Extension, cmd_map: &CommandMap) -> quote::Tokens {
// Don't generate disabled or reserved extensions
if extension.disabled {
return quote!{};
}
let cmd_refs = extension
.elements
.iter()
.flat_map(|ext| {
if let &vkxml::ExtensionElement::Require(ref spec) = ext {
spec.elements
.iter()
.filter_map(|extension_spec| match extension_spec {
vkxml::ExtensionSpecificationElement::CommandReference(ref cmd_ref) => {
Some(cmd_ref)
}
_ => None,
})
.collect()
} else {
vec![]
}
})
.collect_vec();
let commands = cmd_refs
.iter()
.filter_map(|cmd_ref| cmd_map.get(&cmd_ref.name).map(|c| *c))
.collect_vec();
let name = format!("{}Fn", extension.name.to_camel_case());
let ident = Ident::from(&name[2..]);
let fp = generate_function_pointers(ident, &commands);
let extension_enums = extension
.elements
.iter()
.flat_map(|ext| {
println!("{:?}", ext);
if let &vkxml::ExtensionElement::Require(ref spec) = ext {
spec.elements
.iter()
.filter_map(|extension_spec| match extension_spec {
vkxml::ExtensionSpecificationElement::Enum(ref _enum) => Some(_enum),
_ => None,
})
.collect()
} else {
vec![]
}
})
.collect_vec();
let extend_enums = extension_enums.iter().filter_map(|&constant| {
let c = Constant::from_extension_enum(constant)?;
let variant_ident = variant_ident(&constant.extends, &constant.name);
let tokens = c.to_tokens();
let enum_name = name_to_tokens(&constant.extends);
Some(quote! {
impl #enum_name {
pub const #variant_ident: Self = #enum_name(#tokens);
}
})
});
//println!("{:#?}", extension_enums);
quote!{
#fp #fp
#(#extend_enums)* #extension_tokens
} };
Some(q)
} }
pub fn generate_typedef(typedef: &vkxml::Typedef) -> Tokens { pub fn generate_typedef(typedef: &vkxml::Typedef) -> Tokens {
let typedef_name = to_type_tokens(&typedef.name, None); let typedef_name = to_type_tokens(&typedef.name, None);
@ -905,34 +909,6 @@ pub fn generate_bitmask(bitmask: &vkxml::Bitmask) -> Option<Tokens> {
}) })
} }
pub fn to_variant_ident(enum_name: &str, variant_name: &str) -> Ident {
let tag = ["AMD", "NN", "KHR", "NV", "EXT", "NVX", "KHX"]
.iter()
.filter_map(|tag| {
if enum_name.ends_with(tag) {
Some(tag)
} else {
None
}
})
.nth(0);
let name_without_tag = tag
.map(|t| enum_name.replace(t, ""))
.unwrap_or(enum_name.into());
let variant_without_tag = tag
.map(|t| variant_name.replace(&format!("_{}", t), ""))
.unwrap_or(variant_name.into());
let camel_case_name_enum = &name_without_tag.to_camel_case();
let name = variant_without_tag.to_camel_case()[2..].replace(camel_case_name_enum, "");
let is_digit = name.chars().nth(0).map(|c| c.is_digit(10)).unwrap_or(false);
if is_digit {
Ident::from(format!("Type{}", name).as_str())
} else {
Ident::from(name)
}
}
pub enum EnumType { pub enum EnumType {
Bitflags(Tokens), Bitflags(Tokens),
Enum(Tokens), Enum(Tokens),
@ -942,7 +918,6 @@ pub fn variant_ident(enum_name: &str, variant_name: &str) -> Ident {
let _name = enum_name.split("FlagBits").nth(0).expect("split"); let _name = enum_name.split("FlagBits").nth(0).expect("split");
let struct_name = _name.to_shouty_snake_case(); let struct_name = _name.to_shouty_snake_case();
let new_variant_name = variant_name.replace(&struct_name, "").replace("VK", ""); let new_variant_name = variant_name.replace(&struct_name, "").replace("VK", "");
// Not every variant in the vk.xml has a correct shouty snake case name
let new_variant_name = new_variant_name let new_variant_name = new_variant_name
.trim_matches('_') .trim_matches('_')
.to_shouty_snake_case() .to_shouty_snake_case()
@ -961,13 +936,13 @@ pub fn variant_ident(enum_name: &str, variant_name: &str) -> Ident {
pub fn bitflags_impl_block( pub fn bitflags_impl_block(
ident: &Ident, ident: &Ident,
_enum: &vkxml::Enumeration, enum_name: &str,
constants: &[&impl ConstantExt], constants: &[&impl ConstantExt],
) -> Tokens { ) -> Tokens {
let variants = constants let variants = constants
.iter() .iter()
.map(|constant| { .map(|constant| {
let variant_ident = constant.variant_ident(&_enum.name); let variant_ident = constant.variant_ident(enum_name);
let tokens = constant.to_tokens(); let tokens = constant.to_tokens();
(variant_ident, tokens) (variant_ident, tokens)
}) })
@ -996,9 +971,9 @@ pub fn bitflags_impl_block(
} }
} }
pub fn generate_enum( pub fn generate_enum<'a>(
_enum: &vkxml::Enumeration, _enum: &'a vkxml::Enumeration,
create_info_constants: &[&vkxml::Constant], const_cache: &mut HashSet<&'a str>,
) -> EnumType { ) -> EnumType {
let name = &_enum.name[2..]; let name = &_enum.name[2..];
let _name = name.replace("FlagBits", "Flags"); let _name = name.replace("FlagBits", "Flags");
@ -1011,6 +986,9 @@ pub fn generate_enum(
_ => None, _ => None,
}) })
.collect_vec(); .collect_vec();
for constant in &constants {
const_cache.insert(constant.name.as_str());
}
if name.contains("Bit") { if name.contains("Bit") {
let ident = Ident::from(_name.as_str()); let ident = Ident::from(_name.as_str());
@ -1020,21 +998,21 @@ pub fn generate_enum(
.fold(0, |acc, next| acc | next.bits()); .fold(0, |acc, next| acc | next.bits());
let all_bits_term = Term::intern(&format!("0b{:b}", all_bits)); let all_bits_term = Term::intern(&format!("0b{:b}", all_bits));
let impl_bitflags = bitflags_impl_block(&ident, _enum, &constants); let impl_bitflags = bitflags_impl_block(&ident, &_enum.name, &constants);
let q = quote!{ let q = quote!{
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct #ident(Flags); pub struct #ident(pub(crate) Flags);
vk_bitflags_wrapped!(#ident, #all_bits_term, Flags); vk_bitflags_wrapped!(#ident, #all_bits_term, Flags);
#impl_bitflags #impl_bitflags
}; };
EnumType::Bitflags(q) EnumType::Bitflags(q)
} else { } else {
let impl_block = bitflags_impl_block(&ident, _enum, &constants); let impl_block = bitflags_impl_block(&ident, &_enum.name, &constants);
let enum_quote = quote!{ let enum_quote = quote!{
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)] #[repr(C)]
pub struct #ident(pub i32); pub struct #ident(pub(crate) i32);
#impl_block #impl_block
}; };
let special_quote = match _name.as_str() { let special_quote = match _name.as_str() {
@ -1263,16 +1241,16 @@ pub fn generate_feature(feature: &vkxml::Feature, commands: &CommandMap) -> quot
#device #device
} }
} }
pub fn generate_constant(constant: &vkxml::Constant) -> Tokens { pub fn generate_constant<'a>(
constant: &'a vkxml::Constant,
cache: &mut HashSet<&'a str>,
) -> Tokens {
cache.insert(constant.name.as_str());
let c = Constant::from_constant(constant); let c = Constant::from_constant(constant);
let ident = Ident::from(constant.name.as_str()); let ident = Ident::from(constant.name.as_str());
let value = c.to_tokens(); let value = c.to_tokens();
let ty = c.ty().to_tokens(); let ty = c.ty().to_tokens();
quote!{ quote!{
// pub mod constants {
// pub const #ident: #ty = #value;
// }
//pub use constants::*;
pub const #ident: #ty = #value; pub const #ident: #ty = #value;
} }
} }
@ -1290,29 +1268,6 @@ pub fn write_source_code(path: &Path) {
}) })
.nth(0) .nth(0)
.expect("extension"); .expect("extension");
// for ext in extensions {
// println!("{}", ext.name);
// for item in &ext.items {
// match item {
// vk_parse::ExtensionItem::Require { items, .. } => {
// for interface in items {
// match interface {
// vk_parse::InterfaceItem::Enum(ref e) => match &e.spec {
// vk_parse::EnumSpec::Value { value, extends } => {
// print!("{} - ", e.name,);
// println!("{} {:?}", value, extends);
// }
// _ => (),
// },
// _ => (),
// }
// }
// }
// _ => (),
// }
// }
// println!("ext {} {:?}", ext.name, ext.supported);
// }
let spec = vk_parse::parse_file_as_vkxml(path); let spec = vk_parse::parse_file_as_vkxml(path);
let commands: HashMap<vkxml::Identifier, &vkxml::Command> = spec let commands: HashMap<vkxml::Identifier, &vkxml::Command> = spec
@ -1325,10 +1280,6 @@ pub fn write_source_code(path: &Path) {
.flat_map(|cmds| cmds.elements.iter().map(|cmd| (cmd.name.clone(), cmd))) .flat_map(|cmds| cmds.elements.iter().map(|cmd| (cmd.name.clone(), cmd)))
.collect(); .collect();
let _ = extensions
.iter()
.filter_map(|ext| generate_extension2(ext, &commands))
.collect_vec();
let features: Vec<&vkxml::Feature> = spec let features: Vec<&vkxml::Feature> = spec
.elements .elements
.iter() .iter()
@ -1339,16 +1290,6 @@ pub fn write_source_code(path: &Path) {
.flat_map(|features| features.elements.iter().map(|feature| feature)) .flat_map(|features| features.elements.iter().map(|feature| feature))
.collect(); .collect();
// let extensions: Vec<&vkxml::Extension> = spec
// .elements
// .iter()
// .filter_map(|elem| match elem {
// &vkxml::RegistryElement::Extensions(ref extensions) => Some(extensions),
// _ => None,
// })
// .flat_map(|extensions| extensions.elements.iter().map(|extension| extension))
// .collect();
let definitions: Vec<&vkxml::DefinitionsElement> = spec let definitions: Vec<&vkxml::DefinitionsElement> = spec
.elements .elements
.iter() .iter()
@ -1384,20 +1325,25 @@ pub fn write_source_code(path: &Path) {
.flat_map(|constants| constants.elements.iter()) .flat_map(|constants| constants.elements.iter())
.collect(); .collect();
let (enum_code, bitflags_code) = enums.into_iter().map(|e| generate_enum(e, &[])).fold( let mut const_cache = HashSet::new();
(Vec::new(), Vec::new()), let (enum_code, bitflags_code) = enums
|mut acc, elem| { .into_iter()
.map(|e| generate_enum(e, &mut const_cache))
.fold((Vec::new(), Vec::new()), |mut acc, elem| {
match elem { match elem {
EnumType::Enum(token) => acc.0.push(token), EnumType::Enum(token) => acc.0.push(token),
EnumType::Bitflags(token) => acc.1.push(token), EnumType::Bitflags(token) => acc.1.push(token),
}; };
acc acc
}, });
);
let constants_code: Vec<_> = constants let constants_code: Vec<_> = constants
.iter() .iter()
.map(|constant| generate_constant(constant)) .map(|constant| generate_constant(constant, &mut const_cache))
.collect(); .collect();
let extension_code = extensions
.iter()
.filter_map(|ext| generate_extension(ext, &commands, &mut const_cache))
.collect_vec();
let definition_code: Vec<_> = definitions let definition_code: Vec<_> = definitions
.into_iter() .into_iter()
@ -1409,10 +1355,6 @@ pub fn write_source_code(path: &Path) {
.map(|feature| generate_feature(feature, &commands)) .map(|feature| generate_feature(feature, &commands))
.collect(); .collect();
// let extension_code: Vec<_> = extensions
// .iter()
// .map(|ext| generate_extension(ext, &commands))
// .collect();
let mut file = File::create("../ash/src/vk.rs").expect("vk"); let mut file = File::create("../ash/src/vk.rs").expect("vk");
let bitflags_macro = vk_bitflags_wrapped_macro(); let bitflags_macro = vk_bitflags_wrapped_macro();
let handle_nondispatchable_macro = handle_nondispatchable_macro(); let handle_nondispatchable_macro = handle_nondispatchable_macro();
@ -1420,7 +1362,12 @@ pub fn write_source_code(path: &Path) {
let version_macros = vk_version_macros(); let version_macros = vk_version_macros();
let platform_specific_types = platform_specific_types(); let platform_specific_types = platform_specific_types();
let source_code = quote!{ let source_code = quote!{
#[doc(hidden)]
pub use libc::*; pub use libc::*;
#[doc(hidden)]
pub use self::extensions::*;
#[doc(hidden)]
pub use self::bitflags::*;
#version_macros #version_macros
#platform_specific_types #platform_specific_types
#bitflags_macro #bitflags_macro
@ -1429,9 +1376,15 @@ pub fn write_source_code(path: &Path) {
#(#feature_code)* #(#feature_code)*
#(#definition_code)* #(#definition_code)*
#(#enum_code)* #(#enum_code)*
pub mod bitflags {
use super::*;
#(#bitflags_code)* #(#bitflags_code)*
#(#constants_code)* }
//#(#extension_code)* #(#constants_code)*
}; pub mod extensions {
write!(&mut file, "{}", source_code); use super::*;
#(#extension_code)*
}
};
write!(&mut file, "{}", source_code).expect("Unable to write to file");
} }