Repository navigation
[M2-SPRITE-04] Render observability + draw-call budget (G-R2) - #75
Merged
Merged
Conversation
M2-SPRITE-04 scope (roadmap/M2-rendering-2.5d.md), nothing else: - SpriteDrawStats: + programChanges, uploadBytes, renderTargetBytes, drawCallCapExceeded (the G-R2 frame-graph flag); SpriteDrawTotals: + the matching sums + capExceededFrames. - Options::maxDrawCalls: the configurable per-pass draw-call cap (domain [1, kSpriteRendererMaxInstances], documented default kSpriteRendererDefaultDrawCalls = 64 = 2x the PRD §8.1 worst-case reference-scene budget of 30 draw calls). Exceeding it: one rate-limited Warn (draw_call_cap, fields capacity/draw_calls) + the frame flag + the total — the frame is STILL drawn (observation, never an execution gate; the G-R5 precedent). - textureMemoryBytes(): the texture-memory VRAM estimate gauge (bound atlases' w*h*4 sum, updated at bindAtlas incl. re-bind replacement; 0 in the stopped state). - A failed submit zeroes the frame counters (all three GL failure paths: upload, query creation, draw). - New tests/laige-render/render_counters_tests.cpp + CTest entry render_counters (8 tests / 4 suites): the roadmap's known small scene (10 sprites, 2 atlases, 2 blends -> 3 groups) with the per-frame counters EXACT (frame 1 + frame 2, cross-frame state persistence) + totals, the empty frame counts nothing, the PRIMITIVES_GENERATED feed, the cap warn at the configured count (pinned fields, still drawn, no warn at the cap), and the VRAM gauge exact through binds + replacements. RenderCountersCreate is GL-free; the GL suites GTEST_SKIP on an environment failure. - Docs: sprite_renderer.md (new observability + cap section), docs/README index, module README, roadmap box + board (M2 16/33, 62/194) + changelog row. - laige-api.json regenerated (1233 symbols / 38 headers, +12). Verified: all six local trees warning-clean + full ctest green (build 111/111, build-clang 111/111, build-release 100/100, build-shared 111/111, build-asan 108/108); build-tsan 106/108 — the two failures are a driver-internal teardown data race in this machine's Mesa 26.2.3 llvmpipe (both race accesses inside libgallium; the identical GL pattern passes in SpriteDrawSmoke.ThousandSpriteFrame on this tree and on the CI TSan lane — the CI TSan lane is the contract's TSan authority per the M2-GL-01 precedent). api-real-tree/api-check-fresh, include-lint, determinism-lint green. No budgets.json entry (O(1) bookkeeping; the composite 50k render budget is M2-PERF-01). Additive only — no existing symbol changed.
…bled submit (TSan) The CI TSan lane reproduced a driver-internal teardown data race (Mesa llvmpipe: both race accesses inside libgallium — the eglDestroyContext teardown destroys the driver's internal mutex/condvar while the llvmpipe worker thread is still inside it), triggered by a query-enabled submit as the FIRST FBO operation of the frame. Bisected locally (variants: clear-before-draw clean; 100 sprites, 16 384-pixel readback quiesce, and clear-after-draw all still race): the frame-pipeline clear-before-draw pattern (the SpriteDrawSmoke.ThousandSpriteFrame precedent) leaves the llvmpipe teardown clean — verified 5/5 on this machine's Mesa 26.2.3. The clear touches no sprite-pass state: the PRIMITIVES_GENERATED feed, the frame counters, and the totals are unchanged. build-tsan 108/108, all six local trees green.
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-SPRITE-04 · Render observability + draw-call budget (G-R2)
Roadmap:
roadmap/M2-rendering-2.5d.md(M2-SPRITE-04) · PRD §9.3 G-R2 ("Batcher reports; per-pass cap configurable → warn + frame graph flag") · RENDER-001.What
On top of the M2-SPRITE-02 counters (the M2-PROF-01 profiler feed):
SpriteDrawStats+programChanges,uploadBytes(n × 52 B),renderTargetBytes(w × h × 4),drawCallCapExceeded;SpriteDrawTotals+ the matching sums +capExceededFrames.Options::maxDrawCalls(documented defaultkSpriteRendererDefaultDrawCalls= 64 = 2× the PRD §8.1 worst-case reference-scene budget of 30 draw calls; domain[1, kSpriteRendererMaxInstances]). A frame strictly above the cap: one rate-limited Warndraw_call_cap(fieldscapacity,draw_calls) + the frame flag (the frame-graph flag M2-PROF-01 will report) + the total — and is still drawn (observation, never an execution gate — the G-R5 precedent).textureMemoryBytes()— the texture-memory VRAM estimate gauge: bound atlases'w × h × 4sum, updated atbindAtlas(re-bind subtracts old + adds new), 0 in the stopped state.Tests
New
tests/laige-render/render_counters_tests.cpp— CTest entryrender_counters(the step's Verify command), 8 tests / 4 suites:RenderCountersCreate(GL-free): maxDrawCalls validation first-failure-wins + one Warn; the default reaching the context check →GlUnavailable; the stopped state reads zero.RenderCountersScene(GL): the roadmap's known small scene — 10 sprites, 2 atlases, 2 blends → 3 groups — with the per-frame counters pinned exactly (frame 1 + frame 2 incl. the cross-frame last-atlas/last-blend persistence), the since-construction totals exact, the empty frame counts nothing, the opt-inPRIMITIVES_GENERATEDfeed = 20.RenderCountersCap(GL): the warn fires at the configured count with the pinned fields, the frame still drawn, the repeat + total tracked, no flag/warn at or below the cap.RenderCountersMemory(GL): the VRAM gauge exact through binds + re-bind replacements.Verification
TSan note (first commit): the CI TSan lane reproduced a driver-internal teardown data race — both race accesses inside libgallium (the
eglDestroyContextteardown destroys the driver's internal mutex/condvar while the llvmpipe worker thread is still inside it; no engine code between the pthread frames). Bisected: it is triggered by a query-enabled submit as the FIRST FBO operation of the frame; readback quiesce and clear-after-draw do not fix it. The fix is the frame-pipeline clear-before-draw pattern inRenderCountersScene.PrimitiveQueryFeed(theSpriteDrawSmoke.ThousandSpriteFrameprecedent) — the clear touches no sprite-pass state (counters unchanged), and the teardown is then clean (5/5 on Mesa 26.2.3 local; CI TSan on Mesa 25.2.8 re-verified by this PR's run).laige-api.jsonregenerated (1233 symbols / 38 headers, +12);api-real-tree/api-check-fresh,tools/laige-include-lint, andtools/laige-determinism-lintall green.Compat / scope
SpriteDrawStatsfields + 4SpriteDrawTotalsfields +Options::maxDrawCalls+kSpriteRendererDefaultDrawCalls+maxDrawCalls()+textureMemoryBytes()(12 symbols).budgets.jsonentry: the counters are O(1) bookkeeping; the composite 50k render-CPU budget is measured with M2-PERF-01.ENGINE-RULE-EXCEPTIONadded.docs/api/sprite_renderer.md(new "Render observability + the draw-call cap" section),docs/README.mdindex, the module README, the roadmap box + progress board (M2 16/33, total 62/194) + changelog row.Merge is the reviewer's action (the Windows lane is label-gated on CI: apply
ci:windowsbefore the run if the MSVC compile check is wanted — all new size_t→u32/u64 casts are explicit static_casts, audited for MSVC C4267/C4244).