WebGPU adapter: GPUBuffer is missing the spec's `mapState`
Environment: Electrobun 2.0.1 (Hutch 0.25.0), Cottontail main process, macOS 26.5.1 arm64, bundleWGPU: true, system webview.
What happens
GPUBuffer.mapState is a readonly attribute in WebGPU returning "unmapped", "pending", or "mapped". The adapter's GPUBuffer does not define it, so it reads back as undefined.
The class already tracks the state it needs — _mapped in api/sdks/main/webgpuAdapter.ts (2.0.1 devkit), whose lifecycle is complete and correct:
- line 1763 — set from
mappedAtCreationin the constructor - line 1807 — set
trueon a successfulmapAsync - line 1860 — set
falseinunmap()
It is simply never exposed under the spec's name.
Reproduction
const buffer = device.createBuffer({
size: 16,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
mappedAtCreation: true,
});
console.log(buffer.mapState); // undefined — spec requires "mapped"(Note the enum objects GPUBufferUsage and friends are also not installed as globals by webgpu.install(), so a standalone repro has to define them first — that is #542.)
Why it matters
Code that guards a mapped-buffer access on mapState — the spec-sanctioned way to ask — takes the wrong branch every time. In typegpu, any buffer created with an initial value sets mappedAtCreation: true and then checks mapState === "mapped" before writing, so it throws Buffer is not mapped. and no uniform can be initialized.
Suggested fix
get mapState(): "unmapped" | "pending" | "mapped" {
return this._mapped ? "mapped" : "unmapped";
}That covers the two states the adapter distinguishes, and is what I verified against 2.0.1 (defined on the prototype from application code): typegpu then initializes uniforms and renders correctly.
Fully spec-compliant behavior would also report "pending" between a mapAsync call and its resolution, which would mean tracking a third state on the existing MAP_ASYNC_RESOLVERS path. I did not need it, but flagging it so the fix isn't quietly incomplete.
Source: blackboardsh/electrobun