fix(render): retain surfaceless particle metadata

Preserve a successfully decoded GfxObj ParticleGfxInfo when SurfaceId is zero and condition only texture acquisition. Missing/no-Gfx descriptors retain Default behavior; material, additive, routing, and untextured drawing remain unchanged.

Add a production-path GfxObj-backed synthetic no-surface pin with distinct authored/AABB centers, non-unit size, nonidentity orientation, object/particle merge order, visual center, and unassigned texture-slot assertions. Correct the synthetic-DAT evidence wording and advance only the S4-c3a ledger row.

Final F2 source audit found no defect: preparation holds reconstruction inputs only; Append reserves once; false and exception roll back the exact tail once; first-use rejection remains registered; accepted tokens stay stable; independent 3000-entry caps preserve row-2 immediate duplicates; flush/end/abort/next-preparation cleanup converges; retained capacity/bytes remain bounded; warmed production paths remain 0 B.

Gates: no-surface target 1/1; unchanged section 19 baseline 95/95; AP boundary/count 1/1; real allocations 2/2 at 0 B; shader/manifest 32/32; Release solution 0 warnings/0 errors; diff-check pass. No graphical client.

Mutation: restoring eea5793d2's SurfaceId-zero-to-Default ternary first failed CellTurn_GfxObjBillboardWithoutSurfaceRetainsAuthoredSortCenterAndUntexturedFallback at the authored-distance assertion, expected 40 actual 100.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-09-04 14:09:50 +02:00
parent 8bd75ba312
commit b6b0156045
4 changed files with 202 additions and 8 deletions

View file

@ -1702,3 +1702,69 @@ no-surface branch and evidence wording first. Only PASS unlocks the sequential
production/gate-honesty lens over the complete c3a stack. Any further code,
test, register, or evidence correction would be a third fix round: stop the
chunk and write it up; do not land or graphically gate it.
### 20.4 Final-fix implementation result (Sonnet, 2026-09-04)
**IMPLEMENTED — FINAL RETAIL REVIEW REQUIRED.** A successfully decoded GfxObj
now retains its complete `ParticleGfxInfo` when `SurfaceId == 0`; texture
acquisition alone remains conditional on a nonzero Surface DID. Missing or
unreadable GfxObjs and descriptors without a GfxObj retain the existing
`Default` behavior. The production-path synthetic GfxObj test supplies no
Surface DID or entry, a nonzero authored center distinct from its vertex-AABB
center, size 2, and a nonidentity orientation. It proves authored distance²
40, ordinary-object-then-particle merge order against distance² 49, the
unchanged visual center `(10,4,0)`, and an unassigned texture slot. The
evidence comment now calls both fixtures “production-path GfxObj-backed
synthetic DAT records,” not real or installed DATs.
Required §20.2 final-round source audit found no additional defect:
- `PrepareForCellAlpha` clears and rebuilds only reconstruction records; it
does not add source payload to `_deferredAlpha`.
- `PreparedParticleAlphaSubmission.Append` calls
`ReservePreparedDispatchDeferredParticle` exactly once at the visibility
edge, then calls `TryAppend` once.
- A false return rolls back that exact tail once outside the `try`; an
exception rolls it back once in the `catch` and rethrows. The rollback
method rejects any non-tail token, so an accepted earlier token cannot be
renumbered or removed.
- `RetailAlphaQueue.TryAppend` registers the source before its capacity
return. A rejected first-use source therefore still participates in the
next flush/end/abort reset while owning no rejected payload. Accepted token
indices remain stable.
- Independent retained counters admit only the first 3,000 CLIP and first
3,000 ALPHA candidates from the already stable particle order. The row-2
immediate action is computed independently of retention, so candidate
3,001 still draws immediately.
- Flush, EndFrame, and AbortFrame reset `_deferredAlpha`, prepared RHI count,
and `_preparedCellAlphaScratch`; the next preparation also clears the
scratch before any early return or rebuild. The immediate-exception path
holds no source payload and converges on that next preparation/abort.
- The prepared list can retain at most 6,000 records and therefore at most the
8,192-slot `List<T>` geometric backing pinned by the diagnostic. Its bytes
remain included in `RetainedAlphaScratchBytes`.
- The real warmed driver/particle/dispatcher merge and the immediate-particle
RHI path both still measure zero managed bytes.
Mutation ledger: restoring candidate `eea5793d2`'s
`SurfaceId == 0 ? ParticleGfxInfo.Default : ...` ternary made
`CellTurn_GfxObjBillboardWithoutSurfaceRetainsAuthoredSortCenterAndUntexturedFallback`
first fail its authored-distance assertion: expected `40`, actual `100`.
The mutation was restored before the return matrix.
Automated return:
- targeted no-surface production path — PASS, 1/1 after mutation restore;
- unchanged §19 focused baseline (excluding the separately executed new
target) — PASS, 95/95, 0 skipped;
- AP-241/AP-242/AP-243 boundary/count pin — PASS, 1/1;
- real production allocation pins — PASS, 2/2, both asserting 0 B;
- shader/manifest/SPIR-V lane — PASS, 32/32;
- Release solution build — PASS, 0 warnings / 0 errors;
- `git diff --check` — PASS (only the repository's LF-to-CRLF working-copy
notices).
Only `ParticleRenderer.cs`, `WalkFrameDriverTests.cs`, this packet, and the
S4-c3a plan-ledger row changed. AP rows, queue/router/pipeline/shader/manifest/
package behavior, and installed content remain unchanged. No graphical client
was launched.