GPU Multiple Original Surface WebGPU Preparation - barocss/barocss GitHub Wiki
READY for bounded implementation after the actual existing-capability control and the selections below. The API does not exist until implemented; device/graphics qualification stays separate. This uses the actual Viewer880 two-original-Surface requirement and parallel-next-webgpu-20261011.md. #1552 native UV01 WGSL source is being implemented separately; this proposal does not depend on or silently admit that new material profile. Original52215 automatic denial and pending1512 human JSON remain unchanged.
Viewer880 receipts6066848840/6066908656 already qualify two original Source0 Surfaces with the same SurfaceID shared_panel, distinct aliases left_asset/right.asset, independent legacy materials, pixels, per-target changes, complete camera/pose/resize/refusal retention and disposal in its WebGL route. Existing common preparePrimitiveSceneFrame(input,'qualified-v2') admits1..4 qualified entries and0..4 legacy bindings. Current webgpu-primitive-viewport.ts:110 explicitly refuses any frame other than one Surface; create, identity1u, pick and selection also use primitives[0]. This is a real source-visible execution gap, not evidence that existing880 has WebGPU support.
Use ONE existing common preflight. GPU does not recompile CAD or certify packet-to-source/TEN relationships. Caller owns complete original CAD sources/models/revisions and constructs authentic public PreviewPacket0 plus qualified references. Packet millimetres/right-handed-Y-up/CCW/original positions/normals/native parameters/bounds/revision remain unchanged. Ref equality is the complete (modelId,geometryId) pair; geometryId alone is never an identity. Existing common validation enforces SurfaceID/ref agreement and distinct pairs. The new method is explicit; old1512 qualified-surface-v0, defaults, fallbacks, public types and all WebGL paths remain unchanged.
Runtime exports: mountMultiSurfaceWebGPUViewport, MultiSurfaceWebGPUError, merged MultiSurfaceWebGPUDiagnosticCode. Types: MultiSurfaceWebGPUOptions, MultiSurfaceWebGPUViewport, MultiSurfaceWebGPUStatus, MultiSurfaceWebGPUResources, MultiSurfaceWebGPULimits, MultiSurfaceWebGPUPick, and the merged code alias. Reuse PrimitiveSceneCpuFrameV0, PrimitiveSceneRef and UpdateResult without widening them. No public prepared-token/device injection/program-string input.
mountMultiSurfaceWebGPUViewport(canvas:HTMLCanvasElement,input:unknown,options:MultiSurfaceWebGPUOptions):Promise<MultiSurfaceWebGPUViewport>; options exact2 {width,height}, integers1..1024/product<=1048576. Required genuine current-realm Canvas/WebGPU; no automatic fallback in this additive API. Existing explicit WebGL2 APIs remain callable by hosts under their own contracts.
Input remains exact common frame5 {version:0,scene:{version:2,entries},options,particles:null,materials}. Admit1..4 Surface entries,0..4 exact legacy MeshMaterialBinding references, explicit existing camera, optional existing global MeshLighting. No empty frame, Curve, particle, cloth, generic EngineScene, EngineUV, nativeUV01, texture, discrete material or authored light entity. All supplied bindings must target a candidate Surface; no carry from accepted frame. Unbound entries use their admitted color. Common scene options and poses use actual guards; no new unit/frame conversion or inferred basis. Visibility is represented by the caller's complete admitted entry list, not a new boolean field.
Viewport readonly exact4 {canvas,frame,selected,status}. Methods updateFrame(input):Promise<UpdateResult>, resize(width,height):Promise<UpdateResult>, readPixels():Promise<Uint8Array>, pick(x,y):Promise<MultiSurfaceWebGPUPick>, select(ref|null):UpdateResult, dispose():void. frame is fresh frozen complete original frame; selected is fresh frozen ref/null. pick exact2 {generation,ref} reads actual r32uint attachment: background0, token i+1 in admitted entry order,1..N map full copied references. Integer top-left backing-pixel bounds required. Equal-depth ties retain first drawn entry under depthCompare less. No exact CAD/ray/occlusion claim beyond that rendered attachment. select validates current full ref, changes metadata only, does not highlight, redraw or increment generation. Successful frame replacement retains selection only if that exact pair remains, regardless of entry order; otherwise clears it. Failed replacement retains it.
Snapshot complete frame/options synchronously before first await, invoke common preflight once, then trusted legacy lowerMeshMaterialWGSL once for each bound Surface. Lowerer validates complete source; no reinterpretation of caller WGSL or prepared wrappers. Per entry immutable original packet/ref/pose/material stays distinct from independently owned mutable F32 position/normal and U16 views. Existing prepareSceneMesh already applies mmToSceneUnit*(translationMm+R*diag(scale)*originalMm)+options.translate and inverse-transpose/normalized world normals once; upload those results unchanged. Native parameters retain mm-domain authority and do not become texture coordinates. No per-entry geometry sharing/cache in this first bounded route, even when aliases reference identical original geometry.
Trusted per-entry program reuses legacy material arithmetic, clamp/mix, singleton lighting and native power1/2.2 output. Fixed identity literal i+1 is generated from bounded order; it is not a caller symbol. Frame160 bytes view64/projection64/light16/style16, material64 bytes; little-endian DataView packing at fixed offsets. Default lighting exactly existing direction[-.6,.8,1], ambient.35,directional.65. Unbound base color uses inherited pow(color,2.2). Preserve legacy alpha1/color policy rather than silently imposing generic1184 standard transfer. All material numbers must remain finite and nonzero as F32 when nonzero originally; resolved uniform own descriptors/declared ranges checked before packing. Each entry has independent224 uniform bytes. Explicit bind/pipeline layout includes both bindings even when WGSL does not read material data. Slots position0/normal1 stride12; U16 index buffer padded to four-byte storage without changing count. No UV slot/native1552 dependency.
Fresh independent frame getter copies cannot expose live accepted typed arrays. Ordinary originals are frozen; caller inputs are not frozen/mutated. Internal staged views cannot alias callers, other calls or accepted candidate views. Signed zeros in original Numbers/source remain originals; resolved binder zeros and F32 execution values keep existing canonicalization. No public document/codec/schema upgrade; future file proof retains original sources and full frame separately.
For N entries1..4, V_i<=4096,T_i<=7938. Per entry position12V+normal12V+U16indices6T, maximum145932B; four maximum583728B. Actual padded indices add at most2B per entry; charge actual padded sizes. Uniform224N<=896B. Allocate per candidate5N buffers (three geometry plus two uniform), N pipelines, N bindGroups, and three textures shared across all entries: RGBA8 color, r32uint identity, depth24plus. No one framebuffer/target per Surface. Render one pass/shared depth and present one complete candidate.
Resources exact11 fields: geometryBytes,uniformBytes,colorIdentityBytes,depthTexels,depthBudgetBytes,chargedBytes,readbackBytes,buffers,textures,bindGroups,pipelines. colorIdentityBytes=8WH,depthTexels=WH,depthBudgetBytes=4WH is a logical charge, not measured depth24plus allocation. chargedBytes=sum geometry/uniform/colorIdentity/depth charge. Candidate<=32MiB; accepted+candidate+largest padded readback<=64MiB. readbackBytes=ceil(4W/256)256H<=4194304; pick readback256. Maximum candidate at1024square is13167536B for maximal unpadded geometry; indices/padding charged exactly. Maximum simultaneous buffers41 (accepted20+candidate20+one readback), textures6,groups8,pipelines8; no driver-private memory/caches certified.
Limits exact existing9 fields: maxTextureDimension2D>=1024,maxBufferSize>=4194304,maxUniformBufferBindingSize>=160,maxColorAttachments>=2,maxColorAttachmentBytesPerSample>=8,maxVertexBuffers>=2,maxVertexAttributes>=2,maxBindGroups>=1,maxVertexBufferArrayStride>=12. Use fixed RGBA8unorm offscreen/presentation format only if actual canvas admission supports it; otherwise refuse UNSUPPORTED. One sample, depth24plus, no MSAA/HDR/blend/sorting/culling expansion. Original common per-entry pose/range/native/F32 safety and material256nodes/depth16/source64KiB inherited; per-programWGSL64KiB and aggregate ordinary result2MiB are selected guards to select before code.
Status exact8 {backend:'webgpu',profile:'qualified-multiple-surface-wgsl-v0',state,generation,width,height,limits,resources}. States ready/lost/failed/disposed; generation1 only after genuine first complete submission/presentation/error-scope success. Before success no public viewport. Status values are copied/frozen; resource ledger is logical ownership, not hardware cleanup proof.
Diagnostic map exact10 INPUT/PROFILE/BUDGET/REPRESENTATION/UNSUPPORTED/INIT/DEVICE_LOST/RENDER/STATE/BUSY. Message<=1024. New raw reflection always fresh INPUT with exact own cause even brandedError/undefined; localguards outside reflectioncatch have no invented cause. Whole common-preflight refusal INPUT with actual inherited cause; unsupported admitted kinds/profile PROFILE; F32 packing REPRESENTATION; actual lowerer cause PROFILE or REPRESENTATION only after exact mapping is selected. Do not improve inherited getter/reflection semantics by assertion. Update/resize/select return actual UpdateResult; mount/read/pick reject with actual error. First failure retains all already-owned handles and exact cause; cleanup failures cannot replace it.
One operation pending, including readback and selection BUSY. Out-of-band backing resize refuses STATE. New candidate resources stay separate; after queue completion/error scopes/presentation and epoch recheck, accept frame/dimensions/generation/selection then release previous. Validation/staging refusal leaves accepted state and pixels untouched. If presentation was attempted, restore accepted dimensions and color; failed recovery returns ordered AggregateError[original,recovery], terminal failed with CPU provenance retained and no pixel retention promise. Actual device.lost is terminal lost, distinct from recovery failure; no automatic fallback/remount. Ten-second await cap and epoch invalidate late continuations, release late owned handles; never assign them after dispose/timeout. Canvas claim is exclusive and released on failed mount/dispose; no foreign device/context cleanup. dispose is idempotent, attempts all owned buffer/texture/context/device/listener/claim cleanup, clears references/selection; strict cleanup failure semantics need Root selection below. Lost/failed retain copied original frame/status but disallow mutations/readback; dispose remains callable.
Selected owned paths: NEW multi-surface-webgpu-frame.ts (admission/types/errors/packing), NEW multi-surface-webgpu-viewport.ts (actual backend), additive index.ts/README.md, NEW examples/gpu-scene/multi-surface-webgpu-flow.mjs. Reuse common preflight/camera/legacy lowerer/private platform types; no old1512/1184 provider/kernel edits. Source-based reuse of common device utilities must not widen legacy behavior or create an unrelated framework. Root owns exact implementation scope and final evidence.
ONE source/emitted/API/file/cold acceptance uses two authentic complete Source0 assets with same SurfaceID/distinct aliases, actual native compile/tessellate and explicit camera/poses/global lighting, independent material defaults/overrides. Independent arrays/224Npacking/order/identity/ledger, max-four bounded admission, alias/material/wrongkind/getter/F32 refusals retain previous complete files. Save full source and common original frames, cold recompile/rebuild through real APIs; no saved prepared token authority and no replay880/1512.
The same named flow's later eligible original-target stage compares genuine two-Surface pixels/shared-depth overlap and trusted physical qualified hits/background miss, per-target edit retaining sibling, same-ID native source/pose/camera/resize, invalid-frame retention, full original file remount, both actual handle disposals and live console. Loss/late failure/driver cleanup are separate UNRUN unless actual witnesses occur. Device/browser gates do not block code/source stage after Root freeze, but no source gate can qualify pixels or consumer/publication.
- Public names, exact shapes, nonempty1..4 Surface legacy-material profile, required WebGPU only and no new fallback are selected as above. Clean implementation parent9c1b80b6977f9a259d0f31f21b67677e9e9b1764; isolatedcodex/gpu-multiple-surface-webgpu. FIVE owned product paths above. Root owns backend/index/README/final evidence; B staging; C one example.
- Fix rgba8unorm offscreen AND canvas, srgb canvas colorSpace/opaque alpha, explicit render-attachment/copy-dst usage. Actual context.configure plus genuine error scopes decide compatibility; getPreferredCanvasFormat is not a precondition. Fixed rgba8 means noBGRA swizzle. Native1512 pow2.2/pow1over2.2 policy and direct unencoded frame.background clear are retained exactly. This is an explicit new route, no accidental generic1184 transfer union.
- Exact ten codes and lowerer/cleanup/accounting policies selected in the following section. Attempt every handle; firstdispose throwsRENDER on cleanup failure, seconddispose no-op. Failed-mount internal cleanup preserves primary. Timeout failed, actualdevice loss lost.
- CPU preparer remains internal, is not exported from packageindex. Example builds an explicit source-only entry for internal staging plus the actual ordinary public package, pins all real local source inputs, and verifies public emitted exports/types. The backend always accepts originals and re-admits them; no prepared-token/device/program injection seam. Private PreparedMultiSurfaceWebGPUFrame exact6 {frame,width,height,draws,resources,background}; draws are bounded ordered exact10 {ref,identity,wgsl,positions,normals,indices,indexCount,frameUniforms,materialUniforms,material}, material is actual lowered legacy result ornull. Uint8Arrays are independently owned LE execution views. background is direct original options background RGBAalpha1.
- ONE genuine fresh existing-capability control FIRSTexit0 from immutable60fc416a38325cc9d5f8cecbe649d6c53630cb5d pinned315sourceinputs. Two originalSource0 assets share shared_panel under left_asset/right.asset,25V32tri each,2real legacyMeshmaterials. Publiccommonpreflight qualified-v2 admitted2entries2materials/1584B; once-scaled positions independently checked. Same original frame actual publicprepareWebGPUPrimitiveFrame refusedPROFILE/owncauseabsent with one-Surface message; originals retained exactly. No fakeGPU/device/browser/build/oldflow. Relevant common/camera/material/singlebackend source bytes equal clean9c1b implementation parent. Native1552 is independent, not a dependency.
Use the proposed ten-code map INPUT/PROFILE/BUDGET/REPRESENTATION/UNSUPPORTED/INIT/DEVICE_LOST/RENDER/STATE/BUSY. Preserve an already-owned MultiSurfaceWebGPUError unchanged only outside raw reflection catches. Every raw reflection throw becomes a fresh INPUT with exact own nonenumerable cause, even if it already has that class/code or is undefined. Local guards execute outside reflection catch and have no cause. No caller message coercion and no fabricated material start/end fields.
- New outer options/frame shape reflection or field guards: INPUT; locally measured size/work overflow: BUDGET. Unsupported admitted primitive/material profile: PROFILE. Explicit nonempty1..4/all-Surface checks run after common preflight. Inherited common preflight failure becomes INPUT with its exact thrown value as cause; do not infer a new budget category from message text. This matches the existing1512 wrapper's admission boundary and retains the original GPUError/RangeError/source cause chain.
- Catch only the direct lowerMeshMaterialWGSL call. For actual MeshMaterialError: code BUDGET maps to new BUDGET; code RANGE maps to REPRESENTATION; every other code maps to PROFILE. Preserve that exact MeshMaterialError as cause. Lowerer's RANGE specifically describes finite/nonzero F32 execution in this leaf; do not apply that reinterpretation to unrelated common native errors. Any other direct lowerer throw maps PROFILE with exact cause. Do not use diagnostic-message matching.
- Lowerer validates original source once and inherits its complete2MiB output guard. Resolved uniform packing after the call checks own descriptors, finite Number and declared range. Missing/non-numeric/out-of-range own resolved values are local PROFILE/no cause; a valid Number becoming nonfinite or zero from nonzero as F32 is local REPRESENTATION/no cause. Reflected descriptor failure is fresh INPUT/exact cause. Use direct finite descriptor reads, not inherited property lookup; this covers accepted lexical proto without changing old binders or banning authored names.
- Actual asynchronous GPU/context/canvas failures map INIT during mount and RENDER during later operations unless a locally owned diagnostic already specifies UNSUPPORTED/STATE/BUSY/DEVICE_LOST. Preserve actual cause objects.
updateFrame/resizereturn{ok:false,error}; mount/readPixels/pick reject. Timeout is STATE and terminal failed, not a fabricated device loss.
Reuse the policy of existing1184 release(), not the old1512 stop-on-first destroy loop. New per-candidate/readback/device/claim release functions collect unknown[], never throw while collecting, attempt all owned handles, and mark released before the first destruction. They clear owned references in finally; they never touch a foreign context/process/device. Destroy calls and logical ledger changes are not observed hardware-zero claims.
Cleanup encounter order is deterministic: candidate buffers in allocation order, candidate textures in allocation order, readback unmap then destroy, canvas unconfigure if configured, device.destroy if owned/not already attempted, listener/claim cleanup. Release accepted then staged candidate then readback at terminal cleanup. No second destruction attempt after the release marker; subsequent disposal is a no-op. Handle failures are retained as original values without coercion.
- If an operation failed and cleanup succeeds, return/rethrow the exact owned primary error. If it is raw, wrap once at the operational boundary with exact raw cause.
- If operation failure and cleanup failure coexist, return new RENDER whose cause is a new AggregateError
[primaryError, ...cleanupFailures]; freeze the owned errors array without altering any constituent error. Do not replace the primary with a destroy error. Nested original/recovery aggregation remains nested rather than flattened, preserving stage identity. - If presentation fails and accepted recovery also fails, new RENDER cause is AggregateError
[originalPresentationError, recoveryError], terminal failed. Later terminal cleanup failure wraps that pair as the first element followed by cleanup failures. CPU original frame remains retained, pixels are unavailable and selection cannot be used. - If presentation commits successfully but release(previous) fails, generation/frame are already the new accepted values. Report RENDER and terminal failed; do not claim old-frame rollback or decrement generation. Release the now-owned new candidate too. The returned error is a cleanup failure after commit, not validation retention.
- Readback cleanup refusal rejects the read even when pixels were read, and becomes terminal failed. Include any prior read failure first; do not return success with silently ignored cleanup failure.
- Genuine device.lost sets state lost. Preserve the actual loss witness plus cleanup failures inside a DEVICE_LOST error cause; cleanup failure must not change lost to failed. There is no automatic fallback. Timeout/dispose invalidates pending waits; late ownership cleanup never commits or emits an unhandled rejection.
- Select first
dispose():voidset state disposed/invalidate before cleanup, attempt all/release claim, clear selection, and throw RENDER on collected cleanup failures. A second call remains a no-op. A void TypeScript return allows this explicit new-route behavior; it is not a change to old1512 or1184 disposal. Failed-mount cleanup must use the internal collector rather than calling throwing dispose and masking mount error. Late asynchronous cleanup failures have no live caller; retain them in private terminal error state only, with no callbacks/public status-field expansion or fake synchronous witness.
Keep geometry/uniform/GPU residency byte accounting separate. A2MiB limit measures bounded ordinary source/program data, not actual GPU bytes or a promise that every maximum-count packet is admitted. Select the aggregate projection EXACT2 {frame,programs}. frame is the complete copied original PrimitiveSceneCpuFrameV0 from common preflight. programs is an ordered array of EXACT3 {ref,material,wgsl} per admitted Surface; material is its actual lowered legacy material result or null. Thus all original packets/native parameters/poses/material source, all complete lowered layouts/text and every program are charged. Do not charge prepared F32 geometry again through this projection; those bytes are independently charged by the resource ledger. Source strings repeated at separate paths count at each occurrence; no alias deduplication or Set-based discount.
Measure both signed-JSON UTF16 code units and UTF8 bytes, each<=2097152, with an incremental compiler-owned walker before final wrapper copy/freeze and before packed upload buffers/device work. Emit ordinary JSON delimiters/escaped string chunks/number spellings; Object.is(-0) emits -0. Count key names, commas, brackets, true/false/null. Stop at first excess without constructing a whole JSON string. The walker receives only already-admitted compiler-owned ordinary data, never raw caller objects/proxies/getters. Bound traversal independently to262144 value visits/depth48; excessive count/depth is BUDGET. This is a result-work guard, not a new caller snapshot that replaces existing provider checks.
Each complete program WGSL text independently remains<=65536 UTF16 andUTF8; each inherited lowerer already counts its own result before copy. Apply aggregate counting once after programs are generated but before any final aggregate copy; bounded common preflight and individual lowerer may already allocate their admitted results. Do not promise counting before all inherited allocations. Do not call copyGpuData on typed arrays: its implementation treats them as ordinary numeric-key objects. Only ordinary originals/results use that copier; packed Uint8/F32/U16 views need explicit independent allocations and freeze must skip views.
On valid aggregate overflow emit local BUDGET without cause; it is distinct from lowererBUDGET with actual cause. If no authentic accepted source can force aggregate overflow within existing native caps, record the defensive branch UNFORCED. No forged geometry/control can qualify it. Root must freeze the EXACT2 accounting projection along with this choice; if the final result shape changes, explicitly change the projection before code rather than counting an unspecified private object graph.
These policies provide concrete selections for Root, not a standing review gate. Only this private file was written; writes stopped.
Status READY for native implementation; SOURCE/API/FILE/COLD, actual device/pixels/lifetime, Integration adoption, consumer adoption and publication are distinct pending stages. Original52215 access denial/manualflow/server preserved. No product merge or release.
Native1553 receipt6109088498: clean9c1b80b6977f9a259d0f31f21b67677e9e9b1764 -> 00467c164f4dc73d2a54caceb90e13226e469a9e, ONE commit/FIVE frozen paths/799 additions0deletions. Root backend/index/README/finalcustody; B internal source staging; C ONE original-file qualifier; A independent next owned source preparation. Three runtime exports preserve all161 earlier names; private preparation is not public input authority.
FIRST affectedbuild0 / firstsyntax0. FIRSTflow1 at realCurve refusal because qualifier used foreignmodelalias curve_asset for source curve_refusal. Actual common guard correctly refusedINPUT. Firstscript/files/stderr preserved; onlyexamplemodelId repaired. SAME scopedflowretry1 passed0, empty stderr:5admit16ref/getters0/2rawreflections/164namespace/316sourceinputs includingoneprivateentry/onechild8coldrows. Independent authentic1..4Surface originalsource/pose/mm/normals/camera/materialLE160+64/order/token/nativecolor/ledger/file/copies/refusals qualified. Result102311B/SHA4558bb5161bc0f955b80b1c2d3e079bf465989cf8336627f87e9de7747c08535. Exactfivecommittedblobs and realexecutedsourceinputs verified clean.
Native SOURCE/BACKEND/API/FILE/COLD COMPLETE; immutablehandoff sent ONCE to soleIntegration, toolaccepted/readnotinferred. AdoptionPENDING. Actualdevice/configuration/limits/WGSLcompilation/pixels/shareddepth/physicalpick/pixelretention/file-remount/loss/recovery/drivercleanup/Viewer/Render/currentRoot/publicationUNRUN. Resourcecaps/2MiB/tree/textceilings/oddindexpadding remain unforced. Hosts own an exclusive canvas for this route; localclaim protects this route's handles, not a new cross-backendregistry. Original52215 denial/pendingmanualJSON/server/usertab unchanged. Additive adoption must retainlater1552/currentpeer index/README suffixes from the moving Integration head.
Integration6109171987: localf5604d855ec06d258241477354cd0fa1c1415f8d / actualparentd62cde2fec31c73ec70257b908c72a7e4b5779dd. Root one bounded fixed-source audit confirms exact five-path scope, three new byte-identical files and exact native5-line/53-line additive suffixes after the complete current parent index/README, retaining1552/peer additions; failures0. No native build/flow replay. Native implementation and local source availability are complete; actual device/WGSL/pixels/shareddepth/physicalpick/control/recovery/loss/driver/consumer/currentRoot/publication remain UNRUN; #1553OPEN. The native qualifier intentionally pins9c inputs/namespace and can refuse later movingRoot; no silent adaptation. Original52215 denial/manualJSON/server/tab preserved. READY1563 source generator continues independently.