generator: Borrow all command names and disentangle "aliases" (#733)

Turns out we were doing the wrong thing for the right reason: the
`aliases` here aren't `vk.xml` aliases: they are renames.  When
generating function pointers for extensions, a list of command
_definitions_ is collected, which can only ever be "root" `command`s.
Extensions typically reference stabilized `command`s under an alias with
the vendor tag suffixed, which the `Fn` struct field name is renamed to
using this `aliases` - now replaced with `rename_commands` - list, while
generating the rest of the "function pointer" command bits using the
"root" `command` (as this mostly pertains the parameters and return
type).  With that explanation it becomes clear why
`generate_extension_commands()` was creating an "alias" mapping from
stabilized name to vendor-suffixed extension name, and calls
`generate_function_pointers()` with that mapping - and a list of
stabilized/root `command`s - rather than passing `cmd_aliases` directly.
(This `cmd_aliases` list exists because the rename always happens in the
root `<commands>` element: extensions then `<require>` the aliased
rather than the stabilized name, so the base for this alias is found
first to look up the base command, and then stored in `rename_commands`
to rename it back to the aliased name).

With improved clarity we can now also borrow the name strings rather
than cloning them in many places.
This commit is contained in:
Marijn Suijten 2023-04-05 23:42:21 +02:00 committed by GitHub
parent 77e614d776
commit 5732d977cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -893,7 +893,7 @@ pub type CommandMap<'a> = HashMap<vkxml::Identifier, &'a vk_parse::CommandDefini
fn generate_function_pointers<'a>( fn generate_function_pointers<'a>(
ident: Ident, ident: Ident,
commands: &[&'a vk_parse::CommandDefinition], commands: &[&'a vk_parse::CommandDefinition],
aliases: &HashMap<String, String>, rename_commands: &HashMap<&'a str, &'a str>,
fn_cache: &mut HashSet<&'a str>, fn_cache: &mut HashSet<&'a str>,
) -> TokenStream { ) -> TokenStream {
// Commands can have duplicates inside them because they are declared per features. But we only // Commands can have duplicates inside them because they are declared per features. But we only
@ -903,10 +903,10 @@ fn generate_function_pointers<'a>(
.unique_by(|cmd| cmd.proto.name.as_str()) .unique_by(|cmd| cmd.proto.name.as_str())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
struct Command { struct Command<'a> {
type_needs_defining: bool, type_needs_defining: bool,
type_name: Ident, type_name: Ident,
function_name_c: String, function_name_c: &'a str,
function_name_rust: Ident, function_name_rust: Ident,
parameters: TokenStream, parameters: TokenStream,
parameters_unused: TokenStream, parameters_unused: TokenStream,
@ -919,11 +919,10 @@ fn generate_function_pointers<'a>(
let name = &cmd.proto.name; let name = &cmd.proto.name;
let type_name = format_ident!("PFN_{}", name); let type_name = format_ident!("PFN_{}", name);
let function_name_c = if let Some(alias_name) = aliases.get(name) { // We might need to generate a function pointer for an extension, where we are given the original
alias_name.to_string() // `cmd` and a rename back to the extension alias (typically with vendor suffix) in `rename_commands`:
} else { let function_name_c = rename_commands.get(name.as_str()).cloned().unwrap_or(name);
name.to_string()
};
let function_name_rust = format_ident!( let function_name_rust = format_ident!(
"{}", "{}",
function_name_c.strip_prefix("vk").unwrap().to_snake_case() function_name_c.strip_prefix("vk").unwrap().to_snake_case()
@ -976,7 +975,7 @@ fn generate_function_pointers<'a>(
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
struct CommandToType<'a>(&'a Command); struct CommandToType<'a>(&'a Command<'a>);
impl<'a> quote::ToTokens for CommandToType<'a> { impl<'a> quote::ToTokens for CommandToType<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let type_name = &self.0.type_name; let type_name = &self.0.type_name;
@ -990,7 +989,7 @@ fn generate_function_pointers<'a>(
} }
} }
struct CommandToMember<'a>(&'a Command); struct CommandToMember<'a>(&'a Command<'a>);
impl<'a> quote::ToTokens for CommandToMember<'a> { impl<'a> quote::ToTokens for CommandToMember<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let type_name = &self.0.type_name; let type_name = &self.0.type_name;
@ -1006,7 +1005,7 @@ fn generate_function_pointers<'a>(
} }
} }
struct CommandToLoader<'a>(&'a Command); struct CommandToLoader<'a>(&'a Command<'a>);
impl<'a> quote::ToTokens for CommandToLoader<'a> { impl<'a> quote::ToTokens for CommandToLoader<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let function_name_rust = &self.0.function_name_rust; let function_name_rust = &self.0.function_name_rust;
@ -1163,13 +1162,13 @@ pub fn generate_extension_constants<'a>(
} }
pub fn generate_extension_commands<'a>( pub fn generate_extension_commands<'a>(
extension_name: &str, extension_name: &str,
items: &[vk_parse::ExtensionChild], items: &'a [vk_parse::ExtensionChild],
cmd_map: &CommandMap<'a>, cmd_map: &CommandMap<'a>,
cmd_aliases: &HashMap<String, String>, cmd_aliases: &HashMap<&'a str, &'a str>,
fn_cache: &mut HashSet<&'a str>, fn_cache: &mut HashSet<&'a str>,
) -> TokenStream { ) -> TokenStream {
let mut commands = Vec::new(); let mut commands = Vec::new();
let mut aliases = HashMap::new(); let mut rename_commands = HashMap::new();
let names = items let names = items
.iter() .iter()
.filter_map(get_variant!(vk_parse::ExtensionChild::Require { .filter_map(get_variant!(vk_parse::ExtensionChild::Require {
@ -1179,14 +1178,18 @@ pub fn generate_extension_commands<'a>(
.filter(|(api, _items)| matches!(api.as_deref(), None | Some(DESIRED_API))) .filter(|(api, _items)| matches!(api.as_deref(), None | Some(DESIRED_API)))
.flat_map(|(_api, items)| items) .flat_map(|(_api, items)| items)
.filter_map(get_variant!(vk_parse::InterfaceItem::Command { name })); .filter_map(get_variant!(vk_parse::InterfaceItem::Command { name }));
// Collect a subset of `CommandDefinition`s to generate
for name in names { for name in names {
if let Some(cmd) = cmd_map.get(name).copied() { let mut name = name.as_str();
commands.push(cmd); if let Some(&cmd) = cmd_aliases.get(name) {
} else if let Some(cmd) = cmd_aliases.get(name) { // This extension is referencing the base command under a different name,
aliases.insert(cmd.clone(), name.to_string()); // make sure it is generated with a rename to it.
let cmd = cmd_map.get(cmd).copied().unwrap(); rename_commands.insert(cmd, name);
commands.push(cmd); name = cmd;
} }
commands.push(cmd_map[name]);
} }
let ident = format_ident!( let ident = format_ident!(
@ -1196,7 +1199,7 @@ pub fn generate_extension_commands<'a>(
.strip_prefix("Vk") .strip_prefix("Vk")
.unwrap() .unwrap()
); );
let fp = generate_function_pointers(ident.clone(), &commands, &aliases, fn_cache); let fp = generate_function_pointers(ident.clone(), &commands, &rename_commands, fn_cache);
let spec_version = items let spec_version = items
.iter() .iter()
@ -1232,7 +1235,7 @@ pub fn generate_extension<'a>(
cmd_map: &CommandMap<'a>, cmd_map: &CommandMap<'a>,
const_cache: &mut HashSet<&'a str>, const_cache: &mut HashSet<&'a str>,
const_values: &mut BTreeMap<Ident, ConstantTypeInfo>, const_values: &mut BTreeMap<Ident, ConstantTypeInfo>,
cmd_aliases: &HashMap<String, String>, cmd_aliases: &HashMap<&'a str, &'a str>,
fn_cache: &mut HashSet<&'a str>, fn_cache: &mut HashSet<&'a str>,
) -> Option<TokenStream> { ) -> Option<TokenStream> {
let extension_tokens = generate_extension_constants( let extension_tokens = generate_extension_constants(
@ -2776,14 +2779,14 @@ pub fn write_source_code<P: AsRef<Path>>(vk_headers_dir: &Path, src_dir: P) {
.map(|cmd| (cmd.proto.name.clone(), cmd)) .map(|cmd| (cmd.proto.name.clone(), cmd))
.collect(); .collect();
let cmd_aliases: HashMap<String, String> = spec2 let cmd_aliases: HashMap<_, _> = spec2
.0 .0
.iter() .iter()
.filter_map(get_variant!(vk_parse::RegistryChild::Commands)) .filter_map(get_variant!(vk_parse::RegistryChild::Commands))
.flat_map(|cmds| &cmds.children) .flat_map(|cmds| &cmds.children)
.filter_map(get_variant!(vk_parse::Command::Alias { name, alias })) .filter_map(get_variant!(vk_parse::Command::Alias { name, alias }))
.filter(|(name, _alias)| required_commands.contains(name.as_str())) .filter(|(name, _alias)| required_commands.contains(name.as_str()))
.map(|(name, alias)| (name.to_string(), alias.to_string())) .map(|(name, alias)| (name.as_str(), alias.as_str()))
.collect(); .collect();
let mut fn_cache = HashSet::new(); let mut fn_cache = HashSet::new();