309 lines
11 KiB
Rust
309 lines
11 KiB
Rust
use rapier::counters::Counters;
|
|
use rapier::math::Real;
|
|
use std::num::NonZeroUsize;
|
|
|
|
use crate::debug_render::DebugRenderPipelineResource;
|
|
use crate::harness::Harness;
|
|
use crate::testbed::{
|
|
RapierSolverType, RunMode, TestbedActionFlags, TestbedState, TestbedStateFlags,
|
|
PHYSX_BACKEND_PATCH_FRICTION, PHYSX_BACKEND_TWO_FRICTION_DIR,
|
|
};
|
|
|
|
use crate::PhysicsState;
|
|
use bevy_egui::egui::Slider;
|
|
use bevy_egui::{egui, EguiContexts};
|
|
|
|
pub fn update_ui(
|
|
ui_context: &mut EguiContexts,
|
|
state: &mut TestbedState,
|
|
harness: &mut Harness,
|
|
debug_render: &mut DebugRenderPipelineResource,
|
|
) {
|
|
egui::Window::new("Parameters").show(ui_context.ctx_mut(), |ui| {
|
|
if state.backend_names.len() > 1 && !state.example_names.is_empty() {
|
|
let mut changed = false;
|
|
egui::ComboBox::from_label("backend")
|
|
.width(150.0)
|
|
.selected_text(state.backend_names[state.selected_backend])
|
|
.show_ui(ui, |ui| {
|
|
for (id, name) in state.backend_names.iter().enumerate() {
|
|
changed = ui
|
|
.selectable_value(&mut state.selected_backend, id, *name)
|
|
.changed()
|
|
|| changed;
|
|
}
|
|
});
|
|
|
|
if changed {
|
|
state
|
|
.action_flags
|
|
.set(TestbedActionFlags::BACKEND_CHANGED, true);
|
|
}
|
|
|
|
ui.separator();
|
|
}
|
|
|
|
ui.horizontal(|ui| {
|
|
if ui.button("<").clicked() && state.selected_example > 0 {
|
|
state.selected_example -= 1;
|
|
state
|
|
.action_flags
|
|
.set(TestbedActionFlags::EXAMPLE_CHANGED, true)
|
|
}
|
|
|
|
if ui.button(">").clicked() && state.selected_example + 1 < state.example_names.len() {
|
|
state.selected_example += 1;
|
|
state
|
|
.action_flags
|
|
.set(TestbedActionFlags::EXAMPLE_CHANGED, true)
|
|
}
|
|
|
|
let mut changed = false;
|
|
egui::ComboBox::from_label("example")
|
|
.width(150.0)
|
|
.selected_text(state.example_names[state.selected_example])
|
|
.show_ui(ui, |ui| {
|
|
for (id, name) in state.example_names.iter().enumerate() {
|
|
changed = ui
|
|
.selectable_value(&mut state.selected_example, id, *name)
|
|
.changed()
|
|
|| changed;
|
|
}
|
|
});
|
|
if changed {
|
|
state
|
|
.action_flags
|
|
.set(TestbedActionFlags::EXAMPLE_CHANGED, true);
|
|
}
|
|
});
|
|
|
|
ui.separator();
|
|
|
|
ui.collapsing("Profile infos", |ui| {
|
|
ui.horizontal_wrapped(|ui| {
|
|
ui.label(profiling_string(&harness.physics.pipeline.counters))
|
|
});
|
|
});
|
|
ui.collapsing("Serialization infos", |ui| {
|
|
ui.horizontal_wrapped(|ui| {
|
|
ui.label(serialization_string(
|
|
harness.state.timestep_id,
|
|
&harness.physics,
|
|
))
|
|
});
|
|
});
|
|
|
|
let integration_parameters = &mut harness.physics.integration_parameters;
|
|
|
|
if state.selected_backend == PHYSX_BACKEND_PATCH_FRICTION
|
|
|| state.selected_backend == PHYSX_BACKEND_TWO_FRICTION_DIR
|
|
{
|
|
let mut num_iterations = integration_parameters.num_solver_iterations.get();
|
|
ui.add(Slider::new(&mut num_iterations, 1..=40).text("pos. iters."));
|
|
integration_parameters.num_solver_iterations =
|
|
NonZeroUsize::new(num_iterations).unwrap();
|
|
} else {
|
|
let mut changed = false;
|
|
egui::ComboBox::from_label("solver type")
|
|
.width(150.0)
|
|
.selected_text(format!("{:?}", state.solver_type))
|
|
.show_ui(ui, |ui| {
|
|
let solver_types = [
|
|
RapierSolverType::SmallStepsPgs,
|
|
RapierSolverType::StandardPgs,
|
|
];
|
|
for sty in solver_types {
|
|
changed = ui
|
|
.selectable_value(&mut state.solver_type, sty, format!("{sty:?}"))
|
|
.changed()
|
|
|| changed;
|
|
}
|
|
});
|
|
|
|
if changed {
|
|
match state.solver_type {
|
|
RapierSolverType::SmallStepsPgs => {
|
|
integration_parameters.switch_to_small_steps_pgs_solver()
|
|
}
|
|
RapierSolverType::StandardPgs => {
|
|
integration_parameters.switch_to_standard_pgs_solver()
|
|
}
|
|
}
|
|
}
|
|
|
|
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 =
|
|
NonZeroUsize::new(num_iterations).unwrap();
|
|
|
|
ui.add(
|
|
Slider::new(
|
|
&mut integration_parameters.num_internal_pgs_iterations,
|
|
1..=40,
|
|
)
|
|
.text("num internal PGS iters."),
|
|
);
|
|
ui.add(
|
|
Slider::new(
|
|
&mut integration_parameters.num_additional_friction_iterations,
|
|
1..=40,
|
|
)
|
|
.text("num additional frict. iters."),
|
|
);
|
|
}
|
|
|
|
#[cfg(feature = "parallel")]
|
|
{
|
|
let mut num_threads = harness.state.num_threads();
|
|
ui.add(
|
|
Slider::new(&mut num_threads, 1..=num_cpus::get_physical()).text("num. threads"),
|
|
);
|
|
harness.state.set_num_threads(num_threads);
|
|
}
|
|
ui.add(
|
|
Slider::new(&mut integration_parameters.max_ccd_substeps, 0..=10).text("CCD substeps"),
|
|
);
|
|
ui.add(
|
|
Slider::new(&mut integration_parameters.min_island_size, 1..=10_000)
|
|
.text("min island size"),
|
|
);
|
|
let mut frequency = integration_parameters.inv_dt().round() as u32;
|
|
ui.add(Slider::new(&mut frequency, 0..=240).text("frequency (Hz)"));
|
|
integration_parameters.set_inv_dt(frequency as Real);
|
|
|
|
let mut sleep = state.flags.contains(TestbedStateFlags::SLEEP);
|
|
// let mut contact_points = state.flags.contains(TestbedStateFlags::CONTACT_POINTS);
|
|
// let mut wireframe = state.flags.contains(TestbedStateFlags::WIREFRAME);
|
|
ui.checkbox(&mut sleep, "sleep enabled");
|
|
// ui.checkbox(&mut contact_points, "draw contacts");
|
|
// ui.checkbox(&mut wireframe, "draw wireframes");
|
|
ui.checkbox(&mut debug_render.enabled, "debug render enabled");
|
|
|
|
state.flags.set(TestbedStateFlags::SLEEP, sleep);
|
|
// state
|
|
// .flags
|
|
// .set(TestbedStateFlags::CONTACT_POINTS, contact_points);
|
|
// state.flags.set(TestbedStateFlags::WIREFRAME, wireframe);
|
|
ui.separator();
|
|
|
|
let label = if state.running == RunMode::Stop {
|
|
"Start (T)"
|
|
} else {
|
|
"Pause (T)"
|
|
};
|
|
|
|
if ui.button(label).clicked() {
|
|
if state.running == RunMode::Stop {
|
|
state.running = RunMode::Running
|
|
} else {
|
|
state.running = RunMode::Stop
|
|
}
|
|
}
|
|
|
|
if ui.button("Single Step (S)").clicked() {
|
|
state.running = RunMode::Step;
|
|
}
|
|
|
|
if ui.button("Take snapshot").clicked() {
|
|
state
|
|
.action_flags
|
|
.set(TestbedActionFlags::TAKE_SNAPSHOT, true);
|
|
}
|
|
|
|
if ui.button("Restore snapshot").clicked() {
|
|
state
|
|
.action_flags
|
|
.set(TestbedActionFlags::RESTORE_SNAPSHOT, true);
|
|
}
|
|
|
|
if ui.button("Restart (R)").clicked() {
|
|
state.action_flags.set(TestbedActionFlags::RESTART, true);
|
|
}
|
|
});
|
|
}
|
|
|
|
fn profiling_string(counters: &Counters) -> String {
|
|
format!(
|
|
r#"Total: {:.2}ms
|
|
Collision detection: {:.2}ms
|
|
|_ Broad-phase: {:.2}ms
|
|
Narrow-phase: {:.2}ms
|
|
Island computation: {:.2}ms
|
|
Solver: {:.2}ms
|
|
|_ Velocity assembly: {:.2}ms
|
|
Velocity resolution: {:.2}ms
|
|
Velocity integration: {:.2}ms
|
|
Position assembly: {:.2}ms
|
|
Position resolution: {:.2}ms
|
|
CCD: {:.2}ms
|
|
|_ # of substeps: {}
|
|
TOI computation: {:.2}ms
|
|
Broad-phase: {:.2}ms
|
|
Narrow-phase: {:.2}ms
|
|
Solver: {:.2}ms"#,
|
|
counters.step_time(),
|
|
counters.collision_detection_time(),
|
|
counters.broad_phase_time(),
|
|
counters.narrow_phase_time(),
|
|
counters.island_construction_time(),
|
|
counters.solver_time(),
|
|
counters.solver.velocity_assembly_time.time(),
|
|
counters.velocity_resolution_time(),
|
|
counters.solver.velocity_update_time.time(),
|
|
counters.solver.position_assembly_time.time(),
|
|
counters.position_resolution_time(),
|
|
counters.ccd_time(),
|
|
counters.ccd.num_substeps,
|
|
counters.ccd.toi_computation_time.time(),
|
|
counters.ccd.broad_phase_time.time(),
|
|
counters.ccd.narrow_phase_time.time(),
|
|
counters.ccd.solver_time.time(),
|
|
)
|
|
}
|
|
|
|
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();
|
|
// println!("bf: {}", instant::now() - t);
|
|
// let t = instant::now();
|
|
let nf = bincode::serialize(&physics.narrow_phase).unwrap();
|
|
// println!("nf: {}", instant::now() - t);
|
|
// let t = instant::now();
|
|
let bs = bincode::serialize(&physics.bodies).unwrap();
|
|
// println!("bs: {}", instant::now() - t);
|
|
// let t = instant::now();
|
|
let cs = bincode::serialize(&physics.colliders).unwrap();
|
|
// println!("cs: {}", instant::now() - t);
|
|
// let t = instant::now();
|
|
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_nf = md5::compute(&nf);
|
|
let hash_bodies = md5::compute(&bs);
|
|
let hash_colliders = md5::compute(&cs);
|
|
let hash_joints = md5::compute(&js);
|
|
format!(
|
|
r#"Serialization time: {:.2}ms
|
|
Hashes at frame: {}
|
|
|_ Broad phase [{:.1}KB]: {}
|
|
|_ Narrow phase [{:.1}KB]: {}
|
|
|_ &RigidBodySet [{:.1}KB]: {}
|
|
|_ Colliders [{:.1}KB]: {}
|
|
|_ Joints [{:.1}KB]: {}"#,
|
|
serialization_time,
|
|
timestep_id,
|
|
bf.len() as f32 / 1000.0,
|
|
format!("{:?}", hash_bf).split_at(10).0,
|
|
nf.len() as f32 / 1000.0,
|
|
format!("{:?}", hash_nf).split_at(10).0,
|
|
bs.len() as f32 / 1000.0,
|
|
format!("{:?}", hash_bodies).split_at(10).0,
|
|
cs.len() as f32 / 1000.0,
|
|
format!("{:?}", hash_colliders).split_at(10).0,
|
|
js.len() as f32 / 1000.0,
|
|
format!("{:?}", hash_joints).split_at(10).0,
|
|
)
|
|
}
|