Repository navigation
Conversation
GPUTableComputation and GPUTableShaderBindings bind each GPUData chunk at its own byteOffset. WebGPU requires storage-binding offsets to be multiples of minStorageBufferOffsetAlignment (256 bytes by default), so a chunk that starts inside a shared buffer, for example a per-batch view of one allocation, failed later with an opaque validation error or a skipped dispatch. Assert the alignment when bindings are prepared. The existing node tests bound views at offset 4, which WebGPU rejects; move them to aligned offsets. Add a WebGPU test that dispatches aligned per-batch views of one buffer and rejects an unaligned view, plus a NullDevice test for GPUTableShaderBindings. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Related: #3338 fixes two other bugs in the same binders. |
Resolve the table-computation spec import conflict for the move of GPU tables to @luma.gl/graph/gpu-tables. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
[Medium impact] The change reaches GPU table storage-binding workflows in the graph package. Fix the rejection test's leftover GPU commands before merging. FindingsSummaryAdds storage-offset checks to
Reviews (2) · Last reviewed commit: "Merge origin/master into akre54/gpu-tabl..." · Reviewed by Greptile |
| const data = batch.gpuData[input.columnName]; | ||
| if (data) { | ||
| bindings[storageBindingName] = getGPUDataBinding(data); | ||
| bindings[storageBindingName] = getGPUDataBinding(device, data); |
There was a problem hiding this comment.
Rejected views leave GPU buffers
GPUTableShaderBindings now rejects an unaligned storage view after prepareBindings has allocated its row-multiplier buffer and any constant buffers. Those buffers remain in the local ownedBuffers map when construction throws, so the caller cannot release them through destroy(). Repeated failures can accumulate unused GPU memory.
Check offsets before allocating buffers, or release newly allocated buffers when preparation fails.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| expect(() => unalignedComputation.dispatchBatches(unalignedPass, 1)).toThrow(); | ||
| unalignedPass.end(); | ||
|
|
||
| computation.destroy(); | ||
| unalignedComputation.destroy(); | ||
| buffer.destroy(); |
There was a problem hiding this comment.
Rejected dispatch breaks later tests
dispatchBatches() records the aligned first batch before the second batch throws. The test then ends the pass without submitting it and destroys buffer.
The compute test group reuses the device, so the next device.submit() includes this leftover dispatch with a destroyed buffer. WebGPU can reject that submission, including the next test's work. Submit the ended pass before destroying its resources, or discard it using a separate command encoder.
For #3326 (split out of it; independent of master)
Background
GPUTableComputationandGPUTableShaderBindingsbind eachGPUDatachunk as a storage binding at its ownbyteOffset. WebGPU requires storage-binding offsets to be multiples ofminStorageBufferOffsetAlignment(256 bytes by default). A chunk that starts inside a shared buffer, such as a per-batch view of one allocation, failed later with an opaque validation error or a silently skipped dispatch.Rationale
User WGSL indexes each binding from its first element. The binders cannot transparently rebind an unaligned view at an aligned offset without shader cooperation, so the fix is to reject the case clearly. The alignment is asserted where bindings are prepared, with the explanation in a comment (minimal runtime validation). The rule only applies on WebGPU: WebGL and NullDevice report an alignment of 0 and are unaffected. Producers that create shared-buffer views should place views at aligned offsets. The pending opt-in packed analytics uploads do this.
Change List
gpu-table-computation.ts,gpu-table-shader-bindings.ts: assertbyteOffset % minStorageBufferOffsetAlignment === 0for storage bindings.gpu-table-computation.node.spec.ts: the two binding tests used offset 4, which WebGPU rejects; they now use 256. They nowskipinstead of returning silently without WebGPU. Node specs never get a WebGPU device, so these tests still skip in CI.table-computation.spec.ts(headless): dispatches two aligned per-batch views of one buffer and checks both results, then rejects a view one row past alignment.gpu-table-shader-bindings.node.spec.ts: NullDevice with a 256-byte alignment accepts an aligned storage view and rejects an unaligned one.gpu-table-shader-bindings.mdx,gpu-storage-buffers.md.Verification
nvm use: Node 22.22.1.yarn install: run in a fresh worktree.yarn lint fix: passed.yarn build: passed.yarn test-node: 3,680 passed, 10 skipped.experimental/test/gpu-tables,experimental/test/models,splats/test,text/test, the arrow model and path specs, andgpu-constant-render. 18 files, 164 tests passed on native Apple Metal WebGPU.yarn test,yarn website:build,(cd website && yarn build).Risks
Code that bound unaligned storage views on WebGPU already got validation errors; it now gets an assertion when bindings are prepared. Vertex attributes in
GPUTableShaderBindingsstill ignore a chunk's startingbyteOffset. That is a separate, pre-existing limitation that this PR does not change. Empty chunks also still bind with size 0, which WebGPU rejects (pre-existing, unchanged).🤖 Generated with Claude Code
Note
Low Risk
Adds early validation for a hard WebGPU hardware constraint that previously caused downstream validation errors.
Overview
Enforces WebGPU storage offset alignment rules for GPU table bindings. Both
GPUTableComputationandGPUTableShaderBindingsnow assert that a chunk'sbyteOffsetis a multiple ofdevice.limits.minStorageBufferOffsetAlignmentwhen preparing storage bindings.This surfaces unaligned shared-buffer views immediately during binding preparation rather than failing later with opaque WebGPU validation errors or skipped dispatches.
Reviewed by Cursor Bugbot for commit bed1431. Bugbot is set up for automated code reviews on this repo. Configure here.