At the end of the previous article we left a question: what exactly still separates a AAA game from running on mobile devices? The common answer is “performance optimization”, but that phrase is too vague to schedule work against. Taken apart, the gap is three ledgers: a budget ledger that fixes what each frame may spend; a scheduling ledger that fixes how a world far larger than memory flows in and out; and a cadence ledger that fixes the intervals at which frames reach the player’s eyes.

In a AAA studio these three ledgers are kept by dedicated teams: the engine group sets budgets, streaming and memory have their own keepers, a performance group runs regressions, and technical artists translate specifications for the content team. What we call “AAA quality” is, in large part, the output of this invisible organization — and it is precisely the part a small team cannot afford to hire. The mechanisms — render pipelines, virtualized geometry, open-world streaming — can all be bought from an engine today; what cannot be bought is the discipline that enforces the three ledgers. This article first lays the ledgers out, then answers the question from last time — knowing the target platform, how should art asset specifications and code structure be organized — and why this is moving from “highly paid engineering teams” to “infrastructure”.

Ledger 01 · The per-frame budget: accounting in many currencies

The surface currency of a frame budget is time: a frame is 16.6 ms at 60 fps, 8.3 ms at 120 Hz, 33.3 ms at 30 fps. But time is only the settlement unit. What actually gets spent are four other currencies: CPU milliseconds, GPU milliseconds, bandwidth and power — and on mobile, the last two are the hard walls.

ConstraintDesktop workstationFlagship phone
Memory bandwidthDedicated, on the order of 1 TB/sTens of GB/s, shared with the CPU
Power ceilingHundreds of watts for one GPUSingle-digit watts for the whole device, plus a thermal wall
MemoryPhysical RAM mostly usableThe OS kills processes well below physical RAM
Sustained performance≈ peak performancePeak holds for minutes; budget against sustained clocks

This table explains why mobile GPUs are overwhelmingly tile-based: keep a small tile’s intermediate results in on-chip memory and finish there, because a round trip to main memory costs bandwidth and battery at once. It also sets the first lesson of porting — shipping the same assets straight down mails the desktop’s bandwidth bill to a phone: every extra texture sample, every extra load/store on a render target, every mid-pass resolve spends both of the scarcest currencies at once. And heat rewrites the denominator: 16.6 ms at benchmark clocks does not count; 16.6 ms at clocks the device can hold through forty minutes of play does.

For a budget to be enforceable it must be split into line items. “This frame gets 16.6 ms” is a wish; “shadows 1.5, opaque pass 4.5, lighting 2.5, transparents and FX 2.0, post 2.0, UI 0.8, streaming and misc 1.0, headroom 2.3” is a contract — an overrun points at a line item immediately, instead of “the game got slow”. The CPU side is the same: game thread, render submission and the worker pool each keep their own books.

GPU frame-budget ledger (60 fps mid-tier phone · illustrative) 0 8.3 16.6 ms Shadow 1.5 Opaque pass 4.5 Lighting 2.5 Transparents 2.0 Post FX 2.0 UI 0.8 Stream 1.0 Headroom 2.3 Overrun cost is not linear vsync 0 16.6 33.3 ms this frame took 17.1 ms idle until the next vsync slot 0.5 ms over → perceived 30 fps
Figure 1 · The frame ledger and vsync quantization. Top: a budget becomes enforceable only when split into line items — headroom is itself a line item. Bottom: vertical sync slices time into 16.6 ms slots, and a frame 0.5 ms over takes two of them. "60 fps" really means "every frame under 16.6 ms", so the budget must carry its own headroom.

How a budget exists decides whether it works. A budget on a wiki is remembered at review time, sometimes; an effective budget lives in the pipeline — assets validated on import, capture data reconciled against the ledger line by line, overruns blocked before merge. This is where the article returns at the end.

Ledger 02 · World streaming: the scheduler that follows the camera

An open world presumes the world is larger than memory, so “which assets are in memory right now” becomes a question answered anew every frame. The answer follows the camera: the world is cut into cells, the distance is stood in for by HLODs, and a resident set is maintained around the viewpoint. It sounds plain; in engineering terms it is a real scheduler with its own priority function and per-frame budgets.

