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]
generate = "run --package tools --"
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"
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"

View File

@ -11,3 +11,10 @@ pub struct SysMetrics {
pub projectile_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::{
comp,
event::{EventBus, LocalEvent, ServerEvent},
metrics::SysMetrics,
metrics::{PhysicsMetrics, SysMetrics},
region::RegionMap,
sync::WorldSyncExt,
sys,
@ -183,6 +183,7 @@ impl State {
ecs.insert(comp::group::GroupManager::default());
ecs.insert(RegionMap::new());
ecs.insert(SysMetrics::default());
ecs.insert(PhysicsMetrics::default());
ecs
}

View File

@ -4,7 +4,7 @@ use crate::{
Shockwave, Sticky, Vel,
},
event::{EventBus, ServerEvent},
metrics::SysMetrics,
metrics::{PhysicsMetrics, SysMetrics},
span,
state::DeltaTime,
sync::Uid,
@ -12,7 +12,9 @@ use crate::{
vol::ReadVol,
};
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 vek::*;
@ -53,6 +55,7 @@ impl<'a> System<'a> for Sys {
ReadExpect<'a, TerrainGrid>,
Read<'a, DeltaTime>,
ReadExpect<'a, SysMetrics>,
WriteExpect<'a, PhysicsMetrics>,
Read<'a, EventBus<ServerEvent>>,
ReadStorage<'a, Scale>,
ReadStorage<'a, Sticky>,
@ -79,6 +82,7 @@ impl<'a> System<'a> for Sys {
terrain,
dt,
sys_metrics,
mut physics_metrics,
event_bus,
scales,
stickies,
@ -143,7 +147,7 @@ impl<'a> System<'a> for Sys {
.join()
{
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() {
old_velocities_times_dt.resize(id + 1, Vec3::zero());
}
@ -151,7 +155,7 @@ impl<'a> System<'a> for Sys {
}
drop(guard);
span!(guard, "Apply pushback");
for (entity, pos, vel, scale, mass, collider, _, _, physics, projectile) in (
let metrics = (
&entities,
&positions,
&mut velocities,
@ -165,120 +169,138 @@ impl<'a> System<'a> for Sys {
// should interact into the collider component or into a separate component
projectiles.maybe(),
)
.join()
.par_join()
.filter(|(_, _, _, _, _, _, _, sticky, physics, _)| {
sticky.is_none() || (physics.on_wall.is_none() && !physics.on_ground)
})
{
let scale = scale.map(|s| s.0).unwrap_or(1.0);
let radius = collider.map(|c| c.get_radius()).unwrap_or(0.5);
let z_limits = collider.map(|c| c.get_z_limits()).unwrap_or((-0.5, 0.5));
let mass = mass.map(|m| m.0).unwrap_or(scale);
let vel_dt = old_velocities_times_dt[entity.id() as usize];
.fold(PhysicsMetrics::default,
|mut metrics,(entity, pos, vel, scale, mass, collider, _, _, physics, projectile)| {
let scale = scale.map(|s| s.0).unwrap_or(1.0);
let radius = collider.map(|c| c.get_radius()).unwrap_or(0.5);
let z_limits = collider.map(|c| c.get_z_limits()).unwrap_or((-0.5, 0.5));
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
physics.touch_entities.clear();
// Resets touch_entities in physics
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 (
entity_other,
other,
pos_other,
scale_other,
mass_other,
collider_other,
_,
_,
_,
_,
) in (
&entities,
&uids,
&positions,
scales.maybe(),
masses.maybe(),
colliders.maybe(),
!&projectiles,
!&mountings,
!&beams,
!&shockwaves,
)
.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
for (
entity_other,
other,
pos_other,
scale_other,
mass_other,
collider_other,
_,
_,
_,
_,
) in (
&entities,
&uids,
&positions,
scales.maybe(),
masses.maybe(),
colliders.maybe(),
!&projectiles,
!&mountings,
!&beams,
!&shockwaves,
)
.join()
{
if !collided {
physics.touch_entities.push(*other);
if entity == entity_other {
continue;
}
// 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);
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);
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
vel.0 += vel_delta * dt.0;
}
// Change velocity
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);
// Apply movement inputs

View File

@ -59,7 +59,7 @@ use common::{
vol::{ReadVol, RectVolSize},
};
use futures_executor::block_on;
use metrics::{ServerMetrics, StateTickMetrics, TickMetrics};
use metrics::{PhysicsMetrics, ServerMetrics, StateTickMetrics, TickMetrics};
use network::{Network, Pid, ProtocolAddr};
use persistence::{
character_loader::{CharacterLoader, CharacterLoaderResponseType},
@ -108,6 +108,7 @@ pub struct Server {
metrics: ServerMetrics,
tick_metrics: TickMetrics,
state_tick_metrics: StateTickMetrics,
physics_metrics: PhysicsMetrics,
}
impl Server {
@ -327,12 +328,14 @@ impl Server {
let (tick_metrics, registry_tick) = TickMetrics::new(metrics.tick_clone())
.expect("Failed to initialize server tick metrics submodule.");
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_network(&metrics.registry()).expect("failed to register network request metrics");
registry_player(&metrics.registry()).expect("failed to register player metrics");
registry_tick(&metrics.registry()).expect("failed to register tick 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()
.name("veloren-worker".to_string())
@ -358,6 +361,7 @@ impl Server {
metrics,
tick_metrics,
state_tick_metrics,
physics_metrics,
};
debug!(?settings, "created veloren server with");
@ -777,6 +781,24 @@ impl Server {
.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
self.tick_metrics
.time_of_day

View File

@ -17,6 +17,12 @@ use tracing::{debug, 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 {
// Counter will only give us granularity on pool speed (2s?) for actuall spike detection we
// need the Historgram
@ -62,6 +68,44 @@ pub struct ServerMetrics {
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 {
pub fn new() -> Result<(Self, RegistryFn), prometheus::Error> {
let bucket = vec![