Repository navigation
[M2-UI-02] UI rendering pass (screen-space, identity camera) - #91
Merged
Merged
Conversation
- New header-only laige::render UI pass (public header
laige/render/ui_render.h, no GL calls, no new dependency):
the draw half of FR-2.8 -- "UI is a separate render pass,
screen-space". The S-5 declare half; the GL half is the
M2-SPRITE-02 SpriteRenderer::submit with the UI pass's
identity screen matrix (nothing world-space leaks in,
RENDER-006).
- UiDeclareOptions{defaultFont, fontAtlasId, fontScale}: the
default UI font (non-owning const GlyphAtlas*), its bindAtlas
id, its integer scale in [1, kTextMaxScale]; a null font
turns off the pass-generated text (button labels / input
content declare nothing, the art still renders).
- uiScreenMatrix(width, height): the identity screen matrix,
screen px y down -> NDC (x_ndc = 2x/w - 1, y_ndc = 1 - 2y/h;
column-major: m[0].x = 2/w, m[1].y = -2/h, m[3].x = -1,
m[3].y = +1, m[3].w = 1); out-of-dimension domain
[1, kUiMaxDimPx] -> the identity matrix.
- declareUi(batcher, table, tree, options): the O(n) tree ->
batched-quads pass. Walks the laid-out rects in the
deterministic traversal order (RENDER-003); culls the
invisible widgets (the documented simple model -- zero area
or entirely offscreen on all four half-open edges, O(1) per
node; fully covered by an opaque panel drawn LATER in the
traversal -- the ADR 0006 occlusion model -- an
early-stopping re-walk, worst case O(n) per node, O(n^2)
per frame, ponytail: ceiling comment + the O(n*P) upgrade);
declares one centered quad per visible widget (pos = the
rect center, scale = the rect size): the opaque panel
background (a non-opaque panel declares nothing), the
button art + centered label (the exact int64 baseline
rectY + (rectH + lineStep)/2, lineStep = lineHeight() *
fontScale, the node's tint), the text item verbatim with
the depthKey overwritten by the widget's z (the rect is
culling-only -- no clipping quads in M2), the image quad,
the list nothing (a container nests an opaque panel as its
first child), the slider track + fill only for 0 < t < 1
(t = (value - min)/(max - min); t = 0 is a zero-width quad,
t = 1 double-blends the identical track pixels -- the
alpha blend is not idempotent), the input's left-aligned
white content.
- One batcher per sprite pass: the UI pass is its own
SpriteBatcher (M2-SPRITE-01). Every quad carries the
monotone per-node UI z (the index among the nodes that
declare something, in traversal order -- a culled /
no-declare node consumes no z) with depthOverride = true
(the G-R11 escape hatch -- the UI pass is screen-space,
there is no isoDepthKey; counted + warned at build),
materialId 0 + BlendMode::Alpha, one texture per atlas
(the scene binds its UI atlas + glyph atlas(es) through the
existing bindAtlas / bindSdfAtlas).
- Failure model: first failure wins (stopped tree / missing
root or an out-of-domain fontScale with a font ->
InvalidArgument before the first add; a Text / button-label
/ input-text handle not owned by the string table or a
stopped font -> InvalidArgument; a failed batcher add ->
the batcher's error). A failed call leaves the quads
ALREADY DECLARED in the frame (the batcher owns the frame
state -- the declareParticles / declareText precedent). No
logging on preconditions.
- Tests: tests/laige-render/ui_render_tests.cpp (new CTest
entry ui_render; 12 tests / 7 suites): UiRenderScreenMatrix
(the column-major pin incl. m[3].w = 1, the corner/center
NDC, the out-of-domain identity), UiRenderDeclare (the
200x150 all-seven-kinds scene -> 12 quads / 2 groups / 12
overrides against the hand-computed goldens, the
machine-greppable ui-render-declare line; no default font ->
2 quads), UiRenderCull (the offscreen rule, the occlusion
rule -- the later-opaque-panel early stop, the
non-opaque-panel exception), UiRenderSlider (t = 0/1 track
only, t = 0.5 track + fill), UiRenderFailure (the
precondition failures declare nothing; the handle failures
with real rects -- the button's art precedes its label ->
frameCount 1; the batcher-state failures),
UiRenderZeroAlloc (1000 frames = 0 heap blocks, the
machine-greppable ui-render-zeroalloc line), UiRenderSmoke
(the golden image fixture -- a fixed 128x128 UI scene
rendered offscreen through the real SpriteRenderer::submit
(7 quads, 2 groups, 2 draw calls, 7 instances, 364 upload
bytes) against a CPU reference rasterizer (the
strict-interior pixel-center rule -- all scene edges are
integer px, never on an edge; the per-quad 4-channel double
blend out.a = a^2 + (1 - a), byte = min(255, v*255 + 0.5)):
the whole frame within the documented +/- 1 byte tolerance
(16384 readbacks) + the exact probes -- the "a" semi texel
pins the all-four-channel glBlendFunc contract (rgb == A
exact, alpha within +/- 1 of round(255(a^2 + 1 - a)));
GTEST_SKIP where no usable OpenGL 3.3; the committed-PNG
infra lands with M2-GOLD-01).
- Docs (DOC-003/004/006/007): NEW docs/api/ui_render.md (the
full contract + the DOC-004 Performance section), the
docs/README.md index row, the src/laige-render/README.md
status paragraph, docs/concepts/coordinates.md NEW section
4.14 (the screen-space identity camera) + the section 5
conversion row + the Related link (the header's normative
reference), cross-links from ui_widgets.md / text_items.md.
- Tests/CMake: the ui_render entry (TIMEOUT 120, the
LAIGE_TEST_FONT_PATH env) + the TSan block + the ASan
detect_leaks=0 list (the smoke can create a headless
context) + the header comments.
- laige-api.json regenerated LAST (1682 -> 1688 symbols, 46 ->
47 headers; api-real-tree green).
- No budgets.json entry (the per-frame UI cost is part of the
composite 50k render-CPU budget, M2-PERF-01 -- the pin
count stays 17).
Verified locally (GL-free): all six canonical trees build
warning-free; ctest -R ui_render 11/11 GL-free suites green in
each; the full GL-free ctest per tree green except the
pre-regeneration api-real-tree (fixed by the manifest
regeneration); tools/laige-include-lint +
tools/laige-determinism-lint OK.
CI-validated (pending): the UiRenderSmoke GL smoke runs on
the macOS PR lane (arm64 skips -- no usable GL; Intel runs
it) and the merge lane (Linux + Windows MSVC -- the first
MSVC compile of this C++). The local GPU execution policy
prohibits running the smoke locally.
The header's model comment said a node that declares nothing takes no z; a Text widget with empty content is the exception (its declareText succeeds, with no quads, and it consumes the z). Match the header comment to the shipped behavior (docs/api/ui_render.md already stated it).
…sh manifest line numbers CI run 38158862667 (macOS Intel + arm64, AppleClang) failed UiRenderScreenMatrix.UiPlaneMapping: the center composition m[0].x * 640.0f + m[3].x evaluates to 1.4901161e-08 instead of 0.0f. AppleClang contracts the mul+add to an FMA (its default -ffp-contract=fast on Apple targets), which rounds the exact residual (exact a*640 - 1, a = float(2/1280)) in a single step; the non-contracted path (separate mul+add, e.g. GCC without FMA, MSVC) rounds a*640 to exactly 1.0f first and subtracts to exactly 0.0f. Both are within one ULP of 1.0 of the exact value 0, so assert the center mappings with the house absolute tolerance (kEps = 1e-6f, the matrices_tests pattern) instead of ULP equality against zero, which has no tolerance. The corner checks (against +-1.0f) keep EXPECT_FLOAT_EQ: both evaluation paths yield exactly +-1.0f for the pinned 1280x720 dimensions, and UlpEq's 4-ULP tolerance covers the 1.0 scale. Verified: the FMA value reproduces locally with g++ -O2 -mfma on a runtime matrix (vx = 1.4901161194e-08, vy = -2.4214386940e-08, both within kEps); all six canonical trees build warning-free with the eleven GL-free ui_render suites green. Also: laige-api.json regenerated (LAST) for the header comment change in the previous commit, which shifted the ui_render.h symbol line numbers (+2) by twelve manifest fields.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
M2-UI-02 · UI rendering pass (FR-2.8 draw half): the screen-space UI pass on the identity camera.
declareUiwalks the laid-out tree, culls the invisible widgets (offscreen + later-opaque-panel occlusion — the documented simple model), and declares one batched quad per visible widget (+ the glyph quads of the text widgets and the pass-generated labels) into the pass's own SpriteBatcher (the monotone UI z via G-R11 depthOverride, one texture per atlas). The GL half is the existing M2-SPRITE-02 submit withuiScreenMatrix. Golden image fixture:UiRenderSmokerenders a fixed 128×128 UI scene offscreen through the real submit stage and compares the whole frame against a CPU reference rasterizer within the documented ±1 byte tolerance (the committed-PNG infra lands with M2-GOLD-01).laige/render/ui_render.h(no GL, no new dependency); newui_renderCTest entry (12 tests / 7 suites; 11 GL-free + 1 offscreen GL smoke with the GTEST_SKIP contract).ctest -R ui_render11/11 GL-free suites green in each; full GL-free ctest per tree green (api-real-tree fixed by the manifest regeneration); include + determinism lints OK;laige-api.jsonregenerated LAST (1688 symbols / 47 headers).docs/api/ui_render.md, thedocs/README.mdindex row, the module README status,docs/concepts/coordinates.md§4.14 + §5 row + Related, cross-links from ui_widgets.md / text_items.md; roadmap M2-UI-02 shipped + board M2 26/33 (72/194).