Priority is not distance alone. Velocity joins the prediction — the prefetch ring should shift along the movement vector, because where the player is heading at speed deserves the IO more than what is behind them; visibility joins the ordering — cells behind a wall can arrive late; gameplay gets to cut the line — teleport targets and cinematic destinations should warm up before they trigger. The scheduler’s currencies tie straight back into the frame budget: IO requests in flight, decompress milliseconds per frame, upload megabytes per frame, and the resident pool’s watermarks. Big files are split across frames; eviction carries hysteresis, or the pool oscillates between evict and reload right at the waterline.

The resident set follows the camera velocity Resident Prefetch (velocity-biased) Evictable Per-frame streaming budget priority = distance · velocity · visibility · gameplay hints IO queue in-flight ≤ 64 Decompress ≤ 2 ms/frame Upload ≤ 16 MB/frame Resident pool high / low watermark evict (hysteresis) freed Large uploads are split across frames, so no single frame eats the whole budget
Figure 2 · Two halves of the streaming scheduler. Left: the resident set is a function of the camera, and the prefetch ring shifts along the velocity vector rather than staying concentric. Right: IO, decompress and upload each draw a per-frame allowance, and the resident pool evicts by watermark with hysteresis — the four failure modes (hitch, pop-in, thrash, killed by the OS) map to four points on this pipeline giving way.

Mobile adds three special rules. First, the pool is smaller, so prediction quality directly becomes user experience — desktop can paper over misprediction with redundant residency, a phone cannot. Second, decompression is not free: consoles put it in hardware and PCs have NVMe plus DirectStorage behind them, while on a phone every decode burns into those few watts, so compression ratio versus decode cost must be re-balanced per platform. Third, streaming is not only about assets — the first time a new material enters the frame, pipeline compilation can hitch just the same, so PSO warmup belongs on the streaming schedule next to textures and meshes.

The acceptance test for this ledger is the same as the last one: never “feels smooth”. Fixed automated flythrough routes and long soak runs on real devices produce hitch counts, pop-in durations and pool watermark curves — those numbers are the scheduler’s report card.

Ledger 03 · Frame pacing: average FPS lies

The first two ledgers govern what each frame spends; the third governs the interval between frames — most of what a player feels, and exactly what average FPS cannot measure.

vsync, one slot = 16.6 ms → A · avg 60 fps, steady cadence B · avg 60 fps, broken cadence 40 ms hitch · three slots show the same frame (p99) Both rows: same frame count, same average. The eye reads the distribution of intervals, not the mean.
Figure 3 · Same average, different game. Row A drops every frame into its vsync slot on time; row B jitters between 8 and 40 ms — fast frames get flattened by vsync while slow ones repeat the same image across slots. The metrics that work are frame-time p95 / p99, 1% lows and hitches per minute (a common cut: any frame over twice the target), not average FPS.

Cadence fails in two shapes. One is the hitch — an isolated long frame, with a leaderboard of causes that never changes: pipeline compilation triggered by a shader variant’s first appearance, synchronous IO landing on the critical path, allocation or GC spikes. The other is judder — no frame over budget, yet content step and display beat fall out of phase, and the image advances unevenly in a way the eye catches even at high frame rates. The first is fought by moving slow things off the critical path (PSO warmup, async IO, streaming inside its allowance); the classic answer to the second is fixed-timestep simulation with render interpolation, decoupling the simulation beat from the display beat.

The platform layer adds a set of mechanisms that must be done right per platform: swapchain depth trading latency against throughput; the Frame Pacing library aligning to Choreographer on Android; ProMotion on iOS not giving you 120 Hz unless the refresh-rate range is explicitly requested; variable refresh rescinding the “divide vsync evenly” rule — down to a floor, below which duplicated frames return. None of these is individually hard; the hard part is a dozen of them being right at once, and staying right on every new device generation.

And one more that testing misses most easily: thermal throttling. A game with perfect cadence for twenty minutes collapses in minute twenty-five as the SoC steps its clocks down — a thirty-second capture cannot see it; only a long on-device soak can. The cadence ledger is therefore an SLO sheet: it constrains the shape of the frame-time distribution across a whole play session. The mean does not even get a line item.

How three ledgers grow into the pipeline

Back to the two questions from last time. Knowing the target device and platform, organizing art specifications and code paths is, at bottom, moving the three ledgers out of documents and into the pipeline.

