iOS/macOS 27 introduces the StateReporting API for annotating game state in Metal performance traces, alongside new look-back trace collection tools (metalperftrace CLI on macOS, Performance Trace button on iOS) that let you retrieve hours of historic performance data after a session ends.
⢠StateReporting lets you tag GPU traces with game state (current level, graphics preset, player position) so FPS drops are immediately contextualizable without guessing what the game was doing at that moment
⢠The look-back collection system records Metal metrics continuously in the background for days ā no need to start Instruments before a problem occurs, you can pull a trace after the fact
⢠metalperftrace CLI outputs JSON suitable for automated regression pipelines or AI triage agents, enabling headless performance monitoring in CI
Demonstrates how to create a StateReporting domain that tracks level transitions and volatile player metadata, making FPS drops in Instruments traces immediately actionable.
import Metal
import MetalStateReporting // part of Metal tooling umbrella in iOS 27
import simd
// MARK: - Domain & Reporter setup (do once at app launch)
final class GameStateReporter {
// Domain identifier: reverse-DNS string, unique to your app
private static let levelDomain = "com.mygame.state.level"
private static let graphicsDomain = "com.mygame.state.graphics"
private let levelReporter: MTLStateReporter
private let graphicsReporter: MTLStateReporter
init() {
levelReporter = MTLStateReporter(domain: GameStateReporter.levelDomain)
graphicsReporter = MTLStateReporter(domain: GameStateReporter.graphicsDomain)
}
// MARK: - Report a level transition with stable metadata
func enterLevel(id: Int, biome: String) {
// stableMetadata is immutable for the duration of this state
levelReporter.reportTransition(
label: "Level \(id)",
stableMetadata: [
"id": id,
"biome": biome
]
)
}
// MARK: - Update volatile metadata without a state transition
func updatePlayerPosition(_ position: SIMD3<Float>, health: Int) {
// Called every frame or on a timer; HUD shows latest value
levelReporter.reportVolatileMetadataUpdate([
"playerX": position.x,
"playerY": position.y,
"playerZ": position.z,
"health": health
])
}
// MARK: - Report graphics preset changes
func applyGraphicsPreset(_ preset: String) {
// e.g. "Low", "Medium", "High", "Ultra"
graphicsReporter.reportTransition(
label: preset,
stableMetadata: ["preset": preset]
)
}
}
// MARK: - Usage in your game loop / scene manager
let reporter = GameStateReporter()
// When the player loads Level 3 in a jungle biome:
reporter.enterLevel(id: 3, biome: "Jungle")
reporter.applyGraphicsPreset("High")
// Each frame (or via a 1 Hz timer) update volatile position:
let pos = SIMD3<Float>(12.5, 0.0, -44.3)
reporter.updatePlayerPosition(pos, health: 87)StateReporting domains are identified by reverse-DNS strings ā collisions across frameworks could produce confusing overlapping state machines. Volatile metadata updates are throttled to once per second in the HUD overlay. The metalperftrace CLI is macOS-only; iOS collection is via the Control Center button and requires the Performance Trace toggle to be pre-configured before the session you want to capture.
Look-back collection on iOS requires Developer Mode enabled and one-time setup in Developer Settings; no special Apple Silicon requirement
More iOS 27 APIs land every week.
Get notified when new capabilities are published ā no noise, just signal.