* fix(text): mark WorldPosition dirty after slot reallocation in _updateLocalData
Both Text (UI) and TextRenderer share a `bounds` getter that runs
`_updateLocalData` then checks `WorldPosition` dirty. `_updateLocalData`
internally `_freeTextChunks` + `_buildChunk → allocateSubChunk`, which
under PrimitiveChunk's first-fit + free-list-merge allocator can return
a slot previously owned by another renderer. `_buildChunk` writes UV
and color but never pos (pos is `_updatePosition`'s job), so the new
slot retains the previous owner's pos floats as residue.
Before this fix, when a path sets only `LocalPositionBounds` dirty
(e.g. `Text._onRootCanvasModify(ReferenceResolutionPerUnit)` in UI
Text), the bounds getter would:
1. see LocalPositionBounds → run _updateLocalData (slot may swap)
2. see WorldPosition not dirty → skip _updatePosition
3. _setDirtyFlagFalse(Font) clear all dirty bits at once
The next _render also sees clean dirty bits and uploads the residue
pos to GPU — the renderer ends up rendering at someone else's old
world position. In practice this manifested as text glyphs jumping
to the wrong spot or appearing missing after UI tab switches that
free + reallocate chunk slots in the same frame.
Fix: force WorldPosition dirty at the end of _updateLocalData so the
contract "after this call, pos must be rewritten" is unconditionally
honored regardless of which caller invoked it.
Tests cover three layers:
- dirty-flag invariant: _updateLocalData must leave WorldPosition
dirty on exit
- corrupted-slot: bounds getter with only LocalPositionBounds dirty
rewrites pos even when the slot memory is poisoned
- full slot-reuse repro: destroy a sibling renderer occupying a
lower offset, then trigger bounds getter on the survivor — its
pos must remain correct after the slot moves
Without the fix, all three regression tests fail with the survivor
rendering at the destroyed sibling's old position.
* chore: drop Chinese commentary from text dirty-flag fix
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(ui): destroy engine after regression describe block
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(text): move dirty propagation to input side
Previous fix added _setDirtyFlagTrue(WorldPosition) at the end of
_updateLocalData in both TextRenderer and UI Text. That treats the
output side as the place to declare invalidation, which conflates
two concerns: dirty flags should declare staleness from input
semantics, and update methods should be pure compute units that
don't propagate flags themselves.
Root cause is on the input side: _onRootCanvasModify(ReferenceResolutionPerUnit)
declared LocalPositionBounds dirty but not WorldPosition, even though
ReferenceResolutionPerUnit affects both local layout and the world
positions derived from it. Fix the declaration where the input
semantic event lives.
TextRenderer needs no change — it has no entry point that dirties
LocalPositionBounds without also dirtying WorldPosition (all setters
use DirtyFlag.Position which includes both).
Tests rewritten from white-box (poking private _dirtyFlag, hardcoded
enum values) to public-API integration tests that drive the bug
through uiCanvas.referenceResolutionPerUnit and assert observable
vertex position changes. The new tests fail without the fix
(maxDelta = 0, positions don't update) and pass with it.
* fix(text): include WorldVolume in dirty flag for ReferenceResolutionPerUnit change
Use DirtyFlag.Position (= LocalPositionBounds | WorldPosition | WorldVolume)
instead of the manual two-flag combination. ReferenceResolutionPerUnit
also affects world bounding volume; without the WorldVolume bit,
_updateBounds is skipped in the bounds getter and stale BoundingBox
leaks into frustum culling and raycasting.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: chenmo.gl <chenmo.gl@antgroup.com>
- Remove project-loader e2e case (depends on outdated project.json with Environment type)
- Remove project-loader unit test from ResourceManager.test.ts
- Convert ambientLight test data to new GLCN header format
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>