Art asset specifications, organized as data, not documents. One machine-readable spec per device tier: texture compression formats and size caps, mip and channel-packing conventions, LOD chain depth and decimation ratios, material complexity ceilings, the shader variant matrix. Validation happens at DCC export — a non-conforming asset is stopped the moment it is imported, not discovered three months later in an on-device capture; the cook produces per-tier variants from the same source asset. Nor must every variant be made by hand — take decimation: the spec states each LOD’s target triangle ratio and the features to preserve (silhouette, normals, UVs, skin weights), and an automatic decimator generates the whole chain per tier. Our toolchain already carries such a component: an attribute-aware QEM edge-collapse decimator that preserves boundaries and skinning and delivers to a target ratio. As for what defines a “device tier” — not a hand-kept handset list, but frame-time and bandwidth data captured on real hardware: tiers are measured, not guessed.

Code structure, organized around one abstraction seam. The platform-agnostic game and engine core on one side, each platform’s implementation in its own modules and directories, a clean graphics-and-systems abstraction layer between them; quality tiers expressed as data-driven scalability configuration rather than conditional compilation scattered through the code — the problem with an #ifdef forest is not that it is ugly, but that it quietly turns “one codebase” into a superposition of N, and the three ledgers stop reconciling. The direct price of organizing this way is the build matrix: the combinatorial explosion of platform × tier × configuration. That is exactly the cost distributed builds exist to eat — the IrisBuild cluster from the previous article, pressing UE editor shader-compile local fallbacks from 518 to 1 and the Unity IL2CPP stage from 236 s to 129 s, is paying precisely this bill.

Budgets as gates: the ledgers are enforced inside the loop. Spec validation stops non-conforming assets; the build matrix produces every platform and tier; the device matrix flies fixed routes and runs long soaks overnight; capture data reconciles against the three ledgers line by line; and an overrun is not an email — it is an attribution, landing on a specific commit or a specific asset, carried back to the workspace with evidence. The automated loop of the previous article closes here.

pinned to a commit or an asset — back to the workspace with evidence Specmachine-readable Import checksat DCC export Cook per tierplatform variants Distributed buildplatform×tier×config Device automationflythrough + soak Capture auditledgers enforced here AI triageevidence linked Runs nightly. An overrun is not an email — it is an attribution with evidence.
Figure 4 · From spec to gate, closed. What the three ledgers look like grown into a pipeline: the spec is enforced at import, the budgets at capture audit, and triage delivers the result to a specific commit or asset. No node on this chain needs a human on duty — people appear at the two ends: defining the spec, and reviewing the attribution.

Democratizing creativity

With the three ledgers laid out, the question “can a small team ship platform-wide, high-quality games” can be answered honestly.

What infrastructure can flatten is the cost of discipline. Budget reconciliation, streaming acceptance, long-session cadence regression, the cross-platform build matrix, the device farm’s night shift — inside a AAA studio these map to a whole layer of highly paid engineering organization, and they are precisely the most automatable part: mechanisms rented from the engine, discipline subscribed from infrastructure. Ycode puts real devices, captures, automated replay and evidence-carrying AI triage into one workspace; IrisBuild lets the build matrix’s cost amortize across a cluster — together they turn that organizational layer into something subscribed by capacity.

What cannot be flattened is content and judgment. World, levels, feel, taste — content volume is still human time, and taste is still a human decision. But that is the point: a team of three to five used to face a brutal trade between “keeping the ledgers” and “making the game good”; now all of the human time can go to the latter. Engineers are not replaced — their discipline is written into the infrastructure, so it no longer has to be re-hired inside every team.

This is what we mean by democratizing creativity: more people taking part in making high-quality interactive works, with a work’s ceiling set by imagination and taste rather than by how many highly paid engineers one can afford. The three ledgers remain — they just no longer decide who gets to enter.

The road does not end at software. AI infrastructure is making “thinking” ever faster, and the bottleneck moves to the other side: agents must ultimately execute actions, and the heaviest actions in the loop — builds, baking, decimation, frame analysis — will eventually deserve dedicated silicon, just as compilation moved onto distributed clusters. We intend to build custom ASICs for game development and iteration, powering the production lines of real-time interactive worlds. The vision stays the same: players living other lives inside worlds woven from different imaginations, and more creativity reaching everyone in high-quality interactive form.

The entire game-development loop, on one AI-powered infrastructure.

Product page mixstudio.tech/product/ycode · Community Discord · Business [email protected]