Model catalog installation now divides buffer transfers, texture base64 conversion and texture uploads into cooperative batches. This implements the GPU-installation part of the asset pipeline's scheduling contract in ENGINE_PLAN.md; full quality/device qualification remains open.
#Ownership and scheduling
allocateModel owns every candidate lease until its installation succeeds. A synchronous generator issues bounded writes. installModelUploads runs a batch under validation and out-of-memory scopes, pops all scopes synchronously, observes their results, then yields through an ordinary cancellable timer task. It never suspends with a device-global error scope open. Main and portal views may therefore install on the same device without nesting one asynchronous job's scopes inside another's.
Each buffer write is at most 256 KiB. Texture transfers use whole rows, with multiples of three rows aligning intermediate base64 fragments; the final fragment retains its canonical padding. Every original mip is uploaded, with the original format and pixels. With admitted image dimensions of at most 2,048 pixels per edge, each texture write is also at most 256 KiB. Skin influence packing checkpoints every 2,048 vertices, and already-resident influences need no repacking.
A batch stops after reaching a 256 KiB transfer target, 64 generator steps, or 4 ms of elapsed synchronous work. The final step can cross the byte/time target: a batch transfers less than 512 KiB, and a native call cannot be preempted. These are per-installation cooperative limits, not an aggregate device CPU quota or a guaranteed frame deadline. Task yielding allows the normal frame/event loops to run; background timer throttling can lengthen total installation. The existing coordinated scene-revision barrier remains in effect.
Both model replacement and staged catalog revisions pass their recovery controller's cancellation signal to installation. Cancellation while waiting for native validation or the next task rejects promptly; all native promises remain observed. Cancellation clears a scheduled timer. A closed resource pool fences subsequent native work, and a final cancellation/pool check fences lease publication after asynchronous validation.
Only a fully installed and validated candidate marks its leases ready. Pending allocations remain unshareable. Rollback releases only the candidate's references; accepted source/portal resources survive until their last owner retires. Existing 32 MiB model-replacement and 64 MiB device-pool ceilings and frame-retirement publication barriers remain unchanged. Formats and content/checkpoint identities do not change.
#Inspection and acceptance
F2 shows GPU upload yields and GPU upload KiB for the current accepted catalog. Exported playtest reports include its upload byte, batch and yield counts in modelResidency.uploads; portal model snapshots carry their own counts. These are the most recent installation's counters, not cumulative frame timings. A catalog that reuses all resources can report zero uploaded bytes.
Headless checks verify exact reconstructed buffer/mip bytes, bounded write sizes, task progress with closed scopes, cancellation, pending validation rejection, two simultaneous view installations, late failure rollback, exact retry, resource reuse and pool disposal. Recovery checks cover cancellation during creation as well as preparation.
The native browser fixture runs on both main and real Worker placements. It reads uploaded geometry and all ten mips of a 512×512 texture back from WebGPU and compares every byte. It also verifies task progress, cancellation preserving an accepted catalog, retry, zero-byte reuse and disposal during another upload. Native readback uses test-only COPY_SRC allocations; production allocation flags are unchanged.
In one final observation per placement, installing 2,970,968 bytes took 87.1 ms on main and 86.5 ms in the Worker, with 12 yields and 11 timer callbacks each. Longest observed timer gaps were 16.5 and 16.3 ms. Admission and readback are outside that measured interval. There is no before/after performance comparison or physical-device timing claim.
Acceptance record records the final checks and observed timings. Timer progress and SwiftShader byte correctness do not establish physical-device frame rate, broad driver compatibility, long-session stability, progressive mip delivery, compressed GPU formats or an aggregate multi-job scheduler. Those gates remain open, together with the required final character/environment/arrival art pass.