mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-04-20 06:19:05 -04:00
Fixes timing scopes to more accurately represent a per-frame main thread breakdown, and prevents spikes when GC is executed. - **GcPause** - New separate timing scope showing time spent in GC per frame - GC pause time is subtracted from all other scopes, so each scope now only tracks its own code execution and no longer includes GC overhead - e.g. when GC occurs during the audio scope, the audio scope no longer spikes to 20ms - **AudioMixingThread** removed from the main scopes - Runs on a separate thread, so its timings are effectively meaningless in the main thread view - All other scopes are main thread only - No longer relevant given the audio optimisation work done over the past months - **Scene** scope removed - Didn't make much sense as it was an aggregate wrapping many other timing scopes - Replaced with a finer `Update` scope that tracks `Component.FixedUpdate`/`Update` - **Editor** scope no longer shows in-game - Scopes reschuffled - e.g. verlet rope physics traces are now tracked under the physics scope - Audio occlusion queries are now tracked under the audio scope https://files.facepunch.com/lolleko/2026/March/02_12-59-QuixoticMarten.png
114 lines
3.3 KiB
C#
114 lines
3.3 KiB
C#
namespace Sandbox;
|
|
|
|
/// <summary>
|
|
/// This system exists to collect pending collision sounds and filter them into a unique set, to avoid
|
|
/// unnesssary sounds playing, when they're going to be making the same sound anyway.
|
|
/// </summary>
|
|
[Expose]
|
|
public sealed class CollisionSoundSystem : GameObjectSystem<CollisionSoundSystem>, ISceneCollisionEvents
|
|
{
|
|
record struct PendingSound( Surface Surface, Vector3 Position, float Speed, bool Networked );
|
|
|
|
private readonly List<PendingSound> Pending = [];
|
|
|
|
public CollisionSoundSystem( Scene scene ) : base( scene )
|
|
{
|
|
Listen( Stage.FinishUpdate, 100, ProcessQueue, "CollisionSoundSystem Queue" );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register this physics collision with the sound system
|
|
/// </summary>
|
|
public void RegisterCollision( in Collision collision )
|
|
{
|
|
var self = collision.Self;
|
|
var other = collision.Other;
|
|
|
|
if ( !self.Body.EnableCollisionSounds ) return;
|
|
if ( !other.Body.EnableCollisionSounds ) return;
|
|
|
|
// Assume networked if colliding with an object replicated to clients
|
|
var networkRoot = other.GameObject?.NetworkRoot;
|
|
var networked = networkRoot is { NetworkMode: NetworkMode.Object } && networkRoot.Network?.IsProxy == false;
|
|
|
|
AddShapeCollision( other.Shape, other.Surface, collision.Contact, networked );
|
|
AddShapeCollision( self.Shape, self.Surface, collision.Contact, networked );
|
|
}
|
|
|
|
void ISceneCollisionEvents.OnCollisionHit( Collision collision )
|
|
{
|
|
RegisterCollision( collision );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a collision sound for this shape
|
|
/// </summary>
|
|
public void AddShapeCollision( PhysicsShape shape, Surface surface, in Vector3 position, float speed, bool networked )
|
|
{
|
|
if ( !shape.IsValid() ) return;
|
|
if ( !shape.Body.IsValid() ) return;
|
|
if ( !shape.Body.EnableCollisionSounds ) return;
|
|
if ( speed < 100.0f ) return;
|
|
if ( surface is null ) return;
|
|
|
|
// If we have more than 4, remove any that are slower/less significant
|
|
if ( Pending.Count > 4 && Pending.RemoveAll( x => x.Speed < speed ) == 0 )
|
|
return;
|
|
|
|
// check for redundancies
|
|
for ( int i = 0; i < Pending.Count; i++ )
|
|
{
|
|
if ( Pending[i].Surface.Index != surface.Index ) continue;
|
|
if ( Pending[i].Speed >= speed ) return;
|
|
|
|
Pending[i] = new PendingSound( surface, position, speed, networked );
|
|
return;
|
|
}
|
|
|
|
Pending.Add( new PendingSound( surface, position, speed, networked ) );
|
|
}
|
|
|
|
public void AddShapeCollision( PhysicsShape shape, Surface surface, in PhysicsContact contact, bool networked )
|
|
{
|
|
AddShapeCollision( shape, surface, contact.Point, MathF.Abs( contact.NormalSpeed ), networked );
|
|
}
|
|
|
|
RealTimeSince lastRan = 0;
|
|
|
|
/// <summary>
|
|
/// Create the pending sounds
|
|
/// </summary>
|
|
void ProcessQueue()
|
|
{
|
|
using var _ = PerformanceStats.Timings.Audio.Scope();
|
|
|
|
if ( lastRan < 0.05f ) return;
|
|
lastRan = 0;
|
|
|
|
foreach ( var pending in Pending )
|
|
{
|
|
if ( pending.Surface is null ) continue;
|
|
|
|
if ( pending.Networked )
|
|
{
|
|
PlayCollisionSound( (ushort)pending.Surface.Index, pending.Position, pending.Speed );
|
|
}
|
|
else
|
|
{
|
|
pending.Surface.PlayCollisionSound( pending.Position, pending.Speed );
|
|
}
|
|
}
|
|
|
|
Pending.Clear();
|
|
}
|
|
|
|
[Rpc.Broadcast( NetFlags.Unreliable )]
|
|
private void PlayCollisionSound( ushort index, Vector3 pos, float speed )
|
|
{
|
|
var surface = Surface.FindByIndex( index );
|
|
if ( surface == null ) return;
|
|
|
|
surface.PlayCollisionSound( pos, speed );
|
|
}
|
|
}
|