Using Rapier 0.17.2 in a game where objects spawn and despawn (but the total number remains roughly constant), I observe a linear increase in total QueryPipeline memory usage over time (ultimately harming bandwidth usage).
Here I plot serialized size, in bytes, of the QueryPipeline (y axis) against game tick # (x axis):
I was able to reproduce this on Rapier 0.18.0 (warning: don't mouse over the testbed window as it will crash):
[package]
name = "rapier-mem"
version = "0.1.0"
edition = "2021"
[dependencies]
rapier3d = {version = "0.18", features = ["enhanced-determinism", "serde-serialize"] }
rapier_testbed3d = "0.18"
bincode = "1.3"
rand = "0.8.4"
use std::collections::VecDeque;
use rand::thread_rng;
use rand::Rng;
use rapier3d::prelude::*;
use rapier_testbed3d::Testbed;
use rapier_testbed3d::TestbedApp;
pub fn main() {
let builders: Vec<(_, fn(&mut Testbed))> = vec![
("Bug", init_world),
];
let testbed = TestbedApp::from_builders(0, builders);
testbed.run()
}
pub fn init_world(testbed: &mut Testbed) {
let bodies = RigidBodySet::new();
let colliders = ColliderSet::new();
let impulse_joints = ImpulseJointSet::new();
let multibody_joints = MultibodyJointSet::new();
testbed.set_world(bodies, colliders, impulse_joints, multibody_joints);
testbed.look_at(point![100.0, 100.0, 100.0], Point::origin());
let mut i = 0;
let mut rng = thread_rng();
let mut handles = VecDeque::new();
testbed.add_callback(move |mut graphics, state, _, _| {
let rigid_body = RigidBodyBuilder::dynamic().translation(vector![
rng.gen_range(0.0..100.0),
rng.gen_range(0.0..100.0),
rng.gen_range(0.0..100.0)
]);
let handle = state.bodies.insert(rigid_body);
let collider = ColliderBuilder::cuboid(1.0, 1.0, 1.0);
state.colliders.insert_with_parent(collider, handle, &mut state.bodies);
let color0 = [0.7, 0.5, 0.9];
graphics.as_mut().unwrap().set_body_color(handle, color0);
handles.push_back(handle);
while handles.len() > 10 {
let remove = handles.pop_front().unwrap();
state.bodies.remove(remove, &mut state.islands, &mut state.colliders, &mut state.impulse_joints, &mut state.multibody_joints, true);
}
if i % 100 == 0 {
assert!(state.bodies.len() <= 10);
assert!(state.colliders.len() <= 10);
let incremental_bytes = bincode::serialized_size(&state.query_pipeline).unwrap();
let mut new_qp = QueryPipeline::new();
new_qp.update(&state.bodies, &state.colliders);
let new_bytes = bincode::serialized_size(&new_qp).unwrap();
println!("{i}, {incremental_bytes}, {new_bytes}");
}
i += 1;
});
}
At first I thought the refit_and_rebalance algorithm wasn't running but, as far as I can tell, it is.
Workaround
Applications can occasionally reset the query pipeline:
Using Rapier
0.17.2
in a game where objects spawn and despawn (but the total number remains roughly constant), I observe a linear increase in totalQueryPipeline
memory usage over time (ultimately harming bandwidth usage).Here I plot serialized size, in bytes, of the
QueryPipeline
(y axis) against game tick # (x axis):I was able to reproduce this on Rapier
0.18.0
(warning: don't mouse over the testbed window as it will crash):At first I thought the
refit_and_rebalance
algorithm wasn't running but, as far as I can tell, it is.Workaround
Applications can occasionally reset the query pipeline: