CalmUI
CalmUI Studio

Reload Cost

Read the Profiler's Reload cost numbers and fix what causes a full document reload.

Every UI update that changes bound data ends one of two ways: CalmUI patches the already-loaded document in place, or it falls back to a full reload: re-parsing and re-styling the whole document. A patch is cheap. A full reload repeats the most expensive work in the UI pipeline. The Reload cost section in Tests → Profiler shows how often each happens and, when a reload just occurred, exactly why.

Read the Reload cost section

  1. Open Tests → Profiler.
  2. Choose Studio Preview or Unreal, matching the target you want to measure.
  3. Interact with the open View. The numbers update every frame.
NumberMeaning
Layout passesHow many times layout ran for the current target.
Full document rendersHow many times the document was fully re-parsed and re-styled, instead of patched. Studio Preview only.
Style resolvesHow many elements had their style recomputed.
Text patchesHow many text nodes were rewritten in place without a reload.
Attribute patchesHow many attribute writes were applied in place without a reload.
Structural patchesHow many element insertions, removals, or moves were applied in place without a reload.
Retained screensHow many hidden KeepAlive screens are currently resident. See Retained screen budget.
Runtime Profiler for Studio Preview with the Reload cost section (layout passes, full document renders, style resolves, text/attribute/structural patches, retained screens), Frame, PaintFrame, Selected node and Development memory sections
Tests → Profiler: the Reload cost section counts patches versus full document renders.

The Reload cost section sits above Frame, PaintFrame, Selected node, and Development memory in the same Profiler panel, so you can compare a reload spike against overall frame time without switching views. A climbing Full document renders count next to a steady Text/Attribute/ Structural patches count points at the specific update that is falling back to a reload.

A number shown as n/a was not supplied by the selected target; Unreal PIE does not publish every counter Studio Preview does. It is not a zero.

The full-reload banner

When the most recent frame took the full-reload path, the panel shows a banner:

This frame did a full reload because: <reason>

The banner disappears once a frame patches successfully again. The footer pill keeps the same reason visible for a few seconds longer, so you do not have to keep the Profiler panel open while you work.

Reload reasons and how to avoid them

The reason names the exact check that could not describe your change as an in-place patch, so the fix is different for each one.

ReasonWhat triggered itAuthoring fix
attribute: identity changedAn id, data-testid, srcset, href, or a non-image src value changed between renders.Keep element identity stable across ordinary updates. An <img src> change refreshes that image's resource and intrinsic dimensions in place; other resource attributes still require a reload.
attribute: custom property declaration changedA style attribute started or stopped declaring a CSS custom property (--name: value), even if every other declaration stayed the same.Always declare the same set of custom properties in style, with a default value when unused, instead of adding or removing the declaration itself.
attribute: tag name changedRepeated markup produced a different element type at the same position (for example, a list item that sometimes renders as <button> and sometimes as <div>).Keep one element type per position; put the state that used to pick the tag into a class or attribute on the same element.
text: token count differsA bound string changed how many words and spaces it contains ("Available" → "Not available yet"), and the structural patch could not handle the surrounding change.Supported text-run changes, including empty runs gaining text, are spliced in place. Keep surrounding element identities and wrappers stable; a word-count change alone does not require a full reload.
text: custom property in text runBound text content itself contains a -- sequence that reads as a custom-property declaration.Keep -- characters out of bound text content.
document: not a markup-only reload (css text changed)The component's compiled CSS changed since the last render.Expected while editing a stylesheet; a style edit always reloads. Not an authoring defect.
document: resources invalidatedAn image, font, or video mapping changed under the open document, typically from an assets/manifest.json edit.Expected while editing assets; not an authoring defect.
document: first loadThe View has not been rendered yet, so there is nothing to patch against.Informational only; every View pays this once.
structure: … (several specific reasons)Elements were inserted, removed, or reordered in a shape the patch model cannot map onto the previous render, most often a missing stable key on a repeated list, or a wrapping element that appears and disappears around otherwise-unchanged content.Give repeated elements a stable domain ui:key (never a list index) so inserts, removes, and reorders can be matched by identity. Avoid conditionally adding or removing a wrapper around content that otherwise stays in place.

A reload that happens once, for a reason you expect (a stylesheet edit, an asset change, a screen's first appearance), is normal. Recurring reloads during an interaction that should be a value update (typing in a field, hovering a button, a counter ticking) are worth fixing.

A successful patch still has a cost

An empty refusal reason means the document avoided a full reload. It does not mean the update avoided style resolution, layout or paint preparation. Compare those timing fields as well as the patch counters.

When a text patch preserves the element sequence, layout collection retains metadata outside the written and restyled subtrees. If the sequence changes, the metadata list is rebuilt. An accepted patch can retain metadata even when normal-flow changes require a full box render; geometry is still refreshed. Viewport and resource invalidation require a full metadata collect. Inherited custom-property sets are retained outside dirty subtrees under the same element-identity guard; a changed property-name set invalidates their cache. The Unreal style cache similarly separates an element's own metadata from the ancestor metadata used by selectors. Changing an ancestor class that no ancestor selector consumes does not invalidate all descendants. Matching ancestor selectors, pseudo states, inherited custom properties and stylesheet replacement still invalidate the relevant results. When resolution is needed, a selector index skips rules whose required rightmost id, class or tag is absent, while preserving the full selector checks and cascade order.

For multiple Unreal game worlds, documents and renderer state are retained per world and runtime bridges are selected for that world. Inspect each engine/world/Local Player/view target when diagnosing PIE multiplayer cost; a sample from one target is not the total cost of all worlds.

Studio footer showing Compiler daemon ready, 2 Problems and the cost pill 14.40 ms · renders Δ0
The footer cost pill shows the frame total and the change in full document renders; click it to open the Profiler.

The Studio footer shows a compact cost pill for the currently selected target: the frame total in milliseconds and the change in full document renders (renders Δ) since the previous sample. Click it to open the Profiler's Reload cost section. If a reload happened in about the last five seconds, the pill switches to its warning style and appends the refusal reason, so you can notice a reload without keeping the Profiler panel open.

Studio footer with the cost pill in its warning style: 23.00 ms · renders Δ0 · refused: text: token count differs, followed by the changed text
After a full reload the pill switches to its warning style and appends the refusal reason for a few seconds.

The refusal reason appended here is the same string documented in Reload reasons and how to avoid them; read it directly off the footer instead of reopening the Profiler for a quick check.

Check reload cost from the command line

calmui dev inspect --json prints the same counters from a running Unreal target without opening Studio, so a script or CI check can assert on them:

calmui dev inspect --json

The profiler object in the response includes layoutCount, styleResolves, patches, attrPatches, structPatches, lastPatchRefusal, and retainedScreens alongside the frame-timing fields. lastPatchRefusal is an empty string when the most recent update patched successfully. Add --all to collect every active engine/world/Local Player/view target in one call. See CLI reference.

On this page