Lane F R6: ≤350 interior draw gate + fix interior renderer.info accumulation
Added an interior draw-budget sweep to flags_check.py (decision #2): enter all 9 shop types,
assert <=350 draws/room, dig on so it also confirms C1's batching kept ?dig=1 riffling.
The sweep caught a real bug in interior_mode.js (F-owned): the interior/dig render never called
renderer.info.reset() while the shell runs autoReset=false (for the composer), so
renderer.info.render.calls ACCUMULATED every frame — inflating any interior draw read (and the
HUD were it shown). Fixed: reset per interior frame before both the dig and the walk render.
True per-frame result after the fix: all 9 room types <=350 draws (worst dept 96) — C1 batching
(35981ad) verified. flags_check GREEN (regression + interior budget + 4 flag smokes + all-on combo).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
24cef5a7bb
commit
3b86b0c5b3
@ -14,7 +14,7 @@ lanes land, adds C's ≤350-draw interior assertion when C1 lands, extends the d
|
||||
|---|---|
|
||||
| **F1 enforcement harness** | ✅ **DONE + wired.** `tools/flags_check.py` (self-contained server, headless, ~90s): **flags-off regression** (golden hash `0x3fa36874` + subsystems-inactive + ≤300/≤200k budget + ±40% snapshot + 0 errors) · **per-flag smokes** (winmap/dig/roster: cross ≥2 chunks + enter interior; dig opens+closes a riffle) · **all-on combo** (flag-composition law, decision #4). Wired into `qa.sh` as a **warn-level gate** (`--no-flags` skips fast; auto-skips without the Playwright venv). Current: **all smokes GREEN**, regression clean. |
|
||||
| **F2 plansrc** | ✅ **DONE + verified.** A landed the full seam (`52eb109`). Wired the shell bootstrap (`index.html`, F-owned): `?plansrc=osm` → `citygen.generatePlanFor(seed,'osm')` → real-data town **"Melbourne", 95 shops** (synthetic default = "Boolarra Heads" unchanged). Pinned **both goldens** in `scaffold_check.mjs`: synthetic `0x3fa36874`, **osm `0x34cfdec0`** (deterministic, 2 runs). Harness auto-detects plansrc + smokes it (enters OSM shop "Mui Karaoke Rooms") + includes it in the all-on combo. Also restored `binUnderAim`'s **nearest-bin fallback** (matches C's test page) so E riffles a nearby bin without perfect aim / before the bin GLB finishes loading. Synthetic golden frozen (selfcheck 1362/1362). |
|
||||
| **≤350 interior draw assertion** | ⏳ add to harness once C1 (batching) lands (decision #2; book room's 1,245 is the bug). |
|
||||
| **≤350 interior draw assertion** | ✅ **DONE.** C1 batching landed (`35981ad`). Added an interior draw-budget sweep to `flags_check.py` (enter all 9 shop types, assert ≤350 draws/room, dig on so it also confirms batching kept riffling). **Caught a real bug in my own `interior_mode.js`**: it rendered the interior without `renderer.info.reset()` while `autoReset=false`, so draws *accumulated* per frame (inflated 864/644/…). Fixed (reset per interior frame, both dig + walk render). True per-frame result: **all 9 room types ≤350 (worst dept 96)** — C's batching verified. |
|
||||
| **F3 tag `v2.0-alpha`** | ⏳ all landed flags smoke-green + regression green + qa strict green → tag (3 flags OK if A no-shows). |
|
||||
|
||||
Base: v1.1 (`3a831fe`), HEAD `40fefd9`. `qa.sh --strict` GREEN.
|
||||
|
||||
@ -185,6 +185,42 @@ def smoke(p, label, query, is_combo=False):
|
||||
finally:
|
||||
b.close()
|
||||
|
||||
def interior_budget(p):
|
||||
"""Decision #2: interior draw budget is law — ≤350 draws worst room. Sweep every shop type
|
||||
(dig on, so this also confirms batching kept ?dig=1 riffling). C enforces via its sweep; F pins it."""
|
||||
head('INTERIOR DRAW BUDGET (≤350 draws/room law — decision #2)')
|
||||
b, pg, errs = new_page(p)
|
||||
try:
|
||||
boot(pg, 'dig=1')
|
||||
pg.evaluate("() => window.DBG.setSegment(2)") # midday → shops open
|
||||
types = ['record', 'opshop', 'toy', 'book', 'video', 'pawn', 'milkbar', 'dept', 'stall']
|
||||
rows, seen = [], set()
|
||||
for t in types:
|
||||
picked = pg.evaluate("""(t) => {
|
||||
const P=window.PROCITY, D=window.DBG;
|
||||
const s=(P.plan.shops||[]).find(x=>x.type===t && P.isOpen(x));
|
||||
if(!s) return null; D.enterShop(s.id); return {id:s.id, name:s.name};
|
||||
}""", t)
|
||||
if not picked:
|
||||
continue
|
||||
pg.wait_for_timeout(500) # let async GLB fittings load → settled worst-case draws
|
||||
info = pg.evaluate("() => { const i=window.DBG.info(); return {draws:i.drawCalls, tris:i.tris, mode:i.mode}; }")
|
||||
rows.append({'type': t, 'shop': picked['name'], **info})
|
||||
pg.evaluate("() => window.DBG.exitShop && window.DBG.exitShop()")
|
||||
pg.wait_for_timeout(150)
|
||||
over = [r for r in rows if r['draws'] > 350]
|
||||
for r in over:
|
||||
FAIL(f"interior {r['type']} '{r['shop']}': {r['draws']} draws > 350")
|
||||
if rows and not over:
|
||||
worst = max(rows, key=lambda r: r['draws'])
|
||||
OK(f"all {len(rows)} room types ≤350 draws (worst: {worst['type']} {worst['draws']}, tris {worst['tris']})")
|
||||
elif not rows:
|
||||
WARN('no interiors sampled (no open shops of the swept types)')
|
||||
if errs:
|
||||
FAIL(f"interior sweep: {len(errs)} console error(s); first: {errs[0][:140]}")
|
||||
finally:
|
||||
b.close()
|
||||
|
||||
def main():
|
||||
srv = ensure_server()
|
||||
try:
|
||||
@ -196,6 +232,7 @@ def main():
|
||||
print("· plansrc=osm not landed (Lane A) — skipping that flag")
|
||||
|
||||
flags_off_regression(p)
|
||||
interior_budget(p)
|
||||
for fl in flags:
|
||||
smoke(p, fl.split('=')[0], fl)
|
||||
smoke(p, 'ALL-ON combo', '&'.join(flags), is_combo=True)
|
||||
|
||||
@ -153,6 +153,7 @@ export function createInteriorMode({ THREE, renderer, camera, plan, fleet = null
|
||||
if (dig && dig.active) {
|
||||
if (dig.update) dig.update(dt);
|
||||
renderer.autoClear = true;
|
||||
renderer.info.reset(); // autoReset is off (shell owns it for the composer); reset per interior frame
|
||||
renderer.render(dig.scene, dig.camera);
|
||||
return false;
|
||||
}
|
||||
@ -173,6 +174,8 @@ export function createInteriorMode({ THREE, renderer, camera, plan, fleet = null
|
||||
|
||||
keepers.update(dt); // Lane D: keeper idle + turn-to-greet (playerPos defaults to the camera)
|
||||
renderer.autoClear = true; // the composer may have left autoClear=false; force a clean interior frame
|
||||
renderer.info.reset(); // autoReset is off (shell owns it for the composer); reset per interior frame so
|
||||
// renderer.info.render.calls reports THIS frame's draws (HUD + the ≤350 room gate)
|
||||
renderer.render(scene, camera);
|
||||
|
||||
const de = nearestExitDist(camera.position.x, camera.position.z);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user