Phase 0 of the handoff plan, as a marketplace rather than a flat skills/ directory. Content moved out of the LyraResearch archive and depersonalised: addresses stay in the archive, recipes ship. - plugins/ue-design-skills: 17 skills, 232 failure-mode entries, each with the six required fields; catalog.json as the harness-neutral source of truth and .claude-plugin/ as one adapter over it. - _gate: 16 rules, one poisoned fixture per rule, plus surface coverage so a declared file cannot silently miss the line rules. - ADR-0002 (harness-neutral bundle behind a marketplace) and ADR-0003 (split licensing: CC BY-ND 4.0 prose, Apache-2.0 code and metadata). - LICENSE files at both levels, CONTRIBUTING.md, docs/licensing-options.md as the material the licence decision grew from. Verified: gate.py 0 violations; test_gate.py 16/16 rules redden on their fixtures with a clean baseline and 2 root files reaching the line rules. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
12 KiB
name, description
| name | description |
|---|---|
| ue-streaming-and-platform-budgets | Design or audit GPU and streaming budgets in Unreal Engine: texture, mesh and virtual texture streaming, streaming pool sizing, texture LOD groups, scalability buckets, device profiles and console-variable priority, world partition and level instance lifetime, and the boundary between disk, CPU memory and video memory. Use when targeting a platform memory ceiling, tuning quality tiers, adding a background or preview level, or investigating why a quality setting has no effect. |
UE streaming and platform budgets
The invariant:
A streaming pool is a permission, not a measurement. A quality setting is a request, not an outcome. Both are arbitrated by device profiles and console-variable priority before anything reaches the GPU.
Measured patterns from a reference product: patterns. Detection recipes: failure modes.
Related skills: ue-asset-loading-and-memory, ue-game-settings-architecture,
ue-runtime-allocation-and-caching.
Method, not architecture — how to check any claim in this bundle before repeating it: ue-evidence-discipline.
1. Four budgets, four instruments
| Budget | What it physically is | Instrument |
|---|---|---|
| A. Disk / cook | bytes in the shipped package | build and chunk sizes |
| B. CPU memory | object graph plus bulk data in system RAM | memory report, object listing |
| C. Video memory | live render resources: textures, buffers, render-target pool, virtual-texture pool, shadow pages, ray-tracing structures | platform GPU profiler |
| D. Streaming pool | an accounting ceiling the streamer tries to stay under | streaming stat |
Four rules that prevent most wrong conclusions:
- A is not D. Clamping a texture group's maximum size shrinks what is cooked. The pool is unchanged.
- D is not C. The pool covers streamable mips only. Render targets, virtual texture pools, shadow pages and acceleration structures are in C and invisible to the streaming stat.
- B is not C. A loaded texture object need not have any resident mips.
- Never report a pool size as memory used. It is the single most common error in this area, and it makes every downstream number meaningless.
2. Console-variable priority is the real arbiter
Effective value is not "last write wins"; sources are ranked. In practice:
command line > console > device profile > system ini > project ini
> game setting > scalability > constructor
with one exception that matters enormously: a device profile applies variables whose names begin with the scalability prefix at scalability priority, and everything else at device-profile priority.
The consequence
A value set by a device profile cannot be changed by anything the scalability system does afterwards — which includes every in-game quality setting. The same setting is therefore mutable on one tier and frozen on another, with nothing in the UI explaining the difference. Recipe: ST-01.
Gates
- every quality-affecting variable has a documented intended priority source;
- prefixed and unprefixed names are used deliberately, not by habit;
- per-tier overrides are complete across tiers, or partial with a comment saying why;
- the settings UI can express "clamped by platform" rather than silently failing;
- requested and effective values are both observable at runtime.
The one command that settles it: dump variables with their source. Anything else is inference.
3. Pool sizing, and its detachment from real memory
Two knobs interact: the pool size, and whether the pool is clamped by actual video memory.
The trap is structural rather than accidental: the highest quality buckets commonly disable the video-memory clamp, and automatic detection promotes hardware to the top bucket on a compute benchmark that does not consider memory size at all. A fast card with modest memory therefore lands in the bucket where the pool budget is largest and least constrained. Recipe: ST-02.
Rules:
- decide per platform whether the pool is derived from memory or fixed;
- if fixed, write down the assumed memory floor;
- benchmark thresholds must consider memory, not only throughput;
- test the top bucket on a low-memory card, not only on the reference machine.
Sharing the pool
If mesh streaming is enabled and no separate mesh pool is configured, geometry and textures compete inside one budget. That is a legitimate choice; it is not a legitimate surprise. Check both values on every platform. Recipe: ST-03.
4. Texture groups are the per-content lever
Scalability moves everything at once; groups shape individual content classes. Use them to encode intent: hero content larger, world content platform-scaled, UI unstreamed and sized exactly, debug content excluded from cook.
Gates: every group has a stated purpose; mobile tiers override group caps rather than only global quality; no content relies on a group it does not belong to; streaming-exempt content is deliberate and small.
Watch for group settings that describe a delivery mechanism the project does not have — optional mip levels configured with no chunk installation, for instance. Configuration that cannot take effect is worse than absent configuration, because it reads as a decision. Recipe: ST-08.
5. Build-level decisions that look like runtime knobs
Some streaming variables are read-only: their value is fixed at startup and influences what is cooked. Changing them at runtime, or from a device profile, does nothing and says nothing.
Before treating any streaming variable as tunable, check its flags. And never A/B a read-only variable mid-session and compare the numbers — you are comparing one configuration with itself. Recipe: ST-04.
Virtual textures belong in the same category. They change residency characteristics fundamentally, their pool is separate from the streaming budget, and a pool that does not grow on over-subscription produces thrashing rather than an allocation. Enabling them for some content and not other content makes budget comparisons invalid.
6. Device profiles: reachability before content
A profile hierarchy usually encodes platform base → tier → device. Three failures recur, and all three are silent:
A section that is never selected. Defining a profile section does not make it reachable; something must map to it. A tier section absent from the registry is configuration that reviewers read as live. Recipe: ST-05.
Headers that contradict assignment. Comment banners grouping devices under a tier label, while the devices beneath resolve to a different base profile. The comment is documentation; the base profile is behaviour. Recipe: ST-06.
An override list that no configuration populates. A user-facing profile variant list declared in code and set in no file means the selection logic composes an empty name, the override is never applied, and the preset UI never appears. Recipe: ST-07.
Gates: every profile section is reachable from the registry; tier assignment is asserted by a dump, not read from comments; unknown hardware has a documented fallback; every override hook has a real caller and real data.
7. Requested versus effective quality
A settings screen showing the user's request while a device profile clamps the result is lying by omission. Model it explicitly:
requested quality → platform clamp → effective quality → applied variables
Expose both, and give a reason when they differ.
Unit-test every clamp at its range boundaries
The measured defect worth guarding against: a compatibility check comparing a quality level (0–3) against a resolution percentage (default 100), so the constraint can never fire. Its sibling function used the correct accessor, which is what makes this findable — and what makes it invisible to review, since both lines read identically. Recipe: ST-09.
Also check every normalisation for a zero denominator. A remap between two ranges divides by their difference; a configuration that makes those equal is reachable from data. Recipe: ST-10.
8. World content lifetime
Level instances, not second worlds
Background and preview content is usually a level instanced into the current world, not a second world. The cost is duplicated level content in one scene: actors, components, primitives and their render resources.
Measure by scene composition — actor and primitive counts, visible material slots — not by counting world contexts. "There is only one world" is true and irrelevant.
Ownership rules
Every streamed-in instance has one owner and an unload path; async handles are retained and cancellable; failure surfaces a reason rather than a permanent wait; teardown runs on travel, mode change and error.
Hidden render targets
Scene-capture preview systems allocate render targets per capture object and may keep rendering state resident between captures. Budget them explicitly: count × resolution × format, and verify release. A subsystem with no callers that still instantiates per world and installs a ticker is paying for a feature nobody uses — unused systems must decline creation.
If runtime layer state is never set, it is not runtime streaming
Shipping editor-authored initial state is a valid choice. Describing it as dynamic streaming is not, and a team that believes it has a reference implementation will look for one that does not exist.
9. Review checklist
- Four budgets separated in every claim and every report.
- Pool settings never presented as consumption.
- Variable priority documented per quality knob, verified by a source dump.
- Tier overrides complete, or partial with a stated reason.
- Memory-clamp policy explicit per platform.
- Mesh and texture pool split decided per platform.
- Read-only variables identified before anyone tries to tune them.
- Every profile section reachable; assignment asserted, not commented.
- Requested and effective quality both visible, with a reason on divergence.
- Clamp logic unit-tested at both ends of its real range.
- Level instances owned, released and counted.
- Preview and capture render targets budgeted and released.
- Unused subsystems decline creation.
10. Measurement plan
Static first — cheap, and finds the structural problems:
- Dump effective variables with their source per device profile, and diff against intent.
- Verify every profile section is reachable from the registry.
- List texture groups and their per-tier caps.
- Search for runtime streaming and layer-state calls to confirm what is actually dynamic.
Runtime, on target hardware, in a packaged build:
- streaming stat for pool budget versus used and over-budget;
- platform GPU profiler for real video memory — the streaming stat cannot see most of it;
- fixed camera poses for any comparison; view-dependent metrics are otherwise noise;
- one run per tier on real devices, not emulated profiles alone;
- scene composition counts for level-instance cost.
Do not compare numbers taken at different camera poses, different tiers, or between editor and packaged builds. Those comparisons look quantitative and mean nothing.
Provenance
The findings come from a configuration and source audit of Epic's Lyra Starter
Game on Unreal Engine 5.6, cross-read against the engine's own scalability and
device-profile configuration. Source addresses stay in the research archive that
produced this skill; each entry carries a stable identifier (ST-01, ST-02, …)
resolving back to the audited location there.
Evidence boundary
Static configuration analysis only: no profiler was run, no device was measured, and no number in the failure modes is a memory measurement. Engine defaults quoted describe one engine version. The recipes are written to be re-run against your own project and engine, which is the only way any of these values become true for you.