| id | profiler |
|---|---|
| title | Profiler |
| sidebar_position | 20 |
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.
Enable the profiler at runtime:
enableEngineMetrics = trueOr via environment variable:
export UNTOLD_METRICS=1
./YourAppEnable periodic frame stats logging:
setEngineStatsLogging(
enabled: true,
profile: .compact, // or .verbose
intervalSeconds: 1.0
)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")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/uploadDisable after capture:
Logger.disable(category: .oocTiming)
Logger.disable(category: .oocStatus)
Logger.disable(category: .assetLoader)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)
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 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.
#if DEBUG
let metricsLogger = MetricsDebugLogger()
metricsLogger.logIfNeeded() // throttle-prints approximately once per second
#endifProfiler hooks are already integrated into:
UntoldEngine.swift(runFrame)RenderingSystem.swift(UpdateRenderingSystem)UntoldEngineXR.swift(executeXRSystemPass)UntoldEngineAR.swift(draw)