Skip to content

Commit 9da0f54

Browse files
authored
Merge pull request #1314 from godot-rust/perf/object-arg-by-ref
`AsArg` for objects now consistently passes by reference
2 parents a03ecc5 + f8d4d12 commit 9da0f54

File tree

8 files changed

+426
-106
lines changed

8 files changed

+426
-106
lines changed

godot-core/src/meta/args/as_arg.rs

Lines changed: 144 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -91,18 +91,29 @@ where
9191
// ergonomics out weigh the runtime cost. Using the CowArg allows us to create a blanket implementation of the trait for all types that
9292
// implement ToGodot.
9393
#[doc(hidden)]
94-
fn into_arg<'r>(self) -> CowArg<'r, T>
94+
fn into_arg<'arg>(self) -> CowArg<'arg, T>
9595
where
96-
Self: 'r;
96+
Self: 'arg;
97+
98+
/// FFI-optimized argument conversion that may use `FfiObject` when beneficial.
99+
///
100+
/// Defaults to calling `into_arg()`, which always works, but might be an `Owned` for a conservative approach (e.g. object upcast).
101+
#[doc(hidden)]
102+
fn into_ffi_arg<'arg>(self) -> CowArg<'arg, T>
103+
where
104+
Self: 'arg,
105+
{
106+
self.into_arg()
107+
}
97108
}
98109

