Files
sbox-public/engine/Sandbox.Engine/Systems/Render/Debug/DebugOverlay.cs
Lorenz Junglas 7ba7fa6054 Fix vsync micro stutter and latency when fps_max is set above monitor refresh rate (#5185)
* Add frametime overlay (overlay_fps)

* Fix frame pacing under vsync, regressed by f082791c46

With vsync on and fps_max above the refresh we run ahead of the present queue and stall on it, so frametimes jump around and it stutters even though the average fps looks fine. Clamp the cap to the refresh and pace frames on a fixed grid instead.

Basically restores the refresh clamp and drift compensation f082791c46 removed when it moved fps_max to C#.

Before: https://files.facepunch.com/lolleko/2026/June/26_15-25-SquigglyHuia.mp4
After: https://files.facepunch.com/lolleko/2026/June/26_15-25-DotingAmericanblackvulture.mp4

* Bump default menu refresh rate to 120
2026-06-29 10:16:03 +02:00

141 lines
3.7 KiB
C#

using Sandbox.Rendering;
namespace Sandbox;
internal static partial class DebugOverlay
{
static CommandList _overlay = new( "Engine Overlay" );
private const float OverlaySpacing = 16f;
public static CommandList CommandList => _overlay;
public static HudPainter Hud => new HudPainter( CommandList );
public static void Reset()
{
_overlay.Reset();
}
public static void Render()
{
_overlay.ExecuteOnRenderThread();
}
[ConVar( "overlay_profile", Help = "Draws an overlay showing timings from the main profile categories" )]
internal static int overlay_profile { get; set; } = 0;
[ConVar( "overlay_alloc", Help = "Draws an overlay showing allocations and garbage collection" )]
internal static int overlay_alloc { get; set; } = 0;
[ConVar( "overlay_frame", Help = "Render frame stats overlay. 0=off, 1=essentials (timing/geometry/lights/memory), 2=+batching/culling/material changes, 3=+GPU resources" )]
internal static int overlay_frame { get; set; } = 0;
[ConVar( "overlay_network_graph", Help = "Draws an overlay showing a network usage summary" )]
internal static int overlay_network_graph { get; set; } = 0;
[ConVar( "overlay_fps", Help = "Draws an overlay graphing frame-time pacing: a live frametime strip and a distribution histogram" )]
internal static int overlay_fps { get; set; } = 0;
[ConVar( "overlay_network_calls", Help = "Draws an overlay showing most received network calls" )]
internal static int overlay_network_calls { get; set; } = 0;
[ConVar( "overlay_pp", Help = "Draws an overlay showing current post process stack" )]
internal static int overlay_pp { get; set; } = 0;
[ConVar( "overlay_gpu", Help = "Draws an overlay showing GPU timing for render passes" )]
internal static int overlay_gpu { get; set; } = 0;
[ConVar( "overlay_resources", Help = "Draws an overlay showing registered resources and native cache" )]
internal static int overlay_resources { get; set; } = 0;
public static void Draw()
{
Vector2 pos = new Vector2( 64, 64 );
var activeScene = Application.GetActiveScene();
// Show current render debug mode on screen when not default
if ( ToolsVisualization.mat_toolsvis != SceneCameraDebugMode.Normal )
{
DebugOverlay.ToolsVisualization.Draw( ref pos );
pos.y += OverlaySpacing;
}
if ( overlay_network_calls == 1 )
{
DebugOverlay.NetworkCalls.Draw( ref pos );
pos.y += OverlaySpacing;
}
if ( overlay_network_graph == 1 )
{
DebugOverlay.NetworkGraph.Draw( ref pos );
pos.y += OverlaySpacing;
}
if ( overlay_fps == 1 )
{
DebugOverlay.FrameTimeGraph.Draw( ref pos );
pos.y += OverlaySpacing;
}
if ( overlay_profile == 1 )
{
DebugOverlay.Profiler.Draw( ref pos );
pos.y += OverlaySpacing;
}
if ( overlay_frame > 0 )
{
DebugOverlay.Frame.Draw( ref pos, overlay_frame );
pos.y += OverlaySpacing;
}
if ( overlay_pp == 1 )
{
activeScene?.Camera?.PrintPostProcessDebugOverlay( ref pos, Hud );
pos.y += OverlaySpacing;
}
if ( overlay_alloc == 1 )
{
DebugOverlay.Allocations.Draw( ref pos );
pos.y += OverlaySpacing;
}
else
{
DebugOverlay.Allocations.Disabled();
}
// GPU Profiler
Diagnostics.GpuProfilerStats.Enabled = overlay_gpu == 1;
Diagnostics.GpuProfilerStats.Update();
if ( overlay_gpu == 1 )
{
DebugOverlay.GpuProfiler.Draw( ref pos );
pos.y += OverlaySpacing;
}
if ( overlay_resources == 1 )
{
DebugOverlay.Resources.Draw( ref pos );
pos.y += OverlaySpacing;
}
if ( overlay_ui == 1 )
{
DebugOverlay.UI.Draw( ref pos );
pos.y += OverlaySpacing;
}
if ( overlay_audio != 0 )
{
DebugOverlay.Audio.Draw( ref pos );
pos.y += OverlaySpacing;
}
if ( ShadowMapper.DebugEnabled )
ShadowMapper.Draw( ref pos, Hud );
}
}