Optimize rollback sync-test performance (#86)
## Summary - remove immutable stage geometry and derived animation/collision data from rollback snapshots - avoid a duplicate full skeletal hierarchy traversal during root-motion sampling - cache root-motion cycle deltas per animation clip - optimize the game crate in development builds - make Tracy profiling opt-in via the `tracy` feature Reviewed-on: #86 Co-authored-by: Álex Román Núñez <eirexe@eirteam.moe>
This commit was merged in pull request #86.
This commit is contained in:
Generated
+91
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -127,6 +127,8 @@ pub struct FighterBoneMatrices {
|
||||
/// pose has the root translation removed.
|
||||
previous_root_motion_translation: Option<FGVec3>,
|
||||
root_motion_delta: crate::math::vec3::FGVec3,
|
||||
cached_root_motion_kind: Option<AnimKind>,
|
||||
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,12 +393,13 @@ 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.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(
|
||||
self.model_matrices.iter().map(|matrix| {
|
||||
@@ -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
|
||||
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));
|
||||
|
||||
+2
-6
@@ -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::<FighterInvulnerabilityState>()
|
||||
.rollback_component_with_copy::<FighterKnockbackVelocity>()
|
||||
.checksum_component_with_hash::<FighterKnockbackVelocity>()
|
||||
.rollback_resource_with_reflect::<StageCollision>()
|
||||
// Debug views.
|
||||
.add_systems(
|
||||
FixedPostUpdate,
|
||||
@@ -284,11 +283,8 @@ impl Plugin for FighterPlugin {
|
||||
)
|
||||
.rollback_component_with_clone::<FighterState>()
|
||||
.rollback_component_with_clone::<FighterHitboxes>()
|
||||
.rollback_component_with_clone::<FighterBoneMatrices>()
|
||||
.rollback_component_with_clone::<FighterHitstopShake>()
|
||||
.checksum_component_with_hash::<FighterHitboxes>()
|
||||
.rollback_component_with_clone::<FighterSolvedHurtboxes>()
|
||||
.checksum_component_with_hash::<FighterSolvedHurtboxes>()
|
||||
.checksum_component_with_hash::<FighterState>()
|
||||
.checksum_component_with_hash::<FighterFacingDirection>()
|
||||
.register_type::<FighterFacingDirection>();
|
||||
|
||||
Reference in New Issue
Block a user