add metrics for physics sys and implement concurrent physics

This commit is contained in:
Marcel Märtens 2020-10-20 18:31:19 +02:00
parent bbe6e8be7c
commit ce3173ecdf
6 changed files with 203 additions and 107 deletions

View File

@ -6,8 +6,8 @@ rustflags = [
[alias] [alias]
generate = "run --package tools --" generate = "run --package tools --"
test-server = "-Zpackage-features run --bin veloren-server-cli --no-default-features" test-server = "-Zpackage-features run --bin veloren-server-cli --no-default-features"
tracy-server = "-Zunstable-options -Zpackage-features run --bin veloren-server-cli --no-default-features --features tracy --profile no_overflow" tracy-server = "-Zunstable-options -Zpackage-features run --bin veloren-server-cli --no-default-features --features tracy --profile releasedebuginfo"
test-voxygen = "-Zpackage-features run --bin veloren-voxygen --no-default-features --features gl" test-voxygen = "-Zpackage-features run --bin veloren-voxygen --no-default-features --features gl"
tracy-voxygen = "-Zunstable-options -Zpackage-features run --bin veloren-voxygen --no-default-features --features tracy,gl --profile no_overflow" tracy-voxygen = "-Zunstable-options -Zpackage-features run --bin veloren-voxygen --no-default-features --features tracy,gl --profile releasedebuginfo"
server = "run --bin veloren-server-cli" server = "run --bin veloren-server-cli"

View File

@ -11,3 +11,10 @@ pub struct SysMetrics {
pub projectile_ns: AtomicI64, pub projectile_ns: AtomicI64,
pub melee_ns: AtomicI64, pub melee_ns: AtomicI64,
} }
#[derive(Default)]
pub struct PhysicsMetrics {
pub velocities_cache_len: i64,
pub entity_entity_collision_checks: i64,
pub entity_entity_collisions: i64,
}

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
comp, comp,
event::{EventBus, LocalEvent, ServerEvent}, event::{EventBus, LocalEvent, ServerEvent},
metrics::SysMetrics, metrics::{PhysicsMetrics, SysMetrics},
region::RegionMap, region::RegionMap,
sync::WorldSyncExt, sync::WorldSyncExt,
sys, sys,
@ -183,6 +183,7 @@ impl State {
ecs.insert(comp::group::GroupManager::default()); ecs.insert(comp::group::GroupManager::default());
ecs.insert(RegionMap::new()); ecs.insert(RegionMap::new());
ecs.insert(SysMetrics::default()); ecs.insert(SysMetrics::default());
ecs.insert(PhysicsMetrics::default());
ecs ecs
} }

View File

