From c938a4c1052ce507ca7b986f14098e2ab57c9594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lex=20Rom=C3=A1n=20N=C3=BA=C3=B1ez?= Date: Wed, 7 Oct 2026 17:43:19 +0200 Subject: [PATCH] Optimize rollback sync-test simulation --- Cargo.lock | 91 ++++++++++++++++++++++++++ Cargo.toml | 8 ++- src/fighter/baked_animation/runtime.rs | 47 +++++++------ src/fighter/mod.rs | 8 +-- 4 files changed, 127 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f50d95..510808b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -516,6 +516,7 @@ checksum = "4bfadbebfc6599aa59289b754ac30023959854304f3d393da2cf62bb3cd5df8f" dependencies = [ "bevy_dylib", "bevy_internal", + "tracing", ] [[package]] @@ -694,6 +695,7 @@ dependencies = [ "downcast-rs 2.0.2", "log", "thiserror 2.0.19", + "tracing", "variadics_please", "wasm-bindgen", "web-sys", @@ -1001,6 +1003,7 @@ dependencies = [ "slotmap", "smallvec", "thiserror 2.0.19", + "tracing", "variadics_please", ] @@ -1421,9 +1424,11 @@ dependencies = [ "bevy_platform", "bevy_utils", "tracing", + "tracing-error", "tracing-log", "tracing-oslog", "tracing-subscriber", + "tracing-tracy", "tracing-wasm", ] @@ -1733,9 +1738,11 @@ dependencies = [ "naga", "nonmax", "offset-allocator", + "profiling", "send_wrapper", "smallvec", "thiserror 2.0.19", + "tracy-client", "variadics_please", "wasm-bindgen", "weak-table", @@ -3463,6 +3470,21 @@ dependencies = [ "rusb", ] +[[package]] +name = "generator" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ade96dc9003043bce7c035c85a9df5a858bfb2039c5a2e6fdf00f324f6c551" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link", + "windows-result", +] + [[package]] name = "gethostname" version = "1.1.0" @@ -4381,6 +4403,19 @@ version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + [[package]] name = "mach2" version = "0.5.0" @@ -5629,6 +5664,20 @@ name = "profiling" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +dependencies = [ + "profiling-procmacros", + "tracing", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" +dependencies = [ + "quote", + "syn 2.0.119", +] [[package]] name = "pxfm" @@ -6829,6 +6878,16 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -6870,6 +6929,17 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tracing-tracy" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eaa1852afa96e0fe9e44caa53dc0bd2d9d05e0f2611ce09f97f8677af56e4ba" +dependencies = [ + "tracing-core", + "tracing-subscriber", + "tracy-client", +] + [[package]] name = "tracing-wasm" version = "0.2.1" @@ -6881,6 +6951,27 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "tracy-client" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6131992ff3e2cb96f407eb4eb005427ba15e2687260d99089b90e60e33169ce0" +dependencies = [ + "loom", + "once_cell", + "tracy-client-sys", +] + +[[package]] +name = "tracy-client-sys" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab27f167b093214c68413a2e0bcd318f0591af475597405ff68138ba0a433379" +dependencies = [ + "cc", + "windows-targets 0.52.6", +] + [[package]] name = "tree_magic_mini" version = "3.2.2" diff --git a/Cargo.toml b/Cargo.toml index 6de7f27..d891239 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ authors = [""] [features] default = ["development", "debug_features", "gamecube-adapter"] development = ["bevy/dynamic_linking", "bevy/file_watcher", "bevy/embedded_watcher", "hitbox-viewer"] +tracy = ["bevy/trace_tracy"] debug_features = ["bevy/free_camera"] gamecube-adapter = ["dep:gc-adapter-neo"] hitbox-viewer = ["bevy/bevy_dev_tools", "dep:egui_extras"] @@ -44,6 +45,11 @@ fixed_math = "0.4.1" [profile.dev.package."*"] opt-level = 3 +# The rollback simulation runs multiple times per rendered frame in sync-test +# mode, so optimizing the game crate is important even for development builds. +[profile.dev] +opt-level = 1 + [profile.production] inherits = "release" lto = "thin" @@ -56,4 +62,4 @@ gc-adapter-neo = { version = "1.1.1", features = ["libusb"], optional = true } [[bin]] name = "hitbox-editor" path = "src/bin/hitbox-editor.rs" -required-features = ["hitbox-viewer"] \ No newline at end of file +required-features = ["hitbox-viewer"] diff --git a/src/fighter/baked_animation/runtime.rs b/src/fighter/baked_animation/runtime.rs index c66d28b..1759206 100644 --- a/src/fighter/baked_animation/runtime.rs +++ b/src/fighter/baked_animation/runtime.rs @@ -127,6 +127,8 @@ pub struct FighterBoneMatrices { /// pose has the root translation removed. previous_root_motion_translation: Option, root_motion_delta: crate::math::vec3::FGVec3, + cached_root_motion_kind: Option, + cached_root_motion_cycle_delta: FGVec3, } #[derive(Component, Clone, Debug, Default)] @@ -366,12 +368,6 @@ impl FighterBoneMatrices { .expect("configured root motion bone should exist"), ) }; - self.model_matrices.resize(local.len(), None); - self.model_matrices.fill(None); - for index in 0..local.len() { - resolve_model_transform(index, parents, local, &mut self.model_matrices) - .expect("baked bone hierarchy should be valid"); - } let model_scale = FixedMat4 { cols: [ [model_scale, FGi32::ZERO, FGi32::ZERO, FGi32::ZERO], @@ -380,13 +376,16 @@ impl FighterBoneMatrices { [FGi32::ZERO, FGi32::ZERO, FGi32::ZERO, FGi32::ONE], ], }; + // Root motion only needs the configured bone's model transform. Resolve + // that ancestry first, then do the normal full-pose pass once below. + // Previously the complete hierarchy was resolved once before removing + // root translation and once afterwards. let raw_root_motion_translation = root_index.map(|root_index| { - model_scale - .mul( - self.model_matrices[root_index] - .expect("baked bone hierarchy should be valid"), - ) - .get_translation() + self.model_matrices.resize(local.len(), None); + self.model_matrices.fill(None); + let root = resolve_model_transform(root_index, parents, local, &mut self.model_matrices) + .expect("baked bone hierarchy should be valid"); + model_scale.mul(root).get_translation() }); // Gameplay owns the root's displacement. Keep rotation and scale in @@ -394,11 +393,12 @@ impl FighterBoneMatrices { // used by visuals and gameplay volumes. if let Some(root_index) = root_index { self.local_matrices[root_index].translate(FGVec3::ZERO); - self.model_matrices.fill(None); - for index in 0..local.len() { - resolve_model_transform(index, parents, &self.local_matrices, &mut self.model_matrices) - .expect("baked bone hierarchy should be valid"); - } + } + self.model_matrices.resize(local.len(), None); + self.model_matrices.fill(None); + for index in 0..local.len() { + resolve_model_transform(index, parents, &self.local_matrices, &mut self.model_matrices) + .expect("baked bone hierarchy should be valid"); } self.matrices.clear(); self.matrices.extend( @@ -517,9 +517,16 @@ fn sample_fighter_bone_matrices( let manifest = manifests .get(&fighter.manifest) .expect("fighter manifest should be loaded before the match starts"); - let root_motion_cycle_delta = baked - .root_motion_cycle_delta(frame.kind, &manifest.root_motion_bone, manifest.model_scale) - .unwrap_or(FGVec3::ZERO); + let root_motion_cycle_delta = if matrices.cached_root_motion_kind == Some(frame.kind) { + matrices.cached_root_motion_cycle_delta + } else { + let delta = baked + .root_motion_cycle_delta(frame.kind, &manifest.root_motion_bone, manifest.model_scale) + .unwrap_or(FGVec3::ZERO); + matrices.cached_root_motion_kind = Some(frame.kind); + matrices.cached_root_motion_cycle_delta = delta; + delta + }; let root_motion_loop_frame_count = baked .frame_count(frame.kind) .map(|count| count.saturating_sub(1).max(1)); diff --git a/src/fighter/mod.rs b/src/fighter/mod.rs index 922e0bc..3747de5 100644 --- a/src/fighter/mod.rs +++ b/src/fighter/mod.rs @@ -42,8 +42,8 @@ use crate::{ AttackKind, FighterAttackPlugin, FighterAttackScriptAssets, FighterDamage, FighterKnockbackVelocity, FighterPendingHitReaction, FighterSolvedHurtboxes, FighterStaleMoveQueue, - }, baked_animation::{FighterBoneIndexMap, FighterBoneMatrices}, ecb::FighterECBBoneIndices, manifest::FighterManifest, state::{FighterState, air::AirborneStateCommon, fall::FallState}, visual::{FighterAnimations, FighterInvulnerabilityVisualState}, - }, game_settings::GameSettings, input::FighterInput, math::{int::FGi32, vec::FGVec2}, player::Player, schedule::GameplaySet, scripting::FighterAttackScript, stage::line::StageCollision, + }, baked_animation::FighterBoneIndexMap, ecb::FighterECBBoneIndices, manifest::FighterManifest, state::{FighterState, air::AirborneStateCommon, fall::FallState}, visual::{FighterAnimations, FighterInvulnerabilityVisualState}, + }, game_settings::GameSettings, input::FighterInput, math::{int::FGi32, vec::FGVec2}, player::Player, schedule::GameplaySet, scripting::FighterAttackScript, }; use state::state_interrupt_system; @@ -250,7 +250,6 @@ impl Plugin for FighterPlugin { .checksum_component_with_hash::() .rollback_component_with_copy::() .checksum_component_with_hash::() - .rollback_resource_with_reflect::() // Debug views. .add_systems( FixedPostUpdate, @@ -284,11 +283,8 @@ impl Plugin for FighterPlugin { ) .rollback_component_with_clone::() .rollback_component_with_clone::() - .rollback_component_with_clone::() .rollback_component_with_clone::() .checksum_component_with_hash::() - .rollback_component_with_clone::() - .checksum_component_with_hash::() .checksum_component_with_hash::() .checksum_component_with_hash::() .register_type::(); -- 2.54.0