Skip to content

Commit c46ca24

Browse files
committed
Add integration tests and benchmarks for variant marshalling
Tests roundtrip via public API and direct `RustVariant` view for all `RustMarshal` types. Benches: `Array::extend` size-hint fast path, packed-to-typed conversion.
1 parent b57f8df commit c46ca24

7 files changed

Lines changed: 596 additions & 2 deletions

File tree

godot-core/src/builtin/variant/rust_variant.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ impl RustVariant {
111111
///
112112
/// This is safe even for variants holding ref-counted types: `&mut Variant` guarantees exclusive access to the handle itself.
113113
/// The guarded `set_value()` method prevents overwriting such types without proper destruction.
114+
///
115+
/// Gated on `trace` because mutable views (and the `set_value`/`SetError` machinery below) are only used by integration tests
116+
/// in `itest/`. They are not part of the production fast path and would otherwise widen the internal API surface unnecessarily.
114117
#[cfg(feature = "trace")]
115118
pub fn view_mut(variant: &mut Variant) -> &mut Self {
116119
// SAFETY: OpaqueVariant and RustVariant have the same size/alignment (verified at compile time).

itest/rust/src/benchmarks/array.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) godot-rust; Bromeon and contributors.
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
use std::hint::black_box;
9+
10+
use godot::builtin::{Array, GString};
11+
12+
use crate::framework::{BenchResult, bench, bench_measure};
13+
14+
#[bench(manual)]
15+
fn array_extend_i64() -> BenchResult {
16+
bench_measure(1, || {
17+
let mut arr = Array::new();
18+
arr.extend((10_000i64..20_000).map(black_box));
19+
black_box(arr)
20+
})
21+
}
22+
23+
#[bench(manual)]
24+
fn array_extend_gstring() -> BenchResult {
25+
bench_measure(1, || {
26+
let mut arr = Array::new();
27+
arr.extend(
28+
(10_000..20_000).map(|i| black_box(GString::from(format!("str_{}", i).as_str()))),
29+
);
30+
black_box(arr)
31+
})
32+
}

itest/rust/src/benchmarks/mod.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@
1010
use std::hint::black_box;
1111

1212
use godot::builtin::inner::InnerRect2i;
13-
use godot::builtin::{GString, PackedInt32Array, Rect2i, StringName, Vector2i};
13+
use godot::builtin::{GString, PackedFloat32Array, PackedInt32Array, Rect2i, StringName, Vector2i};
1414
use godot::classes::{Node3D, Os, RefCounted};
1515
use godot::obj::{Gd, InstanceId, NewAlloc, NewGd, Singleton};
1616
use godot::register::GodotClass;
1717

18-
use crate::framework::bench;
18+
use crate::framework::{BenchResult, bench, bench_measure};
1919

20+
mod array;
2021
mod callable;
2122
mod color;
23+
mod variant;
2224

2325
#[bench]
2426
fn builtin_string_ctor() -> GString {
@@ -110,6 +112,30 @@ fn packed_array_from_iter_unknown_size() -> PackedInt32Array {
110112
}))
111113
}
112114

115+
#[bench(manual)]
116+
fn packed_f32_to_array() -> BenchResult {
117+
let packed = PackedFloat32Array::from_iter((0..10_000).map(|v: i32| v as f32));
118+
bench_measure(1, || packed.to_typed_array())
119+
}
120+
121+
#[bench(manual)]
122+
fn packed_f32_to_var_array() -> BenchResult {
123+
let packed = PackedFloat32Array::from_iter((0..10_000).map(|v: i32| v as f32));
124+
bench_measure(1, || packed.to_var_array())
125+
}
126+
127+
#[bench(manual)]
128+
fn packed_f32_to_array_small() -> BenchResult {
129+
let packed = PackedFloat32Array::from_iter((0..100).map(|v: i32| v as f32));
130+
bench_measure(1, || packed.to_typed_array())
131+
}
132+
133+
#[bench(manual)]
134+
fn packed_f32_to_var_array_small() -> BenchResult {
135+
let packed = PackedFloat32Array::from_iter((0..100).map(|v: i32| v as f32));
136+
bench_measure(1, || packed.to_var_array())
137+
}
138+
113139
// ----------------------------------------------------------------------------------------------------------------------------------------------
114140
// Helpers for benchmarks above
115141

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (c) godot-rust; Bromeon and contributors.
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
use std::hint::black_box;
9+
10+
use godot::builtin::{Color, Rid, Variant, Vector2i, Vector3};
11+
use godot::meta::{FromGodot, ToGodot};
12+
13+
use crate::framework::{BenchResult, bench, bench_measure};
14+
15+
// Scalar types.
16+
17+
#[bench]
18+
fn variant_from_bool() -> Variant {
19+
black_box(true).to_variant()
20+
}
21+
22+
#[bench]
23+
fn variant_to_bool() -> bool {
24+
bool::from_variant(black_box(&true.to_variant()))
25+
}
26+
27+
#[bench]
28+
fn variant_from_i64() -> Variant {
29+
black_box(12345_i64).to_variant()
30+
}
31+
32+
#[bench]
33+
fn variant_to_i64() -> i64 {
34+
i64::from_variant(black_box(&12345_i64.to_variant()))
35+
}
36+
37+
#[bench]
38+
fn variant_from_f64() -> Variant {
39+
black_box(1.234_f64).to_variant()
40+
}
41+
42+
#[bench]
43+
fn variant_to_f64() -> f64 {
44+
f64::from_variant(black_box(&1.234_f64.to_variant()))
45+
}
46+
47+
// Vector types.
48+
49+
#[bench]
50+
fn variant_from_vector2i() -> Variant {
51+
black_box(Vector2i::new(100, 200)).to_variant()
52+
}
53+
54+
#[bench]
55+
fn variant_to_vector2i() -> Vector2i {
56+
Vector2i::from_variant(black_box(&Vector2i::new(100, 200).to_variant()))
57+
}
58+
59+
#[bench]
60+
fn variant_from_vector3() -> Variant {
61+
black_box(Vector3::new(1.5, 2.5, 3.5)).to_variant()
62+
}
63+
64+
#[bench]
65+
fn variant_to_vector3() -> Vector3 {
66+
Vector3::from_variant(black_box(&Vector3::new(1.5, 2.5, 3.5).to_variant()))
67+
}
68+
69+
// Other POD types.
70+
71+
#[bench]
72+
fn variant_from_color() -> Variant {
73+
black_box(Color::from_rgba(0.5, 0.3, 0.8, 1.0)).to_variant()
74+
}
75+
76+
#[bench]
77+
fn variant_to_color() -> Color {
78+
Color::from_variant(black_box(
79+
&Color::from_rgba(0.5, 0.3, 0.8, 1.0).to_variant(),
80+
))
81+
}
82+
83+
#[bench]
84+
fn variant_from_rid() -> Variant {
85+
black_box(Rid::new(12345)).to_variant()
86+
}
87+
88+
#[bench]
89+
fn variant_to_rid() -> Rid {
90+
Rid::from_variant(black_box(&Rid::new(12345).to_variant()))
91+
}
92+
93+
// Lifecycle: nil construction, clone, drop.
94+
95+
#[bench]
96+
fn variant_nil_ctor() -> Variant {
97+
Variant::nil()
98+
}
99+
100+
#[bench]
101+
fn variant_clone_i64() -> Variant {
102+
let v = black_box(12345_i64).to_variant();
103+
black_box(&v).clone()
104+
}
105+
106+
#[bench]
107+
fn variant_clone_vector3() -> Variant {
108+
let v = black_box(Vector3::new(1.0, 2.0, 3.0)).to_variant();
109+
black_box(&v).clone()
110+
}
111+
112+
#[bench(manual)]
113+
fn variant_drop_i64_x1000() -> BenchResult {
114+
bench_measure(1, || {
115+
let mut count = 0_i64;
116+
for i in 0..1000_i64 {
117+
let v = black_box(i).to_variant();
118+
count += 1;
119+
drop(black_box(v));
120+
}
121+
black_box(count)
122+
})
123+
}
124+
125+
#[bench(manual)]
126+
fn variant_drop_vector3_x1000() -> BenchResult {
127+
bench_measure(1, || {
128+
let mut count = 0_i32;
129+
for i in 0..1000_i32 {
130+
let v = black_box(Vector3::new(i as f32, i as f32, i as f32)).to_variant();
131+
count += 1;
132+
drop(black_box(v));
133+
}
134+
black_box(count)
135+
})
136+
}
137+
138+
// Bulk round-trips (representative of real use: many values through variants).
139+
140+
#[bench(manual)]
141+
fn variant_roundtrip_i64_x1000() -> BenchResult {
142+
bench_measure(1, || {
143+
let mut sum = 0i64;
144+
for i in 0..1000_i64 {
145+
let v = black_box(i).to_variant();
146+
sum = sum.wrapping_add(i64::from_variant(black_box(&v)));
147+
}
148+
black_box(sum)
149+
})
150+
}
151+
152+
#[bench(manual)]
153+
fn variant_roundtrip_vector3_x1000() -> BenchResult {
154+
bench_measure(1, || {
155+
let mut result = Vector3::ZERO;
156+
for i in 0..1000_i32 {
157+
let v = black_box(Vector3::new(i as f32, i as f32 * 2.0, i as f32 * 3.0)).to_variant();
158+
result += Vector3::from_variant(black_box(&v));
159+
}
160+
black_box(result)
161+
})
162+
}

0 commit comments

Comments
 (0)