diff --git a/crates/skate-core/src/physics/board_runtime.rs b/crates/skate-core/src/physics/board_runtime.rs index f63de2f3..7590cec8 100644 --- a/crates/skate-core/src/physics/board_runtime.rs +++ b/crates/skate-core/src/physics/board_runtime.rs @@ -50,6 +50,11 @@ pub struct BoardRuntime { } impl BoardRuntime { + /// Opt-in inspection of completed rows; capture never changes solver inputs. + pub fn take_solver_diagnostics(&mut self, enabled: bool) -> Option { + self.step.diagnostic_capture = enabled; + self.step.diagnostic_snapshot.take() + } /// `authored` contains part poses in board space. `spawn` places the deck /// part in world space, and moves the other parts by the same rigid delta. /// All mass properties come from the caller's physical data producer. diff --git a/crates/skate-core/src/physics/board_step.rs b/crates/skate-core/src/physics/board_step.rs index f61023da..1c705eaa 100644 --- a/crates/skate-core/src/physics/board_step.rs +++ b/crates/skate-core/src/physics/board_step.rs @@ -103,6 +103,9 @@ pub struct BoardStep { contacts: Vec, reports: Vec, reactions: Vec, + pub diagnostic_capture: bool, + diagnostic_tick: u64, + pub diagnostic_snapshot: Option, } impl BoardStep { @@ -206,6 +209,21 @@ impl BoardStep { &mut self.reactions, settings.iterations, ); + self.diagnostic_tick = self.diagnostic_tick.wrapping_add(1); + if self.diagnostic_capture && self.diagnostic_tick % 6 == 0 { + let deck = BodyId::Deck.index(); + self.diagnostic_snapshot = Some(format!( + "tick={} body={:?} hook={:?} reaction={:?} joints={:?} drives={:?}", + self.diagnostic_tick, bodies[deck], hook, self.reactions[deck], + constraints.joints.iter().enumerate() + .filter(|(_, j)| j.reaction_a == deck || j.reaction_b == deck) + .map(|(i, j)| (i, j.reaction_a, j.reaction_b, j.jacobian.words.map(f32::from_bits))) + .collect::>(), + constraints.drives.iter().enumerate() + .filter(|(_, d)| d.frame_a_body.reaction_index == deck || d.frame_b_body.reaction_index == deck) + .collect::>(), + )); + } // Every solver family finishes before ANY body integrates. The native // job tree places BatchIntegrator after all island solver jobs. for (body, reaction) in bodies diff --git a/crates/skate-game/src/physics/riding_outputs.rs b/crates/skate-game/src/physics/riding_outputs.rs index 6ebf3aaa..1faeb045 100644 --- a/crates/skate-game/src/physics/riding_outputs.rs +++ b/crates/skate-game/src/physics/riding_outputs.rs @@ -355,6 +355,10 @@ impl RidingOutputs { processed_flags_2468: u32, time_step: f32, ) -> Result<(), String> { + let capture = bevy::log::tracing::enabled!(target: "skate_game::riding_trace", bevy::log::tracing::Level::DEBUG); + if let Some(snapshot) = board.take_solver_diagnostics(capture) { + bevy::log::debug!(target: "skate_game::riding_trace", "RIDING_SOLVER {snapshot}"); + } self.ground.update( board.contact_reports(), &self.wheel_lines,