feat: switch to the new Bvh from parry for the broad-phase (#853)

* feat: switch to the new Bvh from parry for the broad-phase

* chore: cargo fmt + update testbed

* chore: remove the multi-grid SAP broad-phase

* fix soft-ccd handling in broad-phase

* Fix contact cleanup in broad-phase after collider removal

* chore: clippy fixes

* fix CCD regression

* chore: update changelog

* fix build with the parallel feature enabled

* chore: remove the now useless broad-phase proxy index from colliders

* fix tests
This commit is contained in:
Sébastien Crozet
2025-07-11 22:36:40 +02:00
committed by GitHub
parent 86a257d4f1
commit 95bd6fcfeb
212 changed files with 2140 additions and 3953 deletions

View File

@@ -3,18 +3,18 @@ use rapier::math::Real;
use std::num::NonZeroUsize;
use crate::debug_render::DebugRenderPipelineResource;
use crate::harness::Harness;
use crate::harness::{Harness, RapierBroadPhaseType};
use crate::testbed::{
RapierSolverType, RunMode, TestbedActionFlags, TestbedState, TestbedStateFlags,
PHYSX_BACKEND_PATCH_FRICTION, PHYSX_BACKEND_TWO_FRICTION_DIR,
PHYSX_BACKEND_PATCH_FRICTION, PHYSX_BACKEND_TWO_FRICTION_DIR, RapierSolverType, RunMode,
TestbedActionFlags, TestbedState, TestbedStateFlags,
};
pub use bevy_egui::egui;
use crate::settings::SettingValue;
use crate::PhysicsState;
use bevy_egui::egui::{ComboBox, Slider, Ui, Window};
use crate::settings::SettingValue;
use bevy_egui::EguiContexts;
use bevy_egui::egui::{ComboBox, Slider, Ui, Window};
use rapier::dynamics::IntegrationParameters;
use web_time::Instant;
@@ -118,6 +118,7 @@ pub(crate) fn update_ui(
integration_parameters.num_solver_iterations =
NonZeroUsize::new(num_iterations).unwrap();
} else {
// Solver type.
let mut changed = false;
egui::ComboBox::from_label("solver type")
.width(150.0)
@@ -151,6 +152,32 @@ pub(crate) fn update_ui(
}
}
// Broad-phase.
let mut changed = false;
egui::ComboBox::from_label("broad-phase")
.width(150.0)
.selected_text(format!("{:?}", state.broad_phase_type))
.show_ui(ui, |ui| {
let broad_phase_type = [
RapierBroadPhaseType::BvhSubtreeOptimizer,
RapierBroadPhaseType::BvhWithoutOptimization,
];
for sty in broad_phase_type {
changed = ui
.selectable_value(&mut state.broad_phase_type, sty, format!("{sty:?}"))
.changed()
|| changed;
}
});
if changed {
harness.physics.broad_phase = state.broad_phase_type.init_broad_phase();
// Restart the simulation after a broad-phase changes since some
// broad-phase might not support hot-swapping.
state.action_flags.set(TestbedActionFlags::RESTART, true);
}
// Solver iterations.
let mut num_iterations = integration_parameters.num_solver_iterations.get();
ui.add(Slider::new(&mut num_iterations, 1..=40).text("num solver iters."));
integration_parameters.num_solver_iterations =
@@ -191,17 +218,14 @@ pub(crate) fn update_ui(
&mut integration_parameters.contact_natural_frequency,
0.01..=120.0,
)
.text(format!("contacts Hz (erp = {:.3})", curr_erp)),
.text(format!("contacts Hz (erp = {curr_erp:.3})")),
);
ui.add(
Slider::new(
&mut integration_parameters.contact_damping_ratio,
0.01..=20.0,
)
.text(format!(
"damping ratio (cfm-factor = {:.3})",
curr_cfm_factor
)),
.text(format!("damping ratio (cfm-factor = {curr_cfm_factor:.3})",)),
);
ui.add(
Slider::new(
@@ -377,10 +401,6 @@ fn profiling_ui(ui: &mut Ui, counters: &Counters) {
"Island computation: {:.2}ms",
counters.island_construction_time_ms()
));
ui.label(format!(
"Query pipeline: {:.2}ms",
counters.query_pipeline_update_time_ms()
));
ui.label(format!(
"User changes: {:.2}ms",
counters.stages.user_changes.time_ms()
@@ -391,7 +411,7 @@ fn profiling_ui(ui: &mut Ui, counters: &Counters) {
fn serialization_string(timestep_id: usize, physics: &PhysicsState) -> String {
let t = Instant::now();
// let t = Instant::now();
let bf = bincode::serialize(&physics.broad_phase).unwrap();
// let bf = bincode::serialize(&physics.broad_phase).unwrap();
// println!("bf: {}", Instant::now() - t);
// let t = Instant::now();
let nf = bincode::serialize(&physics.narrow_phase).unwrap();
@@ -406,7 +426,7 @@ fn serialization_string(timestep_id: usize, physics: &PhysicsState) -> String {
let js = bincode::serialize(&physics.impulse_joints).unwrap();
// println!("js: {}", Instant::now() - t);
let serialization_time = Instant::now() - t;
let hash_bf = md5::compute(&bf);
// let hash_bf = md5::compute(&bf);
let hash_nf = md5::compute(&nf);
let hash_bodies = md5::compute(&bs);
let hash_colliders = md5::compute(&cs);
@@ -421,16 +441,16 @@ Hashes at frame: {}
|_ Joints [{:.1}KB]: {}"#,
serialization_time.as_secs_f64() * 1000.0,
timestep_id,
bf.len() as f32 / 1000.0,
format!("{:?}", hash_bf).split_at(10).0,
"<fixme>", // bf.len() as f32 / 1000.0,
"<fixme>", // format!("{:?}", hash_bf).split_at(10).0,
nf.len() as f32 / 1000.0,
format!("{:?}", hash_nf).split_at(10).0,
format!("{hash_nf:?}").split_at(10).0,
bs.len() as f32 / 1000.0,
format!("{:?}", hash_bodies).split_at(10).0,
format!("{hash_bodies:?}").split_at(10).0,
cs.len() as f32 / 1000.0,
format!("{:?}", hash_colliders).split_at(10).0,
format!("{hash_colliders:?}").split_at(10).0,
js.len() as f32 / 1000.0,
format!("{:?}", hash_joints).split_at(10).0,
format!("{hash_joints:?}").split_at(10).0,
)
}
@@ -447,7 +467,7 @@ fn example_settings_ui(ui_context: &mut EguiContexts, state: &mut TestbedState)
let prev_value = value.clone();
match value {
SettingValue::Label(value) => {
ui.label(format!("{}: {}", name, value));
ui.label(format!("{name}: {value}"));
}
SettingValue::F32 { value, range } => {
ui.add(Slider::new(value, range.clone()).text(name));