@ -4,7 +4,7 @@ use crate::{
Shockwave, Sticky, Vel, Shockwave, Sticky, Vel,
}, },
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
metrics::SysMetrics, metrics::{PhysicsMetrics, SysMetrics},
span, span,
state::DeltaTime, state::DeltaTime,
sync::Uid, sync::Uid,
@ -12,7 +12,9 @@ use crate::{
vol::ReadVol, vol::ReadVol,
}; };
use rayon::iter::ParallelIterator; use rayon::iter::ParallelIterator;
use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage, System, WriteStorage}; use specs::{
Entities, Join, ParJoin, Read, ReadExpect, ReadStorage, System, WriteExpect, WriteStorage,
};
use std::ops::Range; use std::ops::Range;
use vek::*; use vek::*;
@ -53,6 +55,7 @@ impl<'a> System<'a> for Sys {
ReadExpect<'a, TerrainGrid>, ReadExpect<'a, TerrainGrid>,
Read<'a, DeltaTime>, Read<'a, DeltaTime>,
ReadExpect<'a, SysMetrics>, ReadExpect<'a, SysMetrics>,
WriteExpect<'a, PhysicsMetrics>,
Read<'a, EventBus<ServerEvent>>, Read<'a, EventBus<ServerEvent>>,
ReadStorage<'a, Scale>, ReadStorage<'a, Scale>,
ReadStorage<'a, Sticky>, ReadStorage<'a, Sticky>,
@ -79,6 +82,7 @@ impl<'a> System<'a> for Sys {
terrain, terrain,
dt, dt,
sys_metrics, sys_metrics,
mut physics_metrics,
event_bus, event_bus,
scales, scales,
stickies, stickies,
@ -143,7 +147,7 @@ impl<'a> System<'a> for Sys {
.join() .join()
{ {
let id = entity.id() as usize; let id = entity.id() as usize;
let vel_dt = vel.0.clone() * dt.0; let vel_dt = vel.0 * dt.0;
if id >= old_velocities_times_dt.len() { if id >= old_velocities_times_dt.len() {
old_velocities_times_dt.resize(id + 1, Vec3::zero()); old_velocities_times_dt.resize(id + 1, Vec3::zero());
} }
@ -151,7 +155,7 @@ impl<'a> System<'a> for Sys {
} }
drop(guard); drop(guard);
span!(guard, "Apply pushback"); span!(guard, "Apply pushback");
for (entity, pos, vel, scale, mass, collider, _, _, physics, projectile) in ( let metrics = (
&entities, &entities,
&positions, &positions,
&mut velocities, &mut velocities,
@ -165,120 +169,138 @@ impl<'a> System<'a> for Sys {
// should interact into the collider component or into a separate component // should interact into the collider component or into a separate component
projectiles.maybe(), projectiles.maybe(),
) )
.join() .par_join()
.filter(|(_, _, _, _, _, _, _, sticky, physics, _)| { .filter(|(_, _, _, _, _, _, _, sticky, physics, _)| {
sticky.is_none() || (physics.on_wall.is_none() && !physics.on_ground) sticky.is_none() || (physics.on_wall.is_none() && !physics.on_ground)
}) })
{ .fold(PhysicsMetrics::default,
let scale = scale.map(|s| s.0).unwrap_or(1.0); |mut metrics,(entity, pos, vel, scale, mass, collider, _, _, physics, projectile)| {
let radius = collider.map(|c| c.get_radius()).unwrap_or(0.5); let scale = scale.map(|s| s.0).unwrap_or(1.0);
let z_limits = collider.map(|c| c.get_z_limits()).unwrap_or((-0.5, 0.5)); let radius = collider.map(|c| c.get_radius()).unwrap_or(0.5);
let mass = mass.map(|m| m.0).unwrap_or(scale); let z_limits = collider.map(|c| c.get_z_limits()).unwrap_or((-0.5, 0.5));
let vel_dt = old_velocities_times_dt[entity.id() as usize]; let mass = mass.map(|m| m.0).unwrap_or(scale);
let vel_dt = old_velocities_times_dt[entity.id() as usize];
// Resets touch_entities in physics // Resets touch_entities in physics
physics.touch_entities.clear(); physics.touch_entities.clear();
let is_projectile = projectile.is_some(); let is_projectile = projectile.is_some();
let mut vel_delta = Vec3::zero(); let mut vel_delta = Vec3::zero();
for ( for (
entity_other, entity_other,
other, other,
pos_other, pos_other,
scale_other, scale_other,
mass_other, mass_other,
collider_other, collider_other,
_, _,
_, _,
_, _,
_, _,
) in ( ) in (
&entities, &entities,
&uids, &uids,
&positions, &positions,
scales.maybe(), scales.maybe(),
masses.maybe(), masses.maybe(),
colliders.maybe(), colliders.maybe(),
!&projectiles, !&projectiles,
!&mountings, !&mountings,
!&beams, !&beams,
!&shockwaves, !&shockwaves,
) )
.join() .join()
{
if entity == entity_other {
continue;
}
let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0);
let radius_other = collider_other.map(|c| c.get_radius()).unwrap_or(0.5);
let collision_dist = scale * radius + scale_other * radius_other;
let collision_dist_sqr = collision_dist * collision_dist;
let vel_dt_other = old_velocities_times_dt[entity_other.id() as usize];
let pos_diff_squared = (pos.0 - pos_other.0).magnitude_squared();
let vel_diff_squared = (vel_dt - vel_dt_other).magnitude_squared();
// Sanity check: don't try colliding entities that are too far from each other
// Note: I think this catches all cases. If you get entity collision problems,
// try removing this!
if pos_diff_squared > vel_diff_squared + collision_dist_sqr {
continue;
}
let z_limits_other = collider_other
.map(|c| c.get_z_limits())
.unwrap_or((-0.5, 0.5));
let mass_other = mass_other.map(|m| m.0).unwrap_or(scale_other);
//This check after the pos check, as we currently don't have that many massless
// entites [citation needed]
if mass_other == 0.0 {
continue;
}
const MIN_COLLISION_DIST: f32 = 0.3;
let increments = (vel_diff_squared.sqrt() / MIN_COLLISION_DIST)
.max(1.0)
.ceil() as usize;
let step_delta = 1.0 / increments as f32;
let mut collided = false;
for i in 0..increments {
let factor = i as f32 * step_delta;
let pos = pos.0 + vel_dt * factor;
let pos_other = pos_other.0 + vel_dt_other * factor;
let diff = pos.xy() - pos_other.xy();
if diff.magnitude_squared() <= collision_dist_sqr
&& pos.z + z_limits.1 * scale
>= pos_other.z + z_limits_other.0 * scale_other
&& pos.z + z_limits.0 * scale
<= pos_other.z + z_limits_other.1 * scale_other
{ {
if !collided { if entity == entity_other {
physics.touch_entities.push(*other); continue;
} }
// Don't apply repulsive force to projectiles let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0);
if diff.magnitude_squared() > 0.0 && !is_projectile { let radius_other = collider_other.map(|c| c.get_radius()).unwrap_or(0.5);
let force = 400.0 * (collision_dist - diff.magnitude()) * mass_other
/ (mass + mass_other);
vel_delta += Vec3::from(diff.normalized()) * force * step_delta; let collision_dist = scale * radius + scale_other * radius_other;
let collision_dist_sqr = collision_dist * collision_dist;
let vel_dt_other = old_velocities_times_dt[entity_other.id() as usize];
let pos_diff_squared = (pos.0 - pos_other.0).magnitude_squared();
let vel_diff_squared = (vel_dt - vel_dt_other).magnitude_squared();
// Sanity check: don't try colliding entities that are too far from each
// other Note: I think this catches all cases. If
// you get entity collision problems, try removing
// this!
if pos_diff_squared > vel_diff_squared + collision_dist_sqr {
continue;
} }
collided = true; let z_limits_other = collider_other
.map(|c| c.get_z_limits())
.unwrap_or((-0.5, 0.5));
let mass_other = mass_other.map(|m| m.0).unwrap_or(scale_other);
//This check after the pos check, as we currently don't have that many
// massless entites [citation needed]
if mass_other == 0.0 {
continue;
}
metrics.entity_entity_collision_checks += 1;
const MIN_COLLISION_DIST: f32 = 0.3;
let increments = (vel_diff_squared.sqrt() / MIN_COLLISION_DIST)
.max(1.0)
.ceil() as usize;
let step_delta = 1.0 / increments as f32;
let mut collided = false;
for i in 0..increments {
let factor = i as f32 * step_delta;
let pos = pos.0 + vel_dt * factor;
let pos_other = pos_other.0 + vel_dt_other * factor;
let diff = pos.xy() - pos_other.xy();
if diff.magnitude_squared() <= collision_dist_sqr
&& pos.z + z_limits.1 * scale
>= pos_other.z + z_limits_other.0 * scale_other
&& pos.z + z_limits.0 * scale
<= pos_other.z + z_limits_other.1 * scale_other
{
if !collided {
physics.touch_entities.push(*other);
metrics.entity_entity_collisions += 1;
}
// Don't apply repulsive force to projectiles
if diff.magnitude_squared() > 0.0 && !is_projectile {
let force =
400.0 * (collision_dist - diff.magnitude()) * mass_other
/ (mass + mass_other);
vel_delta += Vec3::from(diff.normalized()) * force * step_delta;
}
collided = true;
}
}
} }
}
}
// Change velocity // Change velocity
vel.0 += vel_delta * dt.0; vel.0 += vel_delta * dt.0;
} metrics
},
)
.reduce(PhysicsMetrics::default, |old, new| {
PhysicsMetrics {
velocities_cache_len: 0,
entity_entity_collision_checks: old.entity_entity_collision_checks + new.entity_entity_collision_checks,
entity_entity_collisions: old.entity_entity_collisions + new.entity_entity_collisions,
}
});
physics_metrics.velocities_cache_len = old_velocities_times_dt.len() as i64;
physics_metrics.entity_entity_collision_checks = metrics.entity_entity_collision_checks;
physics_metrics.entity_entity_collisions = metrics.entity_entity_collisions;
drop(guard); drop(guard);
// Apply movement inputs // Apply movement inputs

View File

@ -59,7 +59,7 @@ use common::{
vol::{ReadVol, RectVolSize}, vol::{ReadVol, RectVolSize},
}; };
use futures_executor::block_on; use futures_executor::block_on;
use metrics::{ServerMetrics, StateTickMetrics, TickMetrics}; use metrics::{PhysicsMetrics, ServerMetrics, StateTickMetrics, TickMetrics};
use network::{Network, Pid, ProtocolAddr}; use network::{Network, Pid, ProtocolAddr};
use persistence::{ use persistence::{
character_loader::{CharacterLoader, CharacterLoaderResponseType}, character_loader::{CharacterLoader, CharacterLoaderResponseType},
@ -108,6 +108,7 @@ pub struct Server {
metrics: ServerMetrics, metrics: ServerMetrics,
tick_metrics: TickMetrics, tick_metrics: TickMetrics,
state_tick_metrics: StateTickMetrics, state_tick_metrics: StateTickMetrics,
physics_metrics: PhysicsMetrics,
} }
impl Server { impl Server {
@ -327,12 +328,14 @@ impl Server {
let (tick_metrics, registry_tick) = TickMetrics::new(metrics.tick_clone()) let (tick_metrics, registry_tick) = TickMetrics::new(metrics.tick_clone())
.expect("Failed to initialize server tick metrics submodule."); .expect("Failed to initialize server tick metrics submodule.");
let (state_tick_metrics, registry_state) = StateTickMetrics::new().unwrap(); let (state_tick_metrics, registry_state) = StateTickMetrics::new().unwrap();
let (physics_metrics, registry_physics) = PhysicsMetrics::new().unwrap();
registry_chunk(&metrics.registry()).expect("failed to register chunk gen metrics"); registry_chunk(&metrics.registry()).expect("failed to register chunk gen metrics");
registry_network(&metrics.registry()).expect("failed to register network request metrics"); registry_network(&metrics.registry()).expect("failed to register network request metrics");
registry_player(&metrics.registry()).expect("failed to register player metrics"); registry_player(&metrics.registry()).expect("failed to register player metrics");
registry_tick(&metrics.registry()).expect("failed to register tick metrics"); registry_tick(&metrics.registry()).expect("failed to register tick metrics");
registry_state(&metrics.registry()).expect("failed to register state metrics"); registry_state(&metrics.registry()).expect("failed to register state metrics");
registry_physics(&metrics.registry()).expect("failed to register state metrics");
let thread_pool = ThreadPoolBuilder::new() let thread_pool = ThreadPoolBuilder::new()
.name("veloren-worker".to_string()) .name("veloren-worker".to_string())
@ -358,6 +361,7 @@ impl Server {
metrics, metrics,
tick_metrics, tick_metrics,
state_tick_metrics, state_tick_metrics,
physics_metrics,
}; };
debug!(?settings, "created veloren server with"); debug!(?settings, "created veloren server with");
@ -777,6 +781,24 @@ impl Server {
.observe(melee_ns as f64 / NANOSEC_PER_SEC); .observe(melee_ns as f64 / NANOSEC_PER_SEC);
} }
//detailed physics metrics
{
let res = self
.state
.ecs()
.read_resource::<common::metrics::PhysicsMetrics>();
self.physics_metrics
.velocities_cache_len
.set(res.velocities_cache_len);
self.physics_metrics
.entity_entity_collision_checks_count
.inc_by(res.entity_entity_collision_checks);
self.physics_metrics
.entity_entity_collisions_count
.inc_by(res.entity_entity_collisions);
}
// Report other info // Report other info
self.tick_metrics self.tick_metrics
.time_of_day .time_of_day

View File

@ -17,6 +17,12 @@ use tracing::{debug, error};
type RegistryFn = Box<dyn FnOnce(&Registry) -> Result<(), prometheus::Error>>; type RegistryFn = Box<dyn FnOnce(&Registry) -> Result<(), prometheus::Error>>;
pub struct PhysicsMetrics {
pub velocities_cache_len: IntGauge,
pub entity_entity_collision_checks_count: IntCounter,
pub entity_entity_collisions_count: IntCounter,
}
pub struct StateTickMetrics { pub struct StateTickMetrics {
// Counter will only give us granularity on pool speed (2s?) for actuall spike detection we // Counter will only give us granularity on pool speed (2s?) for actuall spike detection we
// need the Historgram // need the Historgram
@ -62,6 +68,44 @@ pub struct ServerMetrics {
tick: Arc<AtomicU64>, tick: Arc<AtomicU64>,
} }
impl PhysicsMetrics {
pub fn new() -> Result<(Self, RegistryFn), prometheus::Error> {
let velocities_cache_len = IntGauge::with_opts(Opts::new(
"velocities_cache_len",
"shows the size of the velocities_cache in entries",
))?;
let entity_entity_collision_checks_count = IntCounter::with_opts(Opts::new(
"entity_entity_collision_checks_count",
"shows the number of collision checks",
))?;
let entity_entity_collisions_count = IntCounter::with_opts(Opts::new(
"entity_entity_collisions_count",
"shows the number of actual collisions detected",
))?;
let velocities_cache_len_clone = velocities_cache_len.clone();
let entity_entity_collision_checks_count_clone =
entity_entity_collision_checks_count.clone();
let entity_entity_collisions_count_clone = entity_entity_collisions_count.clone();
let f = |registry: &Registry| {
registry.register(Box::new(velocities_cache_len_clone))?;
registry.register(Box::new(entity_entity_collision_checks_count_clone))?;
registry.register(Box::new(entity_entity_collisions_count_clone))?;
Ok(())
};
Ok((
Self {
velocities_cache_len,
entity_entity_collision_checks_count,
entity_entity_collisions_count,
},
Box::new(f),
))
}
}
impl StateTickMetrics { impl StateTickMetrics {
pub fn new() -> Result<(Self, RegistryFn), prometheus::Error> { pub fn new() -> Result<(Self, RegistryFn), prometheus::Error> {
let bucket = vec![ let bucket = vec![