99110
impl<T> AsArg<T> for &T
100111
where
101112
T: ToGodot<Pass = ByRef>,
102113
{
103-
fn into_arg<'r>(self) -> CowArg<'r, T>
114+
fn into_arg<'arg>(self) -> CowArg<'arg, T>
104115
where
105-
Self: 'r,
116+
Self: 'arg,
106117
{
107118
CowArg::Borrowed(self)
108119
}
@@ -112,9 +123,9 @@ impl<T> AsArg<T> for T
112123
where
113124
T: ToGodot<Pass = ByValue>,
114125
{
115-
fn into_arg<'r>(self) -> CowArg<'r, T>
126+
fn into_arg<'arg>(self) -> CowArg<'arg, T>
116127
where
117-
Self: 'r,
128+
Self: 'arg,
118129
{
119130
CowArg::Owned(self)
120131
}
@@ -125,98 +136,169 @@ where
125136

126137
// TODO(v0.4): all objects + optional objects should be pass-by-ref.
127138

139+
// Convert `Gd` -> `Gd` (with upcast).
128140
impl<T, Base> AsArg<Gd<Base>> for &Gd<T>
129141
where
130142
T: Inherits<Base>,
131143
Base: GodotClass,
132144
{
133-
fn into_arg<'r>(self) -> CowArg<'r, Gd<Base>>
145+
fn into_arg<'arg>(self) -> CowArg<'arg, Gd<Base>>
134146
where
135-
Self: 'r,
147+
Self: 'arg,
136148
{
137-
CowArg::Owned(self.clone().upcast::<Base>())
149+
if T::IS_SAME_CLASS {
150+
// SAFETY: T == Base, so &Gd<T> can be treated as &Gd<Base>.
151+
let gd_ref = unsafe { std::mem::transmute::<&Gd<T>, &Gd<Base>>(self) };
152+
CowArg::Borrowed(gd_ref)
153+
} else {
154+
// Different types: clone and upcast. May incur ref-count increment for RefCounted objects, but the common path
155+
// of FFI passing is already optimized.
156+
CowArg::Owned(self.clone().upcast())
157+
}
158+
}
159+
160+
fn into_ffi_arg<'arg>(self) -> CowArg<'arg, Gd<Base>>
161+
where
162+
Self: 'arg,
163+
{
164+
// SAFETY: ObjectArg exists only during FFI call.
165+
let arg = unsafe { ObjectArg::from_gd(self) };
166+
CowArg::FfiObject(arg)
138167
}
139168
}
140169

141-
impl<T, U, D> AsArg<DynGd<T, D>> for &DynGd<U, D>
170+
/// Convert `DynGd` -> `DynGd` (with upcast).
171+
impl<T, D, Base> AsArg<DynGd<Base, D>> for &DynGd<T, D>
142172
where
143-
T: GodotClass,
144-
U: Inherits<T>,
173+
T: Inherits<Base>,
145174
D: ?Sized,
175+
Base: GodotClass,
146176
{
147-
fn into_arg<'r>(self) -> CowArg<'r, DynGd<T, D>>
177+
//noinspection RsConstantConditionIf - false positive in IDE for `T::IS_SAME_CLASS`.
178+
fn into_arg<'arg>(self) -> CowArg<'arg, DynGd<Base, D>>
148179
where
149-
Self: 'r,
180+
Self: 'arg,
150181
{
151-
CowArg::Owned(self.clone().upcast::<T>())
182+
if T::IS_SAME_CLASS {
183+
// SAFETY: T == Base, so &DynGd<T, D> can be treated as &DynGd<Base, D>.
184+
let gd_ref = unsafe { std::mem::transmute::<&Gd<T>, &DynGd<Base, D>>(self) };
185+
CowArg::Borrowed(gd_ref)
186+
} else {
187+
// Different types: clone and upcast. May incur ref-count increment for RefCounted objects, but the common path
188+
// of FFI passing is already optimized.
189+
CowArg::Owned(self.clone().upcast())
190+
}
191+
}
192+
193+
fn into_ffi_arg<'arg>(self) -> CowArg<'arg, DynGd<Base, D>>
194+
where
195+
Self: 'arg,
196+
{
197+
// SAFETY: ObjectArg exists only during FFI call.
198+
let arg = unsafe { ObjectArg::from_gd(self) };
199+
CowArg::FfiObject(arg)
152200
}
153201
}
154202

155-
// Convert DynGd -> Gd (with upcast).
156-
impl<'r, T, U, D> AsArg<Gd<T>> for &'r DynGd<U, D>
203+
// Convert `DynGd` -> `Gd` (with upcast).
204+
impl<T, D, Base> AsArg<Gd<Base>> for &DynGd<T, D>
157205
where
158-
T: GodotClass,
159-
U: Inherits<T>,
206+
T: Inherits<Base>,
160207
D: ?Sized,
208+
Base: GodotClass,
161209
{
162-
fn into_arg<'cow>(self) -> CowArg<'cow, Gd<T>>
210+
fn into_arg<'arg>(self) -> CowArg<'arg, Gd<Base>>
211+
where
212+
Self: 'arg,
213+
{
214+
let gd_ref: &Gd<T> = self; // DynGd -> Gd deref.
215+
AsArg::into_arg(gd_ref)
216+
}
217+
218+
fn into_ffi_arg<'arg>(self) -> CowArg<'arg, Gd<Base>>
163219
where
164-
'r: 'cow,
220+
Self: 'arg,
165221
{
166-
CowArg::Owned(self.clone().upcast::<T>().into_gd())
222+
let gd_ref: &Gd<T> = self; // DynGd -> Gd deref.
223+
AsArg::into_ffi_arg(gd_ref)
167224
}
168225
}
169226

170227
// ----------------------------------------------------------------------------------------------------------------------------------------------
171228
// Optional object (Gd + DynGd) impls
172229

173-
impl<T, U> AsArg<Option<Gd<T>>> for &Option<Gd<U>>
230+
/// Convert `Option<&Gd>` -> `Option<Gd>` (with upcast).
231+
impl<T, Base> AsArg<Option<Gd<Base>>> for Option<&Gd<T>>
174232
where
175-
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
176-
U: Inherits<T>,
233+
T: Inherits<Base>,
234+
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
177235
{
178-
fn into_arg<'r>(self) -> CowArg<'r, Option<Gd<T>>> {
236+
fn into_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
237+
where
238+
Self: 'arg,
239+
{
240+
// Upcasting to an owned value Gd<Base> requires cloning. Optimized path in into_ffi_arg().
179241
match self {
180-
Some(gd) => CowArg::Owned(Some(gd.clone().upcast::<T>())),
242+
Some(gd_ref) => AsArg::into_arg(gd_ref),
181243
None => CowArg::Owned(None),
182244
}
183245
}
184-
}
185246

186-
impl<T, U> AsArg<Option<Gd<T>>> for Option<&Gd<U>>
187-
where
188-
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
189-
U: Inherits<T>,
190-
{
191-
fn into_arg<'cow>(self) -> CowArg<'cow, Option<Gd<T>>> {
192-
// This needs to construct a new Option<Gd<T>>, so cloning is unavoidable
193-
// since we go from Option<&Gd<U>> to Option<Gd<T>>
194-
match self {
195-
Some(gd) => CowArg::Owned(Some(gd.clone().upcast::<T>())),
196-
None => CowArg::Owned(None),
197-
}
247+
fn into_ffi_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
248+
where
249+
Self: 'arg,
250+
{
251+
// SAFETY: ObjectArg exists only during FFI call.
252+
let arg = unsafe { ObjectArg::from_option_gd(self) };
253+
CowArg::FfiObject(arg)
198254
}
199255
}
200256

201-
impl<T, U> AsArg<Option<Gd<T>>> for &Gd<U>
257+
/// Convert `&Gd` -> `Option<Gd>` (with upcast).
258+
impl<T, Base> AsArg<Option<Gd<Base>>> for &Gd<T>
202259
where
203-
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
204-
U: Inherits<T>,
260+
T: Inherits<Base>,
261+
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
205262
{
206-
fn into_arg<'cow>(self) -> CowArg<'cow, Option<Gd<T>>> {
207-
CowArg::Owned(Some(self.clone().upcast::<T>()))
263+
fn into_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
264+
where
265+
Self: 'arg,
266+
{
267+
// Upcasting to an owned value Gd<Base> requires cloning. Optimized path in into_ffi_arg().
268+
CowArg::Owned(Some(self.clone().upcast::<Base>()))
269+
}
270+
271+
fn into_ffi_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
272+
where
273+
Self: 'arg,
274+
{
275+
// SAFETY: ObjectArg exists only during FFI call.
276+
let arg = unsafe { ObjectArg::from_gd(self) };
277+
CowArg::FfiObject(arg)
208278
}
209279
}
210280

211-
impl<T, U, D> AsArg<Option<Gd<T>>> for &DynGd<U, D>
281+
/// Convert `&DynGd` -> `Option<Gd>` (with upcast).
282+
impl<T, D, Base> AsArg<Option<Gd<Base>>> for &DynGd<T, D>
212283
where
213-
T: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
214-
U: Inherits<T>,
284+
T: Inherits<Base>,
215285
D: ?Sized,
286+
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
216287
{
217-
fn into_arg<'cow>(self) -> CowArg<'cow, Option<Gd<T>>> {
218-
let gd: &Gd<U> = self; // Deref
219-
CowArg::Owned(Some(gd.clone().upcast::<T>()))
288+
fn into_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
289+
where
290+
Self: 'arg,
291+
{
292+
let gd_ref: &Gd<T> = self; // DynGd -> Gd deref.
293+
AsArg::into_arg(gd_ref)
294+
}
295+
296+
fn into_ffi_arg<'arg>(self) -> CowArg<'arg, Option<Gd<Base>>>
297+
where
298+
Self: 'arg,
299+
{
300+
let gd_ref: &Gd<T> = self; // DynGd -> Gd deref.
301+
AsArg::into_ffi_arg(gd_ref)
220302
}
221303
}
222304

@@ -251,9 +333,9 @@ where
251333
/// }
252334
/// }
253335
/// ```
254-
pub fn owned_into_arg<'r, T>(owned_val: T) -> impl AsArg<T> + 'r
336+
pub fn owned_into_arg<'arg, T>(owned_val: T) -> impl AsArg<T> + 'arg
255337
where
256-
T: ToGodot + 'r,
338+
T: ToGodot + 'arg,
257339
{
258340
CowArg::Owned(owned_val)
259341
}
@@ -354,9 +436,9 @@ impl<T> AsArg<T> for CowArg<'_, T>
354436
where
355437
for<'r> T: ToGodot,
356438
{
357-
fn into_arg<'r>(self) -> CowArg<'r, T>
439+
fn into_arg<'arg>(self) -> CowArg<'arg, T>
358440
where
359-
Self: 'r,
441+
Self: 'arg,
360442
{
361443
self
362444
}
@@ -368,13 +450,13 @@ where
368450
// Note: for all string types S, `impl AsArg<S> for &mut String` is not yet provided, but we can add them if needed.
369451

370452
impl AsArg<GString> for &str {
371-
fn into_arg<'r>(self) -> CowArg<'r, GString> {
453+
fn into_arg<'arg>(self) -> CowArg<'arg, GString> {
372454
CowArg::Owned(GString::from(self))
373455
}
374456
}
375457

376458
impl AsArg<GString> for &String {
377-
fn into_arg<'r>(self) -> CowArg<'r, GString> {
459+
fn into_arg<'arg>(self) -> CowArg<'arg, GString> {
378460
CowArg::Owned(GString::from(self))
379461
}
380462
}
@@ -383,19 +465,19 @@ impl AsArg<GString> for &String {
383465
// StringName
384466

385467
impl AsArg<StringName> for &str {
386-
fn into_arg<'r>(self) -> CowArg<'r, StringName> {
468+
fn into_arg<'arg>(self) -> CowArg<'arg, StringName> {
387469
CowArg::Owned(StringName::from(self))
388470
}
389471
}
390472

391473
impl AsArg<StringName> for &String {
392-
fn into_arg<'r>(self) -> CowArg<'r, StringName> {
474+
fn into_arg<'arg>(self) -> CowArg<'arg, StringName> {
393475
CowArg::Owned(StringName::from(self))
394476
}
395477
}
396478

397479
impl AsArg<StringName> for &'static CStr {
398-
fn into_arg<'r>(self) -> CowArg<'r, StringName> {
480+
fn into_arg<'arg>(self) -> CowArg<'arg, StringName> {
399481
CowArg::Owned(StringName::from(self))
400482
}
401483
}
@@ -404,13 +486,13 @@ impl AsArg<StringName> for &'static CStr {
404486
// NodePath
405487

406488
impl AsArg<NodePath> for &str {
407-
fn into_arg<'r>(self) -> CowArg<'r, NodePath> {
489+
fn into_arg<'arg>(self) -> CowArg<'arg, NodePath> {
408490
CowArg::Owned(NodePath::from(self))
409491
}
410492
}
411493

412494
impl AsArg<NodePath> for &String {
413-
fn into_arg<'r>(self) -> CowArg<'r, NodePath> {
495+
fn into_arg<'arg>(self) -> CowArg<'arg, NodePath> {
414496
CowArg::Owned(NodePath::from(self))
415497
}
416498
}

0 commit comments

Comments
 (0)