Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: Incrementally-updated QueryPipeline memory usage linearly grows over time. #617

Open
finnbear opened this issue Apr 18, 2024 · 0 comments

Comments

@finnbear
Copy link

finnbear commented Apr 18, 2024

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:

state.query_pipeline.update(&state.bodies, &state.colliders);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant