UI
This file covers the retained ECS UI: the component set and anchor solver, the authoring façade over them, and the render pass that composites the resulting draw list.
1. UI (retained ECS canvas)
The UI is retained and lives in the ECS, the same choice the rest of the engine makes: a Canvas
is an entity, and every button, panel, and label is an entity under it carrying
engine/domain/ui/include/SushiEngine/ui/components.hpp components — RectTransform (UGUI
anchor/pivot/offset layout), UIImage, UIText, UIButton, linked by UIParent. So UI appears
in the hierarchy, serializes with the scene, and (in a networked game) snapshots like any other
component.
resolve_rect (engine/domain/ui/include/SushiEngine/ui/layout.hpp) is the entire anchor model
as a pure function of a parent rectangle and a RectTransform, so it is unit-tested
(Unit_UILayout, tests/unit/test_ui_layout.cpp) with no world. The UI façade
(engine/domain/ui/include/SushiEngine/ui/ui.hpp) is the authoring surface:
canvas/panel/image/label/button builders spawn the entities into an existing World and
keep a light ordered index (the same host-record pattern
the Rigid Body toggle
uses), giving a deterministic paint and hit-test order.
Each frame update(screen_size, pointer) resolves every ComputedRect (parents before
children), runs the button state machine and press-and-release-inside click detection off an
explicit per-frame PointerInput (input as a value, not a global — the same determinism
discipline the sim follows), tints each button’s graphic, and fires on_click callbacks.
build_draw_list() emits a renderer-agnostic UIDrawList of coloured rects and text runs, in
paint order. samples/authoring/ui_demo.cpp and Integration_UI
(tests/integration/test_ui.cpp) drive a canvas + button headlessly and assert layout, clicks,
and button states.
1.1. The overlay pass
UIDrawList lives in its own header (engine/domain/ui/include/SushiEngine/ui/draw_list.hpp)
rather than in engine/domain/ui/include/SushiEngine/ui/ui.hpp, because the two sides of that
contract weigh very differently: building a list needs the world and the layout solver, drawing one
needs three structs. That split is what lets
engine/presentation/render/include/SushiEngine/render/scene_view.hpp name a UI draw list without
pulling the ECS in behind it.
Render::UIView carries it across the render seam as a non-owning POD, the same shape every
render-seam view type uses, plus the screen size the layout was solved against — an editor viewport
solves its UI against the viewport, not the window, so the renderer cannot infer it from the
target.
Passes::UIPass (engine/presentation/render/source/passes/ui_pass.*) runs last of the colour
passes, after tone mapping and FXAA, and composites into targets.resolve with
AttachmentLoad::Load. The position is the point: UI drawn before the tone map would shift hue
with the scene’s exposure, and UI drawn before the AA filter would have its text softened. Drawn
here it is exactly the colour it was authored as.
Rectangles and glyphs share one vertex format, one atlas and one indexed draw.
Material::FontAtlas (engine/presentation/render/source/material/font_atlas.*) bakes printable
ASCII once at bring-up with stb_truetype (from the Stb vcpkg package the image loader already
uses, so the font path costs no new dependency) and reserves texel (0,0) as opaque white, which
is what untextured geometry samples — without it a solid panel would need a second pipeline.
Geometry::UIBuffers (engine/presentation/render/source/geometry/ui_buffers.*) tessellates the
list into per-frame-slot host-visible buffers that only grow, the arrangement
GrowableBuffer uses and for the same reason.
It degrades rather than fails. With no font the atlas slot falls back to the texture library’s opaque-white default, so panels still draw and only the labels go missing; with no bindless heap the pass does not register at all.
In the editor the elements now come from the renderer, so the Game view shows what a shipped
build shows. paint_ui_overlay is reduced to authoring chrome the runtime never has: canvas
extents, an edit-mode outline that keeps a transparent element grabbable, and the selected
element’s outline and resize handles.

