Profiler
UntoldEngine profiling has two layers that are meant to be used together:
- Structured metrics (stable numbers for trends and regressions)
- Category logs (on-demand narrative traces for deep debugging)
Use structured metrics as the source of truth, then enable category logs only when you need extra context.
Quick Start
Enable the profiler at runtime:
Or via environment variable:
Enable periodic frame stats logging:
Read profiler snapshots programmatically:
let metrics = EngineProfiler.shared.snapshot()
print("CPU mean: \(metrics.cpuFrame.meanMs) ms")
print("GPU mean: \(metrics.gpuCommandBuffer.meanMs) ms")
let frameStats = getEngineStatsSnapshot()
print("Frame: \(frameStats.frameIndex)")
print("Update: \(frameStats.timing.updateMs) ms")
print("Render: \(frameStats.timing.renderTotalMs) ms")
OOC And Asset Triage Mode
High-volume instrumentation categories are disabled by default:
OOCTimingOOCStatusAssetLoader
Enable them when diagnosing OOC/loader behavior:
// Keep structured profiler metrics on
enableEngineMetrics = true
setEngineStatsLogging(enabled: true, profile: .compact, intervalSeconds: 1.0)
// Add focused trace logs
Logger.enable(category: .oocStatus) // OutOfCore lifecycle/status
Logger.enable(category: .oocTiming) // OOC timing detail
Logger.enable(category: .assetLoader) // progressive loader parse/upload
Disable after capture:
Logger.disable(category: .oocTiming)
Logger.disable(category: .oocStatus)
Logger.disable(category: .assetLoader)
Instruments Workflow
When metrics are enabled, the engine emits signpost scopes:
FrameUpdateRenderPrepEncodeSubmit
To inspect timeline data:
- Open Instruments
- Choose Points of Interest
- Filter subsystem to
com.untoldengine.profiling - Run the app with
enableEngineMetrics = true(orUNTOLD_METRICS=1)
Build Configuration Notes
EngineProfileris available in all configs, but disabled by default until enabled at runtime.EngineStatscollection is compiled in debug by default (ENGINE_STATS_ENABLED).- For release profiling with
EngineStats, build with-DENGINE_STATS_ENABLED.
If ENGINE_STATS_ENABLED is not compiled in, getEngineStatsSnapshot() returns default values and setEngineStatsLogging(...) is effectively a no-op.
Category Toggle Notes
- Category filtering applies to
Logger.log(...)debug/info trace paths. - Warnings and errors still emit regardless of category state.
- Logger messages are lazily evaluated, so disabled categories avoid message-building cost.
Debug Helper (DEBUG Only)
#if DEBUG
let metricsLogger = MetricsDebugLogger()
metricsLogger.logIfNeeded() // throttle-prints approximately once per second
#endif
Integrated Systems
Profiler hooks are already integrated into:
UntoldEngine.swift(runFrame)RenderingSystem.swift(UpdateRenderingSystem)UntoldEngineXR.swift(executeXRSystemPass)UntoldEngineAR.swift(draw)