DisplayMesh
ReferencesPipeline position: DisplayBag (mutable) collects these during a solve; DisplayCache (immutable) snapshots them when the solve commits; MeshDisplayCache resolves them per frame into CachedDrawItems for GPU submission.
DisplayMesh vs CachedDrawItem: DisplayMesh carries the inputs, raw user overrides (colour, glow, style, meta). CachedDrawItem carries the resolved per-frame output (ShadedDisplaySettings for C, RuntimeFaceGuise for B, plus GPU bookkeeping) and gets rebuilt when caches go stale.
They're separate because resolving depends on things that aren't intrinsic to the mesh: the active Guises preset, selection state, expiration fade factor, and the MeshDisplayCache.UseNewShader rollout gate. Those vary at different cadences than the recipe; keeping the stages separate lets us cache the cheap inputs (this struct plus ShaderMetaOverrides) while regenerating the resolved state only when the external inputs change.
Do NOT try to collapse DisplayMesh into CachedDrawItem (this has been attempted repeatedly). It is tempting once ApplyMeta leaves the hot loop, on the grounds that resolution becomes cheap — but cost was never the blocker. The two sit at opposite ends of the resolve step and have incompatible lifetimes: DisplayMesh is a resolution-FREE recipe, exactly one per source mesh, owned by the immutable per-object cache and stable across frames; CachedDrawItem is the resolution-BAKED output (guise/selection/fade/ shader-path applied), regenerated every static-list rebuild, possibly many per source mesh, owned by the shared static list and kept in sorted order. The same DisplayMesh feeds multiple viewports, frames and fade states, so it cannot carry per-frame resolved state. Merging them would force one struct to hold both a stable cross-frame recipe and volatile per-frame output — two lifetimes in one box. Keep them separate.