use {
crate::parse::*,
std::{io, ops::Deref, rc::Rc, cell::RefCell, collections::{HashMap, HashSet, hash_map::Entry}}
};
macro_rules! writeKhrImpl {
( @init
$writer:expr,
$lib_name:expr,
$lib_abbr:expr,
$fn_table:expr,
$fn_uninit:expr,
$fn_init:expr
) => { writeln!($writer, r#"
static mut LIB_{2}: Lib{1} = Lib{1} {{
lib : None,
pfn_{3}GetInstanceProcAddr : {{ extern "C" fn load(arg0: u64, arg1: *const u8) -> PFN_{3}VoidFunction {{ unsafe {{ LIB_{2}.load(); (LIB_{2}.pfn_{3}GetInstanceProcAddr)(arg0, arg1) }} }} load }},{4}
}};
struct Lib{1} {{
lib : Option<libloading::Library>,
pfn_{3}GetInstanceProcAddr : extern "C" fn(u64, *const u8) -> PFN_{3}VoidFunction,{5}
}}
impl Lib{1} {{
unsafe fn load(&mut self) {{
let lib = libloading::Library::new(LIB).expect("failed to load `lib{0}`");
self.pfn_{3}GetInstanceProcAddr = *lib.get(b"{3}GetInstanceProcAddr\0").expect("failed to load `{3}GetInstanceProcAddr`");{6}
self.lib = Some(lib);
log::trace!("loaded `lib{0}`");
}}
}}"#, $lib_name.to_lowercase(), $lib_name, $lib_name.to_uppercase(), $lib_abbr, $fn_table, $fn_uninit, $fn_init) };
(@table $writer:expr, $name:expr, $fn_table:expr, $fn_init:expr, $parent:expr ) => { write!($writer, r#"
pub struct {0}Table {{
{1}
}}
impl {0}Table {{
fn load(handle: {0}, table: &{3}Table) -> Arc<Self> {{
Arc::new(unsafe {{ Self {{
{2}
}} }})
}}
}}
impl fmt::Debug for {0}Table {{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{
f.debug_struct("{0}Table").finish()
}}
}}
"#, $name, $fn_table, $fn_init, $parent) };
(@impl $writer:expr, $handle:expr, $table:expr ) => { write!($writer, r#"
/// Wrapper for a `{0}` handle
#[derive(Clone, Debug)]
pub struct {0}Impl {{
pub handle: {0},
pub table: Arc<{1}Table>
}}
impl ops::Deref for {0}Impl {{
type Target = {0};
#[inline]
fn deref(&self) -> &{0} {{ &self.handle }}
}}
impl fmt::Display for {0}Impl {{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{
write!(f, "{{:#x}}", self.handle)
}}
}}
impl {0}Impl {{
"#, $handle, $table) };
}
#[derive(Clone, Debug)]
struct Alias {
name: String,
comment: Option<String>,
r#type: Rc<RefCell<Type>>
}
#[derive(Clone, Debug)]
struct Struct {
name: String,
comment: Option<String>,
category: KhrTypeStructCategory,
members: Vec<StructMember>
}
impl Struct {
fn has_lifetime(&self) -> bool {
self.members.iter().any(|m| match (&*m.r#type.borrow(), &m.len) {
(Type::Ref(ty), Some(_)) | (Type::Mut(ty), Some(_)) => ty.borrow().has_lifetime(),
(ty, _) => ty.has_lifetime()
})
}
fn is_copy(&self) -> bool {
self.members.iter().all(|m| m.r#type.borrow().is_copy())
}
fn is_debug(&self) -> bool {
self.category != KhrTypeStructCategory::Union && self.members.iter()
.all(|m| m.r#type.borrow().is_debug())
}
fn is_default(&self) -> bool {
self.category != KhrTypeStructCategory::Union && self.members.iter()
.all(|m| m.r#type.borrow().is_default() && m.len.is_none())
}
}
#[derive(Clone, Debug)]
struct StructMember {
name: String,
comment: Option<String>,
r#type: Rc<RefCell<Type>>,
optional: Option<String>,
len: Option<String>
}
#[derive(Clone, Debug)]
struct FnPtr {
params: Vec<Param>,
result: Vec<Rc<RefCell<Type>>>
}
#[derive(Clone, Debug)]
enum Type {
Ref(Rc<RefCell<Type>>),
Mut(Rc<RefCell<Type>>),
Arr(Rc<RefCell<Type>>, Option<Value>),
FnPtr(Rc<FnPtr>),
Alias(Rc<Alias>),
Struct(Rc<Struct>),
Enums(Rc<KhrEnums>),
Primitive(String),
Undefined(String)
}
impl Type {
fn from_str(s: &str, lookup: &mut impl FnMut(&str) -> Rc<RefCell<Self>>) -> Rc<RefCell<Self>> {
let s = s.trim();
if let Some(s) = s.strip_prefix("*const ") {
Rc::new(RefCell::new(Self::Ref(Self::from_str(s, lookup))))
} else if let Some(s) = s.strip_prefix("*mut ") {
Rc::new(RefCell::new(Self::Mut(Self::from_str(s, lookup))))
} else if let (Some(s), true) = (s.strip_prefix('['), s.ends_with(s)) {
Rc::new(RefCell::new(match s.rfind(';') {
Some(i) => Self::Arr(Self::from_str(&s[..i], lookup), Some(Value::Literal(s[i + 1..s.len() - 1].to_string(), lookup("usize")))),
None => {
Self::Arr(Self::from_str(s, lookup), None)
}
}))
} else {
lookup(s)
}
}
fn has_lifetime(&self) -> bool {
match self {
Self::Ref(_) | Self::Mut(_) => true,
Self::Struct(s) => s.has_lifetime(),
Self::Arr(ty, _) => ty.borrow().has_lifetime(),
Self::Alias(v) => v.r#type.borrow().has_lifetime(),
Self::Primitive(_) | Self::FnPtr(_) | Self::Enums(_) | Self::Undefined(_) => false
}
}
fn is_copy(&self) -> bool {
match self {
Self::Ref(_) | Self::Primitive(_) | Self::FnPtr(_) | Self::Enums(_) => true,
Self::Mut(_) | Self::Undefined(_) => false,
Self::Struct(s) => s.is_copy(),
Self::Arr(ty, _) => ty.borrow().is_copy(),
Self::Alias(v) => v.r#type.borrow().is_copy()
}
}
fn is_debug(&self) -> bool {
match self {
Self::Ref(v) | Self::Mut(v) => v.borrow().is_debug(),
Self::Struct(_s) => false,
Self::FnPtr(_)| Self::Undefined(_) | Self::Arr(_, Some(_)) => false,
Self::Primitive(_) | Self::Enums(_) | Self::Arr(_, None) => true,
Self::Alias(v) => v.r#type.borrow().is_debug()
}
}
fn is_default(&self) -> bool {
match self {
Self::Ref(_) | Self::Mut(_) | Self::Arr(_, None) | Self::Primitive(_) => true,
Self::FnPtr(_) | Self::Enums(_) | Self::Undefined(_) | Self::Arr(_, Some(_)) => false,
Self::Struct(_s) => false,
Self::Alias(v) => v.r#type.borrow().is_default()
}
}
}
impl std::fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match (self, f.alternate(), f.sign_plus()) {
(Self::Ref(ty), false, false) => write!(f, "&{}", ty.borrow()),
(Self::Ref(ty), false, true) => write!(f, "&'a {:+}", ty.borrow()),
(Self::Ref(ty), true, false) => write!(f, "*const {:#}", ty.borrow()),
(Self::Ref(ty), true, true) => write!(f, "*const {:+#}", ty.borrow()),
(Self::Mut(ty), false, false) => write!(f, "&mut {}", ty.borrow()),
(Self::Mut(ty), false, true) => write!(f, "&'a mut {:+}", ty.borrow()),
(Self::Mut(ty), true, false) => write!(f, "*mut {:#}", ty.borrow()),
(Self::Mut(ty), true, true) => write!(f, "*mut {:+#}", ty.borrow()),
(Self::Arr(ty, None), false, false) => write!(f, "[{}]", ty.borrow()),
(Self::Arr(ty, None), false, true) => write!(f, "[{:+}]", ty.borrow()),
(Self::Arr(ty, None), true, _) => write!(f, "{}", ty.borrow()),
(Self::Arr(ty, Some(len)), false, false) => write!(f, "[{}; {} as usize]", ty.borrow(), len),
(Self::Arr(ty, Some(len)), false, true) => write!(f, "[{:+}; {} as usize]", ty.borrow(), len),
(Self::Arr(ty, Some(len)), true, _) => write!(f, "[{:#}; {} as usize]", ty.borrow(), len),
(Self::Alias(alias), _, lt) => write!(f, "{}{}", &alias.name, if lt && alias.r#type.borrow().has_lifetime() { "<'a>" } else { "" }),
(Self::Struct(s), _, lt) => write!(f, "{}{}", &s.name, if lt && s.has_lifetime() { "<'a>" } else { "" }),
(Self::Enums(enums), ..) => write!(f, "{}", &enums.name),
(Self::Primitive(s), ..) | (Self::Undefined(s), ..) => f.write_str(s),
(Self::FnPtr(fn_ptr), _, _) => {
let FnPtr { params, result } = &**fn_ptr;
f.write_str("extern fn(\n")?;
for param in params {
match param {
Param { name, ty, optional: Some(s), .. } if s == "true" => writeln!(f, "\t{:<45}: Option<{}>,", name, ty.borrow()),
Param { name, ty, .. } => writeln!(f, "\t{:<45}: {},", name, ty.borrow())
}?;
}
match result.as_slice() {
[] => f.write_str(")"),
[ty] => writeln!(f, ") -> {}", ty.borrow()),
result => {
f.write_str(") -> (\n")?;
for ty in result {
writeln!(f, "{},", ty.borrow())?;
}
f.write_str(")")
}
}
}
}
}
}
#[derive(Clone, Debug)]
enum Value {
Literal(String, Rc<RefCell<Type>>),
#[allow(dead_code)]
Enum(Rc<KhrEnums>, usize)
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Literal(val, _) => f.write_str(val),
Self::Enum(_enums, _idx) => unimplemented!()
}
}
}
#[derive(Clone, Debug)]
struct DispatchableHandle {
name: Option<String>,
commands: Vec<Command>
}
#[derive(Clone, Debug)]
struct Command {
name: String,
comment: Option<String>,
feature: Option<String>,
result: Rc<RefCell<Type>>,
params: Vec<Param>
}
#[derive(Clone, Debug)]
struct Param {
name: String,
comment: Option<String>,
ty: Rc<RefCell<Type>>,
len: Option<String>,
optional: Option<String>
}
#[derive(Clone, Debug)]
enum Item {
Comment(String),
Type(Rc<RefCell<Type>>),
Macro { name: String, content: String }
}
#[allow(clippy::cognitive_complexity)]
pub fn gen(
writer: &mut impl io::Write,
elements: impl IntoIterator<Item = RegistryElement>,
api: Api,
mangle: bool
) -> io::Result<()> {
use std::fmt::Write;
let [beginning, api_name, api_name_upper, api_name_lower, null_handle, load_func, api_suffix_lower, api_suffix] = match api {
Api::Vulkan => VK_STRINGS,
Api::OpenXr => XR_STRINGS
};
let skip_methods = SKIPPED_METHODS.iter()
.copied()
.collect::<HashSet<&str>>();
let mut types = PRIMITIVE_TYPES.iter()
.cloned()
.map(|ty| (ty.to_string(), Rc::new(RefCell::new(Type::Primitive(ty.to_string())))))
.collect::<HashMap<_, _>>();
let mut tables = HashMap::new();
let mut static_uninit = String::new();
let mut handles = HashMap::new();
handles.insert(None, DispatchableHandle { name: None, commands: Vec::new() });
tables.extend(match api {
Api::Vulkan => vec![
(None, (String::new(), String::new(), load_func.trim_start_matches("pfn_"), "")),
(Some("VkInstance"), (String::new(), String::new(), "vkGetInstanceProcAddr", "")),
(Some("VkDevice"), (String::new(), String::new(), "vkGetDeviceProcAddr", "VkInstance")),
],
Api::OpenXr => vec![
(None, (String::new(), String::new(), load_func.trim_start_matches("pfn_"), "")),
(Some("XrInstance"), (String::new(), String::new(), "xrGetInstanceProcAddr", "")),
],
}.into_iter());
let mut proto = String::new();
let mut header = String::new();
let mut call = String::new();
let mut uninit = String::new();
let mut uninit_call = String::new();
let items = elements.into_iter().filter_map(|e| Some(match e {
RegistryElement::Type(ty) => {
let (name, ty) = match ty {
KhrType::Alias(KhrTypeAlias { category, name, alias, body, .. }) => {
if category == KhrTypeAliasCategory::Handle {
handles.insert(Some(name.clone()), DispatchableHandle {
name: Some(name.clone()),
commands: Vec::new()
});
}
(name.clone(), Type::Alias(Rc::new(Alias {
name,
comment: None,
r#type: resolve_type(&convert_c_type(alias.as_ref(), &body), &mut types),
})))
},
KhrType::Struct(KhrTypeStruct { category, name, members, comment }) => (name.clone(), Type::Struct(Rc::new(Struct {
name,
comment,
category,
members: members.into_iter().filter_map(|m| match m {
KhrTypeStructMemberVariant::Comment(_) => None,
KhrTypeStructMemberVariant::Member(KhrTypeStructMember { r#type, name, optional, len, comment, }) => Some(StructMember {
name,
comment,
r#type: resolve_type(&r#type, &mut types),
optional,
len
})
}).collect()
}))),
KhrType::FuncPtr(KhrTypeFuncPtr { name, comment, result, params }) => (name.clone(), Type::Alias(Rc::new(Alias {
name,
comment,
r#type: Rc::new(RefCell::new(Type::FnPtr(Rc::new(FnPtr {
params: params.into_iter().map(|param| Param {
name: param.name,
comment: param.comment,
ty: resolve_type(¶m.r#type, &mut types),
len: param.len,
optional: param.optional
}).collect(),
result: vec![resolve_type(&result, &mut types)]
}))))
}))),
_ => return None
};
Item::Type(insert_type(name, ty, &mut types))
}
RegistryElement::Enums(enums) => {
Item::Type(insert_type(enums.name.clone(), Type::Enums(Rc::new(enums)), &mut types))
}
RegistryElement::Command(cmd) => return match cmd {
KhrCommand::Command { proto, param, comment, feature } if !skip_methods.contains(proto.name.as_str()) => {
match handles.get_mut(¶m.first().map(|p| p.r#type.clone())) {
Some(v) => v,
None => handles.get_mut(&None).unwrap()
}.commands.push(Command {
name: proto.name,
comment,
feature,
result: resolve_type(&proto.r#type, &mut types),
params: param.into_iter().map(|KhrCommandParam { r#type, name, optional, len, comment, }| Param {
name,
comment,
ty: resolve_type(&r#type, &mut types),
len,
optional
}).collect()
});
None
},
KhrCommand::Alias { name, ..} if !skip_methods.contains(name.as_str()) => {
None
},
_ => None
},
RegistryElement::Macro { name, content } => Item::Macro { name, content },
RegistryElement::Comment(comment) => Item::Comment(comment)
})).collect::<Vec<_>>();
writer.write_all(beginning.as_bytes())?;
for item in items {
match item {
Item::Comment(comment) => for l in comment.lines() { writeln!(writer, "// {}", l.trim())?; },
Item::Macro { name, content } => writeln!(writer, "#[inline] pub const fn {}{}", name, content)?,
Item::Type(ty) => match &*ty.borrow() {
Type::Alias(alias) => {
let Alias { name, comment, r#type } = &**alias;
let ty = r#type.borrow();
writeln!(writer)?;
write_comment(comment, writer)?;
writeln!(writer, "pub type {0:<45}{2} = {1:+};", name, ty, if ty.has_lifetime() { "<'a>" } else { "" })?;
},
Type::Struct(r#struct) => {
let derive_debug = r#struct.is_debug();
let derive_default = r#struct.is_default();
let derive_clone = r#struct.is_copy();
let lifetime = r#struct.has_lifetime();
let a_lifetime = if lifetime { "<'_>" } else { "" };
let Struct { name, comment, category, members } = &**r#struct;
writeln!(writer)?;
write_comment(comment, writer)?;
let derives = [
derive_clone.then(|| "Copy, Clone"),
derive_debug.then(|| "Debug"),
derive_default.then(|| "Default")
].iter()
.copied()
.flatten()
.fold(String::new(), |mut buf, s| { buf.push_str(s); buf.push_str(", "); buf });
writeln!(writer, "#[repr(C)]\n#[derive({})]\npub {} {}{} {{", derives.trim_end_matches(", "), category, name, if lifetime { "<'a>" } else { "" })?;
for member in members {
let StructMember { name, comment, r#type, optional, len } = member;
if let Some(comment) = comment {
writeln!(writer, "\t/// {}", comment)?;
}
match (&*r#type.borrow(), optional.as_deref() == Some("true"), len) {
(ty @ Type::Ref(_), _, Some(_)) => writeln!(writer, "\tpub {:<45}: {:+#},", name, ty),
(ty @ Type::Mut(_), _, Some(_)) => writeln!(writer, "\tpub {:<45}: {:+#},", name, ty),
(ty @ Type::Ref(_), true, _) | (ty @ Type::Mut(_), true, _) =>
writeln!(writer, "\tpub {:<45}: Option<{:+}>,", name, ty),
(ty, ..) if *category == KhrTypeStructCategory::Union && !ty.is_copy() =>
writeln!(writer, "\tpub {:<45}: std::mem::ManuallyDrop<{:+}>,", name, ty),
(ty, ..) =>
writeln!(writer, "\tpub {:<45}: {:+},", name, ty)
}?;
}
writeln!(writer, "}}\n\nunsafe impl Send for {0}{1} {{}}\nunsafe impl Sync for {0}{1} {{}}\n",
name, a_lifetime)?;
if !derive_debug {
writeln!(writer, "impl fmt::Debug for {0}{1} {{\n\tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\t\tf.debug_struct(\"{0}\").finish()\n\t}}\n}}\n",
name, a_lifetime)?;
}
if !derive_default {
write!(writer, "impl Default for {0}{1} {{ \n\tfn default() -> Self {{\n\t\tunsafe {{ MaybeUninit::zeroed().assume_init() }}\n\t}}\n}}\n",
name, a_lifetime)?;
}
}
Type::Enums(enums) => {
let KhrEnums { name, r#type, comment, enums } = &**enums;
writeln!(writer)?;
write_comment(comment, writer)?;
let enum_name = &name;
match r#type {
KhrEnumsType::Enum | KhrEnumsType::Bitmask => {
writeln!(writer, "pub use self::{0}::*;\n\n#[repr(C)]\n#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]\npub enum {0} {{", name)?;
let default = enums.iter().find_map(|e| match e {
KhrEnumsVariant::Enum { name, .. } => Some(name.as_str()),
_ => None
}).unwrap_or_else(|| {
writeln!(writer, "\t__Default__ = 0,").unwrap();
"__Default__"
});
for e in enums {
match e {
KhrEnumsVariant::Comment(comment) => write_comment_pre("\t //", Some(comment), writer)?,
KhrEnumsVariant::Enum { name, value, comment, .. } => {
if let KhrEnumValue::Alias(_) = value { continue; }
write_comment_pre("\t ///", comment.as_ref().map(String::as_str), writer)?;
writeln!(writer, "\t{:<80} = {},", name, value)?;
},
_ => ()
}
}
writeln!(writer, "}}\n\nimpl Default for {} {{\n\tfn default() -> Self {{\n\t\tSelf::{}\n\t}}\n}}",
name, default)?;
let mut first = true;
for e in enums {
if let KhrEnumsVariant::Enum { name, value: KhrEnumValue::Alias(alias), comment, .. } = e {
if first { writeln!(writer)?; first = false; }
write_comment_pre(" writeln!(writer, "pub const {:<80}: {} = {};", name, enum_name, alias)?;
}
}
}
_ => {
writeln!(writer)?;
for e in enums {
match e {
KhrEnumsVariant::Comment(comment) => write_comment_pre("\t KhrEnumsVariant::Enum { name, value, comment, .. } => {
write_comment(comment, writer)?;
let __tmp__;
writeln!(writer, "pub const {:<50}: {} = {};", name, if name == "VK_QUEUE_FAMILY_EXTERNAL_KHR" || name == "VK_SHADER_UNUSED_NV" {
"u32"
} else {
__tmp__ = value.to_string();
parse_type(&__tmp__)
}, value)?;
},
_ => ()
}
}
}
}
}
_ => println!("ERROR: invalid type declaration")
}
}
}
for handle in handles.values() {
let DispatchableHandle { name: handle, commands } = handle;
let handle = handle.as_deref();
let table_handle = match (api, handle) {
(_, None) => None,
(Api::Vulkan, Some("VkInstance"))
| (Api::Vulkan, Some("VkPhysicalDevice")) => Some("VkInstance"),
(Api::Vulkan, Some(_)) => Some("VkDevice"),
(Api::OpenXr, Some(_)) => Some("XrInstance")
};
let (table, init, load_fn, _) = tables.get_mut(&table_handle).expect("failed to find table");
let table_handle = table_handle.unwrap_or("LibVulkan");
match handle {
Some(handle @ "VkInstance") | Some(handle @ "XrInstance") => {
writeKhrImpl!(@impl writer, handle, table_handle)?;
writeln!(writer, "\tpub fn new(handle: {0}) -> Self {{\n\t\tSelf {{ handle, table: {0}Table::load(handle) }}\n\t}}", handle)
}
Some(handle @ "VkDevice") => {
writeKhrImpl!(@impl writer, handle, table_handle)?;
writeln!(writer, "\tpub fn new(handle: {0}, parent: &VkInstanceImpl) -> Self {{\n\t\tSelf {{ handle, table: {0}Table::load(handle, &parent.table) }}\n\t}}", handle)
}
Some(handle) => {
writeKhrImpl!(@impl writer, handle, table_handle)?;
writeln!(writer, "\tpub fn new(handle: {0}, parent: &{1}Impl) -> Self {{\n\t\tSelf {{ handle, table: parent.table.clone() }}\n\t}}", handle, table_handle)
},
None => writeln!(writer, "impl {}InstanceImpl {{", api_suffix)
}?;
for Command { name, comment, feature, result, params } in commands {
if skip_methods.contains(&name.as_str()) { continue; }
// mangle
let mut mangled_name = name.clone();
if mangle {
mangled_name = name.replace(handle.unwrap_or("Instance")
.trim_start_matches("Vk").trim_start_matches("Xr"), "");
// remove `vk`/`xr`
mangled_name.remove(0);
mangled_name.remove(0);
// fist char to lower case
let ch = mangled_name.remove(0).to_ascii_lowercase();
mangled_name.insert(0, ch);
}
let params = ¶ms[if handle.is_none() { 0 } else { 1 }..];
let result = result.borrow();
// get array lengths
let lens = params.iter()
.filter_map(|param| param.len.as_deref()
.filter(|_| matches!(&*param.ty.borrow(), Type::Ref(_) | Type::Mut(_)))
.map(|len| (len, param)))
.collect::<HashMap<_, _>>();
// clear buffers
proto.clear(); // table struct entry
header.clear(); // rust function declaration
call.clear(); // ffi call
uninit.clear();
uninit_call.clear();
// init buffers
if let Some(handle) = &handle {
proto.push_str(handle);
proto.push_str(", ");
header.push_str("\n\t\t&self,");
call.push_str("self.handle, ");
}
// write buffers
for Param { name, ty, comment, len, optional, .. } in params {
let ty = ty.borrow();
let mut name = &*name;
let ffi_name = name;
let mut optional = optional.as_deref() == Some("true");
let has_len = len.as_ref().map_or(false, |len| lens.contains_key(&&**len));
let len = lens.contains_key(&&**name) && matches!(&*ty, Type::Primitive(_));
if len {
optional = lens.get(&&**name).unwrap().optional.as_deref() == Some("true");
name = &lens.get(&&**name).unwrap().name;
}
match &*ty {
Type::Ref(ty) | Type::Mut(ty) => if let Type::Primitive(v) = &*ty.borrow() {
if v == "()" && !has_len { call.push('('); }
} else {},
_ => ()
}
call.push_str(name);
call.push_str(match (&*ty, has_len, len, optional) {
(_, _, true, true) => ".as_ref().map_or(0, |v| v.len() as _), ",
(_, _, true, false) => ".len() as _, ",
(Type::Ref(_), false, false, true) => ".map_or(std::ptr::null(), |v| v as _) as _, ",
(Type::Mut(_), false, false, true) => ".map_or(std::ptr::null_mut(), |v| v as _) as _, ",
(Type::Ref(_), true, false, true) => ".map_or(std::ptr::null(), <[_]>::as_ptr) as _, ",
(Type::Mut(_), true, false, true) => ".map_or(std::ptr::null_mut(), <[_]>::as_mut_ptr) as _, ",
(Type::Ref(_), true, false, false) => ".as_ptr() as _, ",
(Type::Mut(_), true, false, false) => ".as_mut_ptr() as _, ",
_ => ", "
});
if !has_len && !optional {
match &*ty {
Type::Ref(ty2) | Type::Mut(ty2) => if let Type::Primitive(v) = &*ty2.borrow() {
if v == "()" {
if call.ends_with(", ") { call.truncate(call.len() - 2); }
call.push_str(").into(), ");
}
} else {
if call.ends_with(", ") { call.truncate(call.len() - 2); }
match &*ty {
Type::Ref(_) => call.push_str(" as *const _ as _, "),
Type::Mut(_) => call.push_str(" as *mut _ as _, "),
_ => unreachable!()
}
},
_ => ()
}
}
write!(proto, "{:#}, ", ty).unwrap();
if handle.is_none() {
write!(uninit, "{}: {:#}, ", ffi_name, ty).unwrap();
write!(uninit_call, "{}, ", ffi_name).unwrap();
}
if len {
continue;
}
if let Some(comment) = comment {
write!(header, " }
let ty_str = match (&*ty, optional, has_len) {
(Type::Ref(ty), false, false) => format!("&{}", ty.borrow()),
(Type::Mut(ty), false, false) => format!("&mut {}", ty.borrow()),
(Type::Ref(ty), false, true) => format!("&[{}]", ty.borrow()),
(Type::Mut(ty), false, true) => format!("&mut [{}]", ty.borrow()),
(Type::Ref(ty), true, false) => format!("Option<&{}>", ty.borrow()),
(Type::Mut(ty), true, false) => format!("Option<&mut {}>", ty.borrow()),
(Type::Ref(ty), true, true) => format!("Option<&[{}]>", ty.borrow()),
(Type::Mut(ty), true, true) => format!("Option<&mut [{}]>", ty.borrow()),
(ty, ..) => format!("{}", ty)
};
write!(header, "\n\t\t{:<45}: {},", name, ty_str).unwrap();
};
let proto = proto.trim_end_matches(", ");
let header = header.trim_end_matches(',');
let call = call.trim_end_matches(", ");
if let Some(comment) = comment {
for l in comment.lines() {
write!(writer, "\n\t/// {}", l).unwrap();
}
}
if let Some(feature) = feature {
write!(writer, "\n\t#[cfg(feature = \"{}\")]", feature).unwrap();
write!(table, "\n\t#[cfg(feature = \"{}\")]", feature).unwrap();
if handle.is_some() {
write!(init, "\n\t\t\t#[cfg(feature = \"{}\")]", feature).unwrap();
} else {
write!(init, "\n\t\t#[cfg(feature = \"{}\")]\n\t\t{{ ", feature).unwrap();
write!(static_uninit, "\n\t#[cfg(feature = \"{}\")]", feature).unwrap();
}
}
write!(table, "\n\tpfn_{:<40}: extern \"C\" fn({}) -> {},",
name, proto, result).unwrap();
if handle.is_some() {
write!(writer, "\n\tpub fn {}({}\n\t) -> {} {{\n\t\t(self.table.pfn_{})({})\n\t}}\n",
mangled_name, &header, result, name, &call).unwrap();
write!(init, "\n\t\t\tpfn_{:<60}: transmute((table.pfn_{})(handle, b\"{0}\\0\".as_ptr())),",
name, load_fn).unwrap();
} else {
write!(writer, "\n\tpub fn {}({}\n\t) -> {} {{\n\t\tunsafe {{ (LIB_{}.pfn_{})({}) }}\n\t}}\n",
mangled_name, &header, result, api_name_upper, name, &call).unwrap();
write!(static_uninit, "\n\tpfn_{0:<40}: {{ extern \"C\" fn load({2}) -> {1} {{ unsafe {{ LIB_{4}.load(); (LIB_{4}.pfn_{0})({3}) }} }} load }},",
name, result, uninit, uninit_call, api_name_upper).unwrap();
write!(init, "\n\t\t\tself.pfn_{:<40}= transmute((self.{})({}, b\"{0}\\0\".as_ptr()));",
name, load_func, null_handle).unwrap();
if feature.is_some() {
init.push_str("\n\t\t}");
}
}
}
writeln!(writer, "}}")?;
}
if let Some((inst_table, inst_init, ..)) = tables.get_mut(&Some("VkInstance")) {
inst_table.push_str("\n\tpfn_vkGetDeviceProcAddr : extern fn(VkDevice, *const u8) -> PFN_vkVoidFunction,");
inst_init.push_str("\n\t\t\tpfn_vkGetDeviceProcAddr : transmute((LIB_VULKAN.pfn_vkGetInstanceProcAddr)(handle, b\"vkGetDeviceProcAddr\\0\".as_ptr())),");
}
for (name, (table, init, _, parent)) in tables {
match name {
None => write!(writer, r#"
static mut LIB_{5}: Lib{0} = Lib{0} {{
lib: None,
pfn_{3}GetInstanceProcAddr: {{ extern fn load(handle: {7}Instance, name: *const u8) -> PFN_{3}VoidFunction {{ unsafe {{ LIB_{5}.load(); (LIB_{5}.pfn_{3}GetInstanceProcAddr)(handle, name) }} }} load }},{6}
}};
pub struct Lib{0} {{
#[allow(dead_code)]
lib: Option<libloading::Library>,
pfn_{3}GetInstanceProcAddr: extern fn({7}Instance, *const u8) -> PFN_{3}VoidFunction,{1}
}}
impl Lib{0} {{
unsafe fn load(&mut self) {{
let lib = libloading::Library::new(LIB).expect("failed to load `lib{4}`");
self.pfn_{3}GetInstanceProcAddr = *lib.get(b"{3}GetInstanceProcAddr\0").expect("failed to load `{3}GetInstanceProcAddr`");
self.lib = Some(lib);
{2}
}}
}}
"#, api_name, table, init, api_suffix_lower, api_name_lower, api_name_upper, static_uninit, api_suffix),
Some(name) if name.ends_with("Instance") => write!(writer, r#"
pub struct {0}Table {{
{1}
}}
impl {0}Table {{
fn load(handle: {0}) -> Arc<Self> {{
let table = unsafe {{ &LIB_{3} }};
Arc::new(unsafe {{ Self {{
{2}
}} }})
}}
}}
impl fmt::Debug for {0}Table {{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{
f.debug_struct("{0}Table").finish()
}}
}}
"#, name, table, init, api_name_upper),
Some(name) => writeKhrImpl!(@table writer, name, table, init, parent)
}?;
}
Ok(())
}
pub fn gen_cargo(
writer: &mut impl io::Write,
elements: impl IntoIterator<Item = (String, Vec<String>)>
) -> io::Result<()> {
let mut extensions = elements.into_iter()
.collect::<Vec<_>>();
writeln!(writer, "[dependencies]\nlog = \"0.*\"\nlibloading = \"0.*\"\n\n[features]\ndefault = [{}]", extensions.iter()
.filter(|(name, _)| name.starts_with("VK_VERSION") | name.starts_with("XR_VERSION"))
.map(|(name, _)| format!("\"{}\", ", name))
.collect::<String>()
.trim_start_matches(", "))?;
extensions.sort_by_key(|v| v.0.clone());
for (name, requires) in extensions {
use std::fmt::Write;
let mut dependencies = String::new();
for s in requires { write!(&mut dependencies, "\"{}\", ", s).unwrap(); }
writeln!(writer, "{:<50}= [{}]", name, dependencies.trim_end_matches(", "))?;
}
Ok(())
}
fn resolve_type(s: &str, types: &mut HashMap<String, Rc<RefCell<Type>>>) -> Rc<RefCell<Type>> {
Type::from_str(s, &mut |s| types
.entry(s.to_string())
.or_insert_with(|| Rc::new(RefCell::new(Type::Undefined(s.to_string()))))
.clone())
}
fn insert_type(name: String, ty: Type, types: &mut HashMap<String, Rc<RefCell<Type>>>) -> Rc<RefCell<Type>> {
match types.entry(name) {
Entry::Vacant(e) => { e.insert(Rc::new(RefCell::new(ty))).clone() },
Entry::Occupied(e) => {
*e.get().borrow_mut() = ty;
e.get().clone()
}
}
}
fn write_comment(comment: &Option<impl Deref<Target = str>>, writer: &mut impl io::Write) -> io::Result<()> {
write_comment_pre("///", comment.as_deref(), writer)
}
fn write_comment_pre(pre: &str, comment: Option<&str>, writer: &mut impl io::Write) -> io::Result<()> {
if let Some(comment) = comment {
for l in comment.lines() {
writeln!(writer, "{} {}", pre, l.trim())?;
}
}
Ok(())
}
static VK_STRINGS: [&str; 8] = [VK_BEGINNING, "Vulkan", "VULKAN", "vulkan", "VK_NULL_HANDLE", "pfn_vkGetInstanceProcAddr", "vk", "Vk"];
static XR_STRINGS: [&str; 8] = [XR_BEGINNING, "OpenXr", "OPENXR", "openxr", "XR_NULL_HANDLE", "pfn_xrGetInstanceProcAddr", "xr", "Xr"];
static SKIPPED_METHODS: &[&str] = &[
"vkGetInstanceProcAddr",
"vkGetDeviceProcAddr",
"xrGetInstanceProcAddr"
];
static PRIMITIVE_TYPES: &[&str] = &[
"()",
"usize",
"isize",
"u8",
"i8",
"u16",
"i16",
"u32",
"i32",
"u64",
"i64",
"u128",
"i128",
"f32",
"f64",
"char"
];
static VK_BEGINNING: &str = r#"//! MACHINE GENERATED FILE, DO NOT EDIT
#![feature(try_trait_v2, const_fn_transmute, stmt_expr_attributes)]
#![warn(clippy::all)]
#![allow(
non_snake_case,
non_upper_case_globals,
non_camel_case_types,
unused_attributes,
unused_parens,
invalid_value,
clippy::unused_unit,
clippy::too_many_arguments,
clippy::enum_clike_unportable_variant,
clippy::unnecessary_cast,
clippy::missing_safety_doc,
clippy::from_over_into,
clippy::upper_case_acronyms,
clippy::mixed_case_hex_literals
)]
use {
core::{mem::{transmute, MaybeUninit}, ops, fmt},
std::sync::Arc,
self::external::*
};
pub use types::*;
/// Contains some useful types
mod types {
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
pub struct VkAnyRef<'b>(&'b ());
impl<'a> VkAnyRef<'a> {
#[allow(clippy::transmute_ptr_to_ptr)]
pub const fn new<T>(v: &'a T) -> Self {
Self(unsafe { std::mem::transmute(v) })
}
}
impl<'a, T> From<&'a T> for VkAnyRef<'a> {
fn from(v: &'a T) -> Self {
unsafe { Self((v as *const T as *const ()).as_ref().unwrap()) }
}
}
impl<'a, T> Into<*const T> for VkAnyRef<'a> {
fn into(self) -> *const T {
self.0 as *const () as *const T
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct VkAnyMut<'b>(&'b mut ());
impl<'a> VkAnyMut<'a> {
pub fn new<T>(v: &'a mut T) -> Self {
Self(unsafe { &mut*(v as *mut T as *mut ()) })
}
}
impl<'a, T> From<&'a mut T> for VkAnyMut<'a> {
fn from(v: &'a mut T) -> Self {
unsafe { Self((v as *mut T as *mut ()).as_mut().unwrap()) }
}
}
impl<'a, T> Into<*mut T> for VkAnyMut<'a> {
fn into(self) -> *mut T {
self.0 as *mut () as *mut T
}
}
}
pub mod external {
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum wl_display {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum wl_surface {}
impl wl_display {
pub unsafe fn from_ptr<'a, T>(ptr: *mut T) -> Option<&'a mut Self> {
(ptr as *mut Self).as_mut()
}
}
impl wl_surface {
pub unsafe fn from_ptr<'a, T>(ptr: *mut T) -> Option<&'a mut Self> {
(ptr as *mut Self).as_mut()
}
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum xcb_connection_t {}
pub type xcb_window_t = u32;
pub type xcb_visualid_t = u32;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Display {}
pub type VisualID = u32;
pub type Window = u32;
pub type RROutput = u32;
pub type GgpStreamDescriptor = usize;
pub type GgpFrameToken = usize;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum ANativeWindow {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum AHardwareBuffer {}
pub type zx_handle_t = usize;
pub type HINSTANCE = Option<std::ptr::NonNull<()>>;
pub type HWND = Option<std::ptr::NonNull<()>>;
pub type HMONITOR = Option<std::ptr::NonNull<()>>;
pub type HANDLE = Option<std::ptr::NonNull<()>>;
pub type DWORD = u32;
pub type LPCWSTR = Option<std::ptr::NonNull<u16>>;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct SECURITY_ATTRIBUTES {
pub nLength: DWORD,
pub lpSecurityDescriptor: Option<std::ptr::NonNull<()>>,
pub bInheritHandle: i32
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum CAMetalLayer {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum IDirectFB {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum IDirectFBSurface {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum _screen_context {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum _screen_window {}
pub struct StdVideoH264ProfileIdc;
pub struct StdVideoH264SequenceParameterSet;
pub struct StdVideoH264PictureParameterSet;
pub struct StdVideoDecodeH264PictureInfo;
pub struct StdVideoDecodeH264ReferenceInfo;
pub struct StdVideoDecodeH264Mvc;
pub struct StdVideoEncodeH264PictureInfo;
pub struct StdVideoEncodeH264SliceHeader;
pub struct StdVideoH265ProfileIdc;
pub struct StdVideoH265SequenceParameterSet;
pub struct StdVideoH265PictureParameterSet;
pub struct StdVideoDecodeH265PictureInfo;
pub struct StdVideoDecodeH265ReferenceInfo;
}
pub const VK_NULL_HANDLE : u64 = 0;
/// Vulkan 1.0 version number
pub const VK_API_VERSION_1_0 : u32 = VK_MAKE_VERSION(1, 0, 0);
/// Vulkan 1.1 version number
pub const VK_API_VERSION_1_1 : u32 = VK_MAKE_VERSION(1, 1, 0);
/// Vulkan 1.1 version number
pub const VK_API_VERSION_1_2 : u32 = VK_MAKE_VERSION(1, 2, 0);
#[cfg(unix)]
const LIB: &str = "libvulkan.so.1";
#[cfg(windows)]
const LIB: &str = "vulkan-1.dll";
impl<T, E: From<VkResult>> std::ops::FromResidual<VkResult> for Result<T, E> {
fn from_residual(r: VkResult) -> Self {
Err(r.into())
}
}
impl<T, E: Into<VkResult>> std::ops::FromResidual<Result<T, E>> for VkResult {
fn from_residual(r: Result<T, E>) -> Self {
match r {
Ok(_) => VK_SUCCESS,
Err(v) => v.into()
}
}
}
impl ops::FromResidual<VkResult> for VkResult {
fn from_residual(residual: VkResult) -> Self {
residual
}
}
impl ops::Try for VkResult {
type Output = Self;
type Residual = Self;
fn from_output(output: Self::Output) -> Self {
output
}
fn branch(self) -> ops::ControlFlow<Self::Residual, Self::Output> {
if self as i32 >= 0 {
ops::ControlFlow::Continue(self)
} else {
ops::ControlFlow::Break(self)
}
}
}
impl Into<Result<Self, Self>> for VkResult {
fn into(self) -> Result<VkResult, VkResult> {
if self as i32 >= 0 {
Ok(self)
} else {
Err(self)
}
}
}
impl std::error::Error for VkResult {}
impl fmt::Display for VkResult {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
"#;
static XR_BEGINNING: &str = r#"//! MACHINE GENERATED FILE, DO NOT EDIT
#![feature(try_trait_v2, const_fn_transmute, stmt_expr_attributes)]
#![warn(clippy::all)]
#![allow(
non_snake_case,
non_upper_case_globals,
non_camel_case_types,
unused_attributes,
unused_parens,
invalid_value,
clippy::unused_unit,
clippy::too_many_arguments,
clippy::enum_clike_unportable_variant,
clippy::unnecessary_cast,
clippy::missing_safety_doc,
clippy::from_over_into,
clippy::upper_case_acronyms
)]
use {
core::{mem::{transmute, MaybeUninit}, ops, fmt},
std::sync::Arc,
self::external::*,
vk::*,
};
pub use types::*;
/// Contains some useful types
mod types {
#[derive(Copy, Clone, Debug)]
#[repr(transparent)]
pub struct XrAnyRef<'b>(&'b ());
impl<'a> XrAnyRef<'a> {
#[allow(clippy::transmute_ptr_to_ptr)]
pub const fn new<T>(v: &'a T) -> Self {
Self(unsafe { std::mem::transmute(v) })
}
}
impl<'a, T> From<&'a T> for XrAnyRef<'a> {
fn from(v: &'a T) -> Self {
unsafe { Self((v as *const T as *const ()).as_ref().unwrap()) }
}
}
impl<'a, T> Into<*const T> for XrAnyRef<'a> {
fn into(self) -> *const T {
self.0 as *const () as *const T
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct XrAnyMut<'b>(&'b mut ());
impl<'a> XrAnyMut<'a> {
pub fn new<T>(v: &'a mut T) -> Self {
Self(unsafe { &mut*(v as *mut T as *mut ()) })
}
}
impl<'a, T> From<&'a mut T> for XrAnyMut<'a> {
fn from(v: &'a mut T) -> Self {
unsafe { Self((v as *mut T as *mut ()).as_mut().unwrap()) }
}
}
impl<'a, T> Into<*mut T> for XrAnyMut<'a> {
fn into(self) -> *mut T {
self.0 as *mut () as *mut T
}
}
}
pub mod external {
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum wl_display {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum xcb_connection_t {}
pub type xcb_window_t = u32;
pub type xcb_visualid_t = u32;
pub type xcb_glx_fbconfig_t = u32;
pub type xcb_glx_drawable_t = u32;
pub type xcb_glx_context_t = u32;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Display {}
pub type GLXFBConfig = u64;
pub type GLXDrawable = u64;
pub type GLXContext = u64;
pub type EGLDisplay = u64;
pub type EGLConfig = u64;
pub type EGLContext = u64;
pub type PFNEGLGETPROCADDRESSPROC = extern fn();
pub type HDC = *mut ();
pub type HGLRC = *mut ();
pub type LUID = u64;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum ID3D11Device {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum ID3D11Texture2D {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum ID3D12Device {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum ID3D12CommandQueue {}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum ID3D12Resource {}
#[repr(C)]
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum D3D_FEATURE_LEVEL { Variant }
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum IUnknown {}
/// This typedef only exists in vulkan.h, but not in the Vulkan registry, so it is defined here.
pub type PFN_vkGetInstanceProcAddr = extern fn(vk::VkInstance, *const u8) -> vk::PFN_vkVoidFunction;
}
pub const XR_NULL_HANDLE : u64 = 0;
pub const XR_CURRENT_API_VERSION : XrVersion = XR_MAKE_VERSION(1, 0, 5);
pub const XR_NULL_PATH : XrPath = 0;
pub const XR_NO_DURATION : XrDuration = 0;
pub const XR_INFINITE_DURATION : XrDuration = 0x7fffffffffffffff;
pub const XR_MIN_HAPTIC_DURATION : XrDuration = -1;
pub const XR_FREQUENCY_UNSPECIFIED: i32 = 0;
pub const XR_MAX_EVENT_DATA_SIZE : usize = std::mem::size_of::<XrEventDataBuffer>();
#[cfg(unix)]
const LIB: &str = "libopenxr.so.1";
#[cfg(windows)]
const LIB: &str = "openxr-1.dll";
impl<T, E: From<XrResult>> std::ops::FromResidual<XrResult> for Result<T, E> {
fn from_residual(r: XrResult) -> Self {
Err(r.into())
}
}
impl<T, E: Into<XrResult>> std::ops::FromResidual<Result<T, E>> for XrResult {
fn from_residual(r: Result<T, E>) -> Self {
match r {
Ok(_) => VK_SUCCESS,
Err(v) => v.into()
}
}
}
impl ops::FromResidual<XrResult> for XrResult {
fn from_residual(residual: XrResult) -> Self {
residual
}
}
impl ops::Try for XrResult {
type Output = Self;
type Residual = Self;
fn from_output(output: Self::Output) -> Self {
output
}
fn branch(self) -> ops::ControlFlow<Self::Residual, Self::Output> {
if self as i32 >= 0 {
ops::ControlFlow::Continue(self)
} else {
ops::ControlFlow::Break(self)
}
}
}
impl Into<Result<Self, Self>> for XrResult {
fn into(self) -> Result<XrResult, XrResult> {
if self as i32 >= 0 {
Ok(self)
} else {
Err(self)
}
}
}
impl std::error::Error for XrResult {}
impl fmt::Display for XrResult {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
"#;