"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.
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.