Skip to content

Commit 259c211

Browse files
committed
Cache utility function pointers
1 parent a06a333 commit 259c211

File tree

7 files changed

+118
-41
lines changed

7 files changed

+118
-41
lines changed

godot-codegen/src/central_generator.rs

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,59 @@ pub(crate) fn generate_sys_classes_file(
116116
}
117117
}
118118

119+
pub(crate) fn generate_sys_utilities_file(
120+
api: &ExtensionApi,
121+
ctx: &mut Context,
122+
sys_gen_path: &Path,
123+
submit_fn: &mut SubmitFn,
124+
) {
125+
let mut table = MethodTableInfo {
126+
table_name: ident("UtilityFunctionTable"),
127+
imports: quote! {},
128+
ctor_parameters: quote! {
129+
interface: &crate::GDExtensionInterface,
130+
string_names: &mut crate::StringCache,
131+
},
132+
pre_init_code: quote! {
133+
let get_utility_fn = interface.variant_get_ptr_utility_function
134+
.expect("variant_get_ptr_utility_function absent");
135+
},
136+
method_decls: vec![],
137+
method_inits: vec![],
138+
class_count: 0,
139+
method_count: 0,
140+
};
141+
142+
for function in api.utility_functions.iter() {
143+
if codegen_special_cases::is_function_excluded(function, ctx) {
144+
continue;
145+
}
146+
147+
let fn_name_str = &function.name;
148+
let field = util::make_utility_function_ptr_name(&function);
149+
let hash = function.hash;
150+
151+
table.method_decls.push(quote! {
152+
pub #field: crate::UtilityFunctionBind,
153+
});
154+
155+
table.method_inits.push(quote! {
156+
#field: {
157+
let utility_fn = unsafe {
158+
get_utility_fn(string_names.fetch(#fn_name_str), #hash)
159+
};
160+
crate::validate_utility_function(utility_fn, #fn_name_str, #hash)
161+
},
162+
});
163+
164+
table.method_count += 1;
165+
}
166+
167+
let code = make_method_table(table);
168+
169+
submit_fn(sys_gen_path.join("table_utilities.rs"), code);
170+
}
171+
119172
/// Generate code for a method table based on shared layout.
120173
fn make_method_table(info: MethodTableInfo) -> TokenStream {
121174
let MethodTableInfo {
@@ -635,10 +688,11 @@ fn populate_class_methods(
635688
continue;
636689
}
637690

638-
let method_field = util::make_class_method_ptr_name(&class.name, method);
691+
let field = util::make_class_method_ptr_name(&class.name, method);
639692

640-
let method_decl = make_class_method_decl(&method_field);
641-
let method_init = make_class_method_init(method, &method_field, class_var, class_name_str);
693+
// Note: varcall/ptrcall is only decided at call time; the method bind is the same for both.
694+
let method_decl = quote! { pub #field: crate::ClassMethodBind, };
695+
let method_init = make_class_method_init(method, &field, class_var, class_name_str);
642696

643697
table.method_decls.push(method_decl);
644698
table.method_inits.push(method_init);
@@ -656,25 +710,20 @@ fn populate_builtin_methods(
656710
continue;
657711
}
658712

659-
let method_field = util::make_builtin_method_ptr_name(type_name, method);
713+
let field = util::make_builtin_method_ptr_name(type_name, method);
660714

661-
let method_decl = make_builtin_method_decl(method, &method_field);
662-
let method_init = make_builtin_method_init(method, &method_field, type_name);
715+
let method_decl = quote! { pub #field: crate::BuiltinMethodBind, };
716+
let method_init = make_builtin_method_init(method, &field, type_name);
663717

664718
table.method_decls.push(method_decl);
665719
table.method_inits.push(method_init);
666720
table.method_count += 1;
667721
}
668722
}
669723

670-
fn make_class_method_decl(method_field: &Ident) -> TokenStream {
671-
// Note: varcall/ptrcall is only decided at call time; the method bind is the same for both.
672-
quote! { pub #method_field: crate::ClassMethodBind, }
673-
}
674-
675724
fn make_class_method_init(
676725
method: &ClassMethod,
677-
method_field: &Ident,
726+
field: &Ident,
678727
class_var: &Ident,
679728
class_name_str: &str,
680729
) -> TokenStream {
@@ -689,7 +738,7 @@ fn make_class_method_init(
689738
});
690739

691740
quote! {
692-
#method_field: {
741+
#field: {
693742
let method_bind = unsafe {
694743
get_method_bind(#class_var, #method_sname, #hash)
695744
};
@@ -698,13 +747,9 @@ fn make_class_method_init(
698747
}
699748
}
700749

701-
fn make_builtin_method_decl(_method: &BuiltinClassMethod, method_field: &Ident) -> TokenStream {
702-
quote! { pub #method_field: crate::BuiltinMethodBind, }
703-
}
704-
705750
fn make_builtin_method_init(
706751
method: &BuiltinClassMethod,
707-
method_field: &Ident,
752+
field: &Ident,
708753
type_name: &TypeNames,
709754
) -> TokenStream {
710755
let method_name_str = method.name.as_str();
@@ -721,7 +766,7 @@ fn make_builtin_method_init(
721766
});
722767

723768
quote! {
724-
#method_field: {
769+
#field: {
725770
let method_bind = unsafe {
726771
get_builtin_method(sys::#variant_type, #method_sname, #hash)
727772
};

godot-codegen/src/class_generator.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,18 +1210,15 @@ pub(crate) fn make_utility_function_definition(
12101210
}
12111211

12121212
let function_name_str = &function.name;
1213-
let function_name_stringname = make_string_name(function_name_str);
1213+
let fn_ptr = util::make_utility_function_ptr_name(&function);
12141214

12151215
let return_value = function
12161216
.return_type
12171217
.as_deref()
12181218
.map(MethodReturn::from_type_no_meta);
1219-
let hash = function.hash;
12201219
let variant_ffi = function.is_vararg.then_some(VariantFfi::type_ptr());
12211220
let init_code = quote! {
1222-
let __function_name = #function_name_stringname;
1223-
let __call_fn = sys::interface_fn!(variant_get_ptr_utility_function)(__function_name.string_sys(), #hash);
1224-
let __call_fn = __call_fn.unwrap_unchecked();
1221+
let __call_fn = sys::utility_function_table().#fn_ptr;
12251222
};
12261223
let invocation = quote! {
12271224
__call_fn(return_ptr, __args_ptr, __args.len() as i32);

godot-codegen/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ use util::{ident, to_pascal_case, to_snake_case};
3131
use utilities_generator::generate_utilities_file;
3232

3333
use crate::central_generator::{
34-
generate_sys_builtin_lifecycle_file, generate_sys_builtin_methods_file, BuiltinTypeMap,
34+
generate_sys_builtin_lifecycle_file, generate_sys_builtin_methods_file,
35+
generate_sys_utilities_file, BuiltinTypeMap,
3536
};
3637
use crate::context::NotificationEnum;
3738
use proc_macro2::{Ident, TokenStream};
@@ -80,6 +81,9 @@ pub fn generate_sys_files(
8081
generate_sys_classes_file(&api, &mut ctx, sys_gen_path, watch, &mut submit_fn);
8182
// watch records inside the function.
8283

84+
generate_sys_utilities_file(&api, &mut ctx, sys_gen_path, &mut submit_fn);
85+
watch.record("generate_utilities_file");
86+
8387
let is_godot_4_0 = api.header.version_major == 4 && api.header.version_minor == 0;
8488
generate_sys_interface_file(h_path, sys_gen_path, is_godot_4_0, &mut submit_fn);
8589
watch.record("generate_interface_file");

godot-codegen/src/util.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7-
use crate::api_parser::{BuiltinClassMethod, Class, ClassConstant, ClassMethod, Enum};
7+
use crate::api_parser::{
8+
BuiltinClassMethod, Class, ClassConstant, ClassMethod, Enum, UtilityFunction,
9+
};
810
use crate::central_generator::TypeNames;
911
use crate::special_cases::is_builtin_scalar;
1012
use crate::{Context, GodotTy, ModName, RustTy, TyName};
@@ -85,6 +87,10 @@ pub fn make_builtin_method_ptr_name(
8587
format_ident!("{}__{}", variant_type.json_builtin_name, method.name)
8688
}
8789

90+
pub fn make_utility_function_ptr_name(function: &UtilityFunction) -> Ident {
91+
safe_ident(&function.name)
92+
}
93+
8894
// TODO should eventually be removed, as all StringNames are cached
8995
pub fn make_string_name(identifier: &str) -> TokenStream {
9096
quote! {

godot-ffi/src/godot_ffi.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
55
*/
66

7-
use crate::{self as sys, ptr_then};
7+
use crate as sys;
88
use std::{error::Error, fmt::Debug, marker::PhantomData, ptr};
9+
use sys::ptr_then;
910

1011
/// Adds methods to convert from and to Godot FFI pointers.
1112
/// See [crate::ffi_methods] for ergonomic implementation.

godot-ffi/src/lib.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ pub(crate) mod gen {
2323
pub mod table_servers_classes;
2424
pub mod table_scene_classes;
2525
pub mod table_editor_classes;
26+
pub mod table_utilities;
2627

2728
pub mod central;
2829
pub mod gdextension_interface;
2930
pub mod interface;
30-
3131
}
3232

3333
mod compat;
@@ -58,6 +58,7 @@ pub use gen::table_builtins_lifecycle::*;
5858
pub use gen::table_editor_classes::*;
5959
pub use gen::table_scene_classes::*;
6060
pub use gen::table_servers_classes::*;
61+
pub use gen::table_utilities::*;
6162

6263
// Other
6364
pub use gdextension_plus::*;
@@ -85,6 +86,7 @@ struct GodotBinding {
8586
class_scene_method_table: Option<ClassSceneMethodTable>, // late-init
8687
class_editor_method_table: Option<ClassEditorMethodTable>, // late-init
8788
builtin_method_table: BuiltinMethodTable,
89+
utility_function_table: UtilityFunctionTable,
8890
runtime_metadata: GdextRuntimeMetadata,
8991
config: GdextConfig,
9092
}
@@ -140,6 +142,9 @@ pub unsafe fn initialize(
140142
let builtin_method_table = BuiltinMethodTable::load(&interface, &mut string_names);
141143
out!("Loaded builtin method table.");
142144

145+
let utility_function_table = UtilityFunctionTable::load(&interface, &mut string_names);
146+
out!("Loaded utility function table.");
147+
143148
let runtime_metadata = GdextRuntimeMetadata {
144149
godot_version: version,
145150
};
@@ -149,10 +154,11 @@ pub unsafe fn initialize(
149154
BINDING = Some(GodotBinding {
150155
interface,
151156
global_method_table,
152-
builtin_method_table,
153157
class_server_method_table: None,
154158
class_scene_method_table: None,
155159
class_editor_method_table: None,
160+
builtin_method_table,
161+
utility_function_table,
156162
library,
157163
runtime_metadata,
158164
config,
@@ -248,6 +254,14 @@ pub unsafe fn builtin_method_table() -> &'static BuiltinMethodTable {
248254
&unwrap_ref_unchecked(&BINDING).builtin_method_table
249255
}
250256

257+
/// # Safety
258+
///
259+
/// The interface must have been initialised with [`initialize`] before calling this function.
260+
#[inline(always)]
261+
pub unsafe fn utility_function_table() -> &'static UtilityFunctionTable {
262+
&unwrap_ref_unchecked(&BINDING).utility_function_table
263+
}
264+
251265
/// # Safety
252266
///
253267
/// The interface must have been initialised with [`initialize`] before calling this function.

godot-ffi/src/toolbox.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,12 @@ pub(crate) type BuiltinMethodBind = unsafe extern "C" fn(
218218
p_argument_count: std::os::raw::c_int,
219219
);
220220

221+
pub(crate) type UtilityFunctionBind = unsafe extern "C" fn(
222+
r_return: sys::GDExtensionTypePtr,
223+
p_args: *const sys::GDExtensionConstTypePtr,
224+
p_argument_count: std::os::raw::c_int,
225+
);
226+
221227
pub(crate) fn validate_builtin_method(
222228
method: sys::GDExtensionPtrBuiltInMethod,
223229
variant_type: &str,
@@ -231,20 +237,24 @@ pub(crate) fn validate_builtin_method(
231237
hash
232238
);*/
233239
method.unwrap_or_else(|| {
234-
panic!(
235-
"Failed to load builtin method {}::{} (hash {}).\n\
236-
Make sure gdext and Godot are compatible: https://godot-rust.github.io/book/gdext/advanced/compatibility.html",
237-
variant_type,
238-
method_name,
239-
hash
240-
)
240+
panic!("Failed to load builtin method {variant_type}::{method_name} (hash {hash}).{INFO}")
241241
})
242242
}
243243

244244
pub(crate) fn validate_builtin_lifecycle<T>(function: Option<T>, description: &str) -> T {
245-
function.unwrap_or_else(|| panic!(
246-
"Failed to load builtin lifecycle function {}.\n\
247-
Make sure gdext and Godot are compatible: https://godot-rust.github.io/book/gdext/advanced/compatibility.html",
248-
description
249-
))
245+
function.unwrap_or_else(|| {
246+
panic!("Failed to load builtin lifecycle function {description}.{INFO}",)
247+
})
248+
}
249+
250+
pub(crate) fn validate_utility_function(
251+
utility_fn: sys::GDExtensionPtrUtilityFunction,
252+
name: &str,
253+
hash: i64,
254+
) -> UtilityFunctionBind {
255+
utility_fn.unwrap_or_else(|| {
256+
panic!("Failed to load builtin lifecycle function {name} (hash {hash}).{INFO}")
257+
})
250258
}
259+
260+
const INFO: &'static str = "\nMake sure gdext and Godot are compatible: https://godot-rust.github.io/book/gdext/advanced/compatibility.html";

0 commit comments

Comments
 (0)