Skip to content

Commit f10b178

Browse files
authored
Try #973:
2 parents 599feda + cd3e140 commit f10b178

File tree

12 files changed

+110
-10
lines changed

12 files changed

+110
-10
lines changed

.github/workflows/full-ci.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,19 +273,34 @@ jobs:
273273
- rust: stable
274274
godot: "3.5.1-stable"
275275
postfix: ''
276+
- rust: stable
277+
godot: "3.5.1-stable"
278+
postfix: ' (ptrcall)'
279+
build_args: '--features ptrcall'
276280
- rust: nightly
277281
godot: "3.5.1-stable"
278282
postfix: ' (nightly)'
283+
- rust: nightly
284+
godot: "3.5.1-stable"
285+
postfix: ' (nightly, ptrcall)'
286+
build_args: '--features ptrcall'
279287
- rust: '1.63'
280288
godot: "3.5.1-stable"
281289
postfix: ' (msrv 1.63)'
290+
- rust: '1.63'
291+
godot: "3.5.1-stable"
292+
postfix: ' (msrv 1.63, ptrcall)'
293+
build_args: '--features ptrcall'
282294

283295
# Test with oldest supported engine version
284296
- rust: stable
285297
godot: "3.2-stable"
286298
postfix: ''
287299
build_args: '--features custom-godot'
288-
300+
- rust: stable
301+
godot: "3.2-stable"
302+
postfix: ' (ptrcall)'
303+
build_args: '--features custom-godot,ptrcall'
289304
runs-on: ubuntu-latest
290305
steps:
291306
- uses: actions/checkout@v3

bindings-generator/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ rust-version = "1.63"
1313

1414
[features]
1515
debug = []
16+
ptrcall = []
1617
custom-godot = ["which"]
1718

1819
[dependencies]

bindings-generator/src/api.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,48 @@ impl Ty {
612612
}
613613
}
614614
}
615+
616+
pub fn to_return_post_variant(&self) -> TokenStream {
617+
match self {
618+
Ty::Void => Default::default(),
619+
Ty::F64 | Ty::I64 | Ty::Bool => {
620+
let rust_type = self.to_rust();
621+
quote! {
622+
<#rust_type>::coerce_from_variant(&ret)
623+
}
624+
}
625+
626+
// The `sys` type aliases here can point to different types depending on the platform.
627+
// Do not simplify to (Linux) primitive types.
628+
Ty::Result => {
629+
quote! { GodotError::result_from_sys(sys::godot_error::from_variant(&ret).expect("Unexpected variant type")) }
630+
}
631+
Ty::VariantType => {
632+
quote! { VariantType::from_sys(sys::godot_variant_type::from_variant(&ret).expect("Unexpected variant type")) }
633+
}
634+
Ty::VariantOperator => {
635+
quote! {
636+
VariantOperator::try_from_sys(sys::godot_variant_operator::from_variant(&ret).expect("Unexpected variant type"))
637+
.expect("enum variant should be valid")
638+
}
639+
}
640+
641+
Ty::Vector3Axis => {
642+
quote! {
643+
unsafe {
644+
mem::transmute::<u32, Axis>(u32::from_variant(&ret).expect("Unexpected variant type") as _)
645+
}
646+
}
647+
}
648+
_ => {
649+
let rust_type = self.to_rust();
650+
// always a variant returned, use FromVariant
651+
quote! {
652+
<#rust_type>::from_variant(&ret).expect("Unexpected variant type")
653+
}
654+
}
655+
}
656+
}
615657
}
616658

617659
pub fn module_name_from_class_name(class_name: &str) -> String {

bindings-generator/src/classes.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ pub(crate) fn generate_enums(class: &GodotClass) -> TokenStream {
135135
v.0
136136
}
137137
}
138+
139+
impl FromVariant for #typ_name {
140+
#[inline]
141+
fn from_variant(v: &Variant) -> Result<Self, FromVariantError> {
142+
i64::from_variant(v).map(Self::from)
143+
}
144+
}
138145
}
139146
});
140147

bindings-generator/src/methods.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ use std::collections::HashMap;
1010
/// Types of icalls.
1111
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
1212
pub(crate) enum IcallType {
13+
#[cfg(feature = "ptrcall")]
1314
Ptr,
1415
Varargs,
1516
Var,
1617
}
1718

19+
// The `return_type` field is currently only used when ptrcall is enabled
20+
#[cfg_attr(not(feature = "ptrcall"), allow(dead_code))]
1821
pub(crate) struct MethodSig {
1922
pub(crate) return_type: Ty,
2023
pub(crate) arguments: Vec<Ty>,
@@ -107,6 +110,7 @@ impl MethodSig {
107110
// Only ptrcalls have "static" typing on their return types.
108111
// The other calling types always return `Variant`.
109112
let mut name = match icall_ty {
113+
#[cfg(feature = "ptrcall")]
110114
IcallType::Ptr => format!("icallptr_{}", ty_arg_name(&self.return_type)),
111115
IcallType::Varargs => String::from("icallvarargs_"),
112116
IcallType::Var => String::from("icallvar_"),
@@ -121,6 +125,7 @@ impl MethodSig {
121125
}
122126

123127
#[allow(clippy::single_match)]
128+
#[cfg(feature = "ptrcall")]
124129
pub(crate) fn icall_type(&self) -> IcallType {
125130
if self.has_varargs {
126131
return IcallType::Varargs;
@@ -133,6 +138,15 @@ impl MethodSig {
133138

134139
IcallType::Ptr
135140
}
141+
142+
#[cfg(not(feature = "ptrcall"))]
143+
pub(crate) fn icall_type(&self) -> IcallType {
144+
if self.has_varargs {
145+
return IcallType::Varargs;
146+
}
147+
148+
IcallType::Var
149+
}
136150
}
137151

138152
fn skip_method(method: &GodotMethod, name: &str) -> bool {
@@ -544,29 +558,26 @@ fn unsafe_reason(
544558

545559
fn ret_recover(ty: &Ty, icall_ty: IcallType) -> TokenStream {
546560
match icall_ty {
561+
#[cfg(feature = "ptrcall")]
547562
IcallType::Ptr => ty.to_return_post(),
548563
IcallType::Varargs => {
549564
// only variant possible
550565
quote! { ret }
551566
}
552-
IcallType::Var => {
553-
let rust_type = ty.to_rust();
554-
// always a variant returned, use FromVariant
555-
quote! {
556-
<#rust_type>::from_variant(&ret).expect("Unexpected variant type")
557-
}
558-
}
567+
IcallType::Var => ty.to_return_post_variant(),
559568
}
560569
}
561570

562571
pub(crate) fn generate_icall(name: String, sig: MethodSig) -> TokenStream {
563572
match sig.icall_type() {
573+
#[cfg(feature = "ptrcall")]
564574
IcallType::Ptr => ptrcall::generate_icall(name, sig),
565575
IcallType::Varargs => varargs_call::generate_icall(name, sig),
566576
IcallType::Var => varcall::generate_icall(name, sig),
567577
}
568578
}
569579

580+
#[cfg(feature = "ptrcall")]
570581
mod ptrcall {
571582
use super::*;
572583
use quote::{format_ident, quote};

bindings-generator/src/special_methods.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,18 @@ pub fn generate_queue_free_impl(api: &Api, class: &GodotClass) -> TokenStream {
8080
let class_name = format_ident!("{}", class.name);
8181

8282
let queue_free_output = if class.name == "Node" || api.class_inherits(class, "Node") {
83+
#[cfg(feature = "ptrcall")]
84+
let icall_ident = proc_macro2::Ident::new("icallptr_void", proc_macro2::Span::call_site());
85+
86+
#[cfg(not(feature = "ptrcall"))]
87+
let icall_ident = proc_macro2::Ident::new("icallvar_", proc_macro2::Span::call_site());
88+
8389
quote! {
8490
impl QueueFree for #class_name {
8591
#[inline]
8692
unsafe fn godot_queue_free(obj: *mut sys::godot_object) {
8793
let method_bind: *mut sys::godot_method_bind = crate::generated::node::NodeMethodTable::get(get_api()).queue_free;
88-
crate::icalls::icallptr_void(method_bind, obj)
94+
crate::icalls::#icall_ident(method_bind, obj);
8995
}
9096
}
9197
}

gdnative-bindings/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rust-version = "1.63"
1515
formatted = []
1616
one-class-one-file = []
1717
custom-godot = ["gdnative_bindings_generator/custom-godot"]
18+
ptrcall = ["gdnative_bindings_generator/ptrcall"]
1819

1920
[dependencies]
2021
gdnative-core = { path = "../gdnative-core", version = "=0.11.0" }

gdnative-bindings/src/generated.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#![allow(unused_variables)]
22

3+
#[cfg(feature = "ptrcall")]
34
use libc;
5+
46
use libc::c_char;
57
use std::mem;
68
use std::ptr;

gdnative-bindings/src/icalls.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
#![allow(unused_variables)]
22

3+
#[cfg(feature = "ptrcall")]
4+
use gdnative_core::*;
5+
#[cfg(feature = "ptrcall")]
36
use libc;
7+
48
use std::ptr;
59

610
use gdnative_core::core_types::*;
711
use gdnative_core::private::get_api;
812
use gdnative_core::sys;
9-
use gdnative_core::*;
1013

1114
include!(concat!(env!("OUT_DIR"), "/icalls.rs"));

gdnative/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ default = []
1919
async = ["gdnative-async"]
2020
custom-godot = ["gdnative-bindings/custom-godot"]
2121
formatted = ["gdnative-bindings/formatted", "gdnative-bindings/one-class-one-file"]
22+
ptrcall = ["gdnative-bindings/ptrcall"]
2223
serde = ["gdnative-core/serde"]
2324

2425
# Internal

gdnative/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@
6969
//! Enable if the generated binding source code should be human-readable and split
7070
//! into multiple files. This can also help IDEs that struggle with a single huge file.
7171
//!
72+
//! * **`ptrcall`**<br>
73+
//! Enables the `ptrcall` convention for calling Godot API methods. This increases performance, at the
74+
//! cost of forward binary compatibility with the engine. Binaries built with `ptrcall` enabled
75+
//! **may exhibit undefined behavior** when loaded by a different version of Godot, even when there are
76+
//! no breaking API changes as far as GDScript is concerned. Notably, the addition of new default
77+
//! parameters breaks any code using `ptrcall`.
78+
//!
79+
//! Cargo features are additive, and as such, it's only necessary to enable this feature for the final
80+
//! `cdylib` crates, whenever desired.
81+
//!
7282
//! [thread-safety]: https://docs.godotengine.org/en/stable/tutorials/threads/thread_safe_apis.html
7383
//! [gdnative-overview]: https://godot-rust.github.io/book/gdnative-overview.html
7484
//! [custom-godot]: https://godot-rust.github.io/book/advanced-guides/custom-godot.html

test/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ crate-type = ["cdylib"]
1414
default = []
1515
type-tag-fallback = ["gdnative/type-tag-fallback"]
1616
custom-godot = ["gdnative/custom-godot"]
17+
ptrcall = ["gdnative/ptrcall"]
1718

1819
[dependencies]
1920
gdnative = { path = "../gdnative", features = ["gd-test", "serde", "async"] }

0 commit comments

Comments
 (0)