"FrameStats and PerformanceStats are accessible from game code: no engine benchmark system needed"
▸ SYMPTOM
You want to profile draw calls, triangle counts, GC pressure, or frame pacing from within your game code but assume you need a special engine benchmark system or editor-only API.
▸ CAUSE
The profiling surface is already exposed in the Sandbox.Diagnostics namespace, whitelist-clean, and accessible from any game-assembly Component running in play mode.
▸ FIX
Build a play-mode Component that reads from the available diagnostic APIs each frame:
Frame stats (Sandbox.Diagnostics.FrameStats.Current):
DrawCalls: number of draw calls this frameTrianglesRendered: total rendered trianglesObjectsRendered/ObjectsPreCull: visible vs pre-cull object countsShadowMaps/ShadowedLightsInView: shadow rendering load
Performance stats (Sandbox.Diagnostics.PerformanceStats):
Gen0Collections/Gen1Collections/Gen2Collections: GC countsBytesAllocated: allocation pressure
Frame pacing: RealTime.Delta gives per-frame wall time for frame-pacing analysis.
Physics timing: implement IScenePhysicsEvents and use PrePhysicsStep / PostPhysicsStep callbacks with a Stopwatch to time the actual physics solver step. This gives vsync-free CPU cost even when frame fps is capped at 60 in the editor.
Read each stat field inside try/catch so a future engine rename degrades a field to -1 instead of breaking the frame baseline.
Don't reach for the raw GC API for allocation telemetry. Confirmed SB1000-blocked on engine 26.07.22: GC.GetAllocatedBytesForCurrentThread() and GC.CollectionCount(int) both compile clean and pass a headless dotnet build, but fail the in-editor whitelist compiler with SB1000, the same trap family as Environment.TickCount / Array.Clone / reflection. Sandbox.Diagnostics.PerformanceStats (BytesAllocated, Gen0Collections/Gen1Collections/Gen2Collections, GcPause) is the whitelisted, supported surface for per-frame allocation/GC telemetry, so reach for it first when instrumenting game-assembly code.
What you still can't measure this way: the true GPU ceiling / absolute FPS. Editor play mode is vsync-capped at 60 Hz, so GPU cost below 16.7 ms is invisible. Take absolute numbers from a standalone build.
▸ WHY IT WORKS
These APIs are part of the Sandbox namespace, not the Editor namespace, so they pass the whitelist and run in the game assembly. They give relative numbers sufficient for profiling: frame-time trends, draw-call counts, GC pressure, physics cost. Combined, they cover everything except the absolute GPU ceiling, which requires a standalone build where the engine controls its own swap chain.
- Added the SB1000 whitelist trap: raw GC.GetAllocatedBytesForCurrentThread/GC.CollectionCount compile clean and pass headless build but fail the in-editor whitelist. Reach for PerformanceStats first. Re-verified on engine 26.07.22.
- Published