alex/volcanic
Automated vulkan bindings
BRANCH main
SNAPSHOT Improved loading api and made ready for publish
DOWNLOAD
root codegen src emit types.rs
use super::*;
pub(super) fn emit_type_aliases(
out: &mut String,
ctx: &mut Context,
registry: &Registry,
is_vk: bool,
names: &IndexMap<String, select::FeatureExpr>,
) {
let section = section(out, if is_vk { "Vulkan types" } else { "Video types" });
for ty in ®istry.types {
if !names.contains_key(&ty.name)
|| !select::api_matches(ty.api.as_deref())
|| is_native(&ty.name)
{
continue;
}
if is_vk && ctx.video_type_names.contains(&ty.name) {
continue;
}
if let Some(alias) = &ty.alias {
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = {alias};\n", ty.name),
);
continue;
}
match ty.category.as_str() {
"include" | "define" => {}
"none" => {
if !is_primitive(&ty.name)
&& !ty
.requires
.as_deref()
.is_some_and(|r| r.starts_with("vk_video/"))
{
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = c_void;\n", ty.name),
);
}
}
"basetype" => emit_basetype(out, ctx, ty),
"bitmask" => {
if is_vk {
emit_bitflags_type(out, ctx, registry, ty);
} else {
let base = ty.underlying_type.as_deref().unwrap_or("VkFlags");
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = {};\n", ty.name, rust_base(base)),
);
}
}
"handle" => emit_handle(out, ctx, ty),
"enum" => {
if is_vk && let Some(flags_type) = ctx.bit_enums_to_flags.get(&ty.name).cloned() {
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = {flags_type};\n", ty.name),
);
} else if ctx.rust_enums.contains_key(&ty.name) {
emit_rust_enum(out, ctx, &ty.name);
} else {
let rust_ty = ctx.enum_widths.get(&ty.name).copied().unwrap_or("i32");
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = {rust_ty};\n", ty.name),
);
}
}
"struct" | "union" | "funcpointer" => {}
other => panic!("unsupported type category {other} for {}", ty.name),
}
}
section.finish(out);
}
pub(super) fn emit_rust_enum(out: &mut String, ctx: &mut Context, name: &str) {
if !ctx.emitted_types.insert(name.to_string()) {
return;
}
let info = ctx
.rust_enums
.get(name)
.unwrap_or_else(|| panic!("unknown Rust enum {name}"));
emit_cfg(out, ctx.type_condition(name), "");
out.push_str("#[repr(transparent)]\n");
out.push_str("#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n");
out.push_str(&format!("pub struct {name}(i32);\n"));
emit_cfg(out, ctx.type_condition(name), "");
out.push_str(&format!("impl {name} {{\n"));
out.push_str(" pub const fn from_raw(value: i32) -> Self { Self(value) }\n");
out.push_str(" pub const fn as_raw(self) -> i32 { self.0 }\n");
for (constant_name, constant) in &info.constants {
emit_nested_cfg(out, &constant.condition, ctx.type_condition(name), " ");
out.push_str(&format!(
" pub const {}: Self = Self({});\n",
constant_name, constant.value
));
}
out.push_str("}\n");
}
pub(super) fn emit_basetype(out: &mut String, ctx: &mut Context, ty: &TypeDef) {
if let Some(decl) = &ty.underlying_decl {
let rust_ty = rust_decl_type(decl, false);
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = {rust_ty};\n", ty.name),
);
} else if let Some(base) = &ty.underlying_type {
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = {};\n", ty.name, rust_base(base)),
);
} else {
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = c_void;\n", ty.name),
);
}
}
pub(super) fn emit_bitflags_type(
out: &mut String,
ctx: &mut Context,
registry: &Registry,
ty: &TypeDef,
) {
let Some(info) = ctx.bitmasks.get(&ty.name).cloned() else {
let base = ty.underlying_type.as_deref().unwrap_or("VkFlags");
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = {};\n", ty.name, rust_base(base)),
);
return;
};
if !ctx.emitted_types.insert(ty.name.clone()) {
return;
}
emit_cfg(out, ctx.type_condition(&ty.name), "");
out.push_str("crate::bitflags::bitflags! {\n");
out.push_str(" #[repr(transparent)]\n");
out.push_str(" #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n");
out.push_str(&format!(
" pub struct {}: {} {{\n",
ty.name, info.bits_ty
));
if let Some(bits_name) = &info.bits_name
&& let Some(group) = registry
.enum_groups
.iter()
.find(|group| group.name.as_deref() == Some(bits_name.as_str()))
{
for entry in &group.entries {
if !select::api_matches(entry.api.as_deref()) {
continue;
}
let assoc_name = ctx
.flag_constants
.get(&entry.name)
.map(|constant| constant.assoc_name.as_str())
.unwrap_or_else(|| panic!("unknown flag constant {}", entry.name));
let value = bitflag_bits_value(ctx, entry, Some(bits_name), None, Some(&ty.name));
emit_nested_cfg(
out,
ctx.enum_condition(&entry.name),
ctx.type_condition(&ty.name),
" ",
);
out.push_str(&format!(" const {assoc_name} = {value};\n"));
}
}
out.push_str(" const _ = !0;\n");
out.push_str(" }\n");
out.push_str("}\n");
}
pub(super) fn emit_handle(out: &mut String, ctx: &mut Context, ty: &TypeDef) {
if ty
.handle_macro
.as_deref()
.is_some_and(|name| name == "VK_DEFINE_NON_DISPATCHABLE_HANDLE")
{
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = u64;\n", ty.name),
);
} else {
let tag = format!("{}_T", ty.name);
emit_type(
out,
ctx,
&tag,
&format!("#[repr(C)]\npub struct {tag} {{ _private: [u8; 0] }}\n"),
);
emit_type(
out,
ctx,
&ty.name,
&format!("pub type {} = *mut {tag};\n", ty.name),
);
}
}
pub(super) fn emit_enum_constants(
out: &mut String,
ctx: &mut Context,
enums: &IndexMap<String, ResolvedEnum>,
) {
let section = section(out, "Constants");
for required in enums.values() {
if ctx.flag_constants.contains_key(&required.entry.name) {
emit_bitflag_extension_entry(
out,
ctx,
&required.entry,
required.group_name.as_deref(),
required.extension_number,
);
} else {
emit_enum_entry(
out,
ctx,
&required.entry,
required.group_name.as_deref(),
required.extension_number,
);
}
}
section.finish(out);
}
pub(super) fn emit_enum_entry(
out: &mut String,
ctx: &mut Context,
entry: &EnumEntry,
group_name: Option<&str>,
extension_number: Option<u32>,
) {
if ctx.emitted_consts.contains(&entry.name) || !select::api_matches(entry.api.as_deref()) {
return;
}
if group_name.is_some_and(|name| ctx.rust_enums.contains_key(name)) {
return;
}
let const_ty = const_type(ctx, entry, group_name);
let value = const_value(ctx, entry, group_name, extension_number);
emit_const(out, ctx, &entry.name, &const_ty, &value);
}
pub(super) fn emit_bitflag_extension_entry(
out: &mut String,
ctx: &mut Context,
entry: &EnumEntry,
group_name: Option<&str>,
extension_number: Option<u32>,
) {
if ctx.bitflag_macro_entries.contains(&entry.name) || !select::api_matches(entry.api.as_deref())
{
return;
}
let constant = ctx
.flag_constants
.get(&entry.name)
.cloned()
.unwrap_or_else(|| panic!("unknown flag constant {}", entry.name));
let key = format!("{}::{}", constant.flags_type, constant.assoc_name);
if !ctx.emitted_flag_consts.insert(key) {
return;
}
let value = bitflag_bits_value(
ctx,
entry,
group_name,
extension_number,
Some(&constant.flags_type),
);
emit_cfg(out, ctx.enum_condition(&entry.name), "");
out.push_str(&format!("impl {} {{\n", constant.flags_type));
out.push_str(&format!(
" pub const {}: Self = Self::from_bits_retain({value});\n",
constant.assoc_name
));
out.push_str("}\n");
}
pub(super) fn emit_records(
out: &mut String,
ctx: &mut Context,
registry: &Registry,
is_vk: bool,
names: &IndexMap<String, select::FeatureExpr>,
) {
let section = section(
out,
if is_vk {
"Vulkan records"
} else {
"Video records"
},
);
for ty in ®istry.types {
if !names.contains_key(&ty.name)
|| !select::api_matches(ty.api.as_deref())
|| ty.alias.is_some()
{
continue;
}
if !matches!(ty.category.as_str(), "struct" | "union") {
continue;
}
if is_vk && ctx.video_type_names.contains(&ty.name) {
continue;
}
if ctx.emitted_types.contains(&ty.name) {
continue;
}
emit_cfg(out, ctx.type_condition(&ty.name), "");
out.push_str("#[repr(C)]\n#[derive(Copy, Clone)]\n");
out.push_str(&format!("pub {} {} {{\n", ty.category, ty.name));
emit_fields(out, ty);
out.push_str("}\n");
ctx.emitted_types.insert(ty.name.clone());
}
section.finish(out);
}
pub(super) fn emit_fields(out: &mut String, ty: &TypeDef) {
let mut bitfield_index = 0usize;
let mut bitfield_width = 0u32;
for member in &ty.members {
if !select::api_matches(member.api.as_deref()) {
continue;
}
if let Some(width) = member.bit_width {
if bitfield_width + width > 32 {
bitfield_index += 1;
out.push_str(&format!(" pub _bitfield_{bitfield_index}: u32,\n"));
bitfield_width = 0;
}
bitfield_width += width;
continue;
}
if bitfield_width > 0 {
bitfield_index += 1;
out.push_str(&format!(" pub _bitfield_{bitfield_index}: u32,\n"));
bitfield_width = 0;
}
out.push_str(&format!(
" pub {}: {},\n",
rust_ident(&member.name),
rust_decl_type(member, false)
));
}
if bitfield_width > 0 {
bitfield_index += 1;
out.push_str(&format!(" pub _bitfield_{bitfield_index}: u32,\n"));
}
}
pub(super) fn emit_func_pointers(
out: &mut String,
ctx: &mut Context,
registry: &Registry,
names: &IndexMap<String, select::FeatureExpr>,
) {
let section = section(out, "Callback function pointers");
for ty in ®istry.types {
if !names.contains_key(&ty.name)
|| ty.category != "funcpointer"
|| !select::api_matches(ty.api.as_deref())
{
continue;
}
if ty.name == "PFN_vkVoidFunction" {
emit_type(
out,
ctx,
&ty.name,
"pub type PFN_vkVoidFunction = Option<unsafe extern \"system\" fn() -> ()>;\n",
);
continue;
}
let func = ty.func.as_ref().unwrap();
let ret = rust_decl_type(&func.proto, true);
let params = params(&func.params);
emit_type(
out,
ctx,
&ty.name,
&format!(
"pub type {} = Option<unsafe extern \"system\" fn({params}) -> {ret}>;\n",
ty.name
),
);
}
section.finish(out);
}
pub(super) fn emit_command_pointers(
out: &mut String,
ctx: &mut Context,
vk: &Registry,
selected_commands: &IndexMap<String, select::FeatureExpr>,
) {
let section = section(out, "Vulkan command function pointers");
let commands: IndexMap<_, _> = vk
.commands
.iter()
.map(|command| (command.name.as_str(), command))
.collect();
for command in &vk.commands {
if !selected_commands.contains_key(&command.name)
|| !select::api_matches(command.api.as_deref())
{
continue;
}
let signature = resolve_command_signature(command, &commands);
let ret = rust_decl_type(signature.proto.as_ref().unwrap(), true);
let params = params(&signature.params);
let pfn = format!("PFN_{}", command.name);
emit_type(
out,
ctx,
&pfn,
&format!("pub type {pfn} = unsafe extern \"system\" fn({params}) -> {ret};\n"),
);
}
section.finish(out);
}
pub(super) fn resolve_command_signature<'a>(
command: &'a Command,
commands: &'a IndexMap<&str, &'a Command>,
) -> &'a Command {
if let Some(alias) = &command.alias {
commands
.get(alias.as_str())
.copied()
.unwrap_or_else(|| panic!("unknown command alias target {alias}"))
} else {
command
}
}