From 7cb4c4027399eed5d2826e61efa8fea5c05ec7ef Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Mon, 28 Sep 2026 10:33:55 -0400 Subject: [PATCH 1/2] fix(webgpu): convert index arguments to numbers setBindGroup and setVertexBuffer passed their index straight to the native binding, which only takes a u32, so an index from a for...in loop (a string key) failed with "Failed to convert napi value String into rust type u32". WebIDL converts it to a number; so do these now. --- packages/canvas/WebGPU/GPUComputePassEncoder.ts | 7 ++++--- packages/canvas/WebGPU/GPURenderBundleEncoder.ts | 9 +++++---- packages/canvas/WebGPU/GPURenderPassEncoder.ts | 9 +++++---- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/canvas/WebGPU/GPUComputePassEncoder.ts b/packages/canvas/WebGPU/GPUComputePassEncoder.ts index 30eca504e..12da7eac9 100644 --- a/packages/canvas/WebGPU/GPUComputePassEncoder.ts +++ b/packages/canvas/WebGPU/GPUComputePassEncoder.ts @@ -38,6 +38,7 @@ export class GPUComputePassEncoder { this[native_].pushDebugGroup(groupLabel); } + // Index arguments are converted to numbers, as WebIDL does: `for...in` keys arrive as strings. setBindGroup(index: number, bindGroup: GPUBindGroup, dynamicOffsetsData?: number[] | Uint32Array, dynamicOffsetsDataStart?: number, dynamicOffsetsDataLength?: number) { const group = bindGroup?.[native_]; if (!group) { @@ -45,11 +46,11 @@ export class GPUComputePassEncoder { } if (Array.isArray(dynamicOffsetsData)) { const data = new Uint32Array(dynamicOffsetsData); - this[native_].setBindGroup(index, group, data, 0, data.length); + this[native_].setBindGroup(Number(index), group, data, 0, data.length); } else if (dynamicOffsetsData instanceof Uint32Array) { - this[native_].setBindGroup(index, group, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength); + this[native_].setBindGroup(Number(index), group, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength); } else { - this[native_].setBindGroup(index, group); + this[native_].setBindGroup(Number(index), group); } } diff --git a/packages/canvas/WebGPU/GPURenderBundleEncoder.ts b/packages/canvas/WebGPU/GPURenderBundleEncoder.ts index dc0d20c49..470d5a44b 100644 --- a/packages/canvas/WebGPU/GPURenderBundleEncoder.ts +++ b/packages/canvas/WebGPU/GPURenderBundleEncoder.ts @@ -43,6 +43,7 @@ export class GPURenderBundleEncoder { this[native_].pushDebugGroup(groupLabel); } + // Index arguments are converted to numbers, as WebIDL does: `for...in` keys arrive as strings. setBindGroup(index: number, bindGroup: GPUBindGroup, dynamicOffsetsData?: number[] | Uint32Array, dynamicOffsetsDataStart?: number, dynamicOffsetsDataLength?: number) { const group = bindGroup?.[native_]; if (!group) { @@ -50,11 +51,11 @@ export class GPURenderBundleEncoder { } if (Array.isArray(dynamicOffsetsData)) { const data = new Uint32Array(dynamicOffsetsData); - this[native_].setBindGroup(index, group, data, 0, data.length); + this[native_].setBindGroup(Number(index), group, data, 0, data.length); } else if (dynamicOffsetsData instanceof Uint32Array) { - this[native_].setBindGroup(index, group, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength); + this[native_].setBindGroup(Number(index), group, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength); } else { - this[native_].setBindGroup(index, group); + this[native_].setBindGroup(Number(index), group); } } @@ -68,7 +69,7 @@ export class GPURenderBundleEncoder { } setVertexBuffer(slot: number, buffer: GPUBuffer, offset?: number, size?: number) { - this[native_].setVertexBuffer(slot, buffer[native_], offset ?? 0, size ?? buffer.size - (offset ?? 0)); + this[native_].setVertexBuffer(Number(slot), buffer[native_], offset ?? 0, size ?? buffer.size - (offset ?? 0)); } static fromNative(encoder) { diff --git a/packages/canvas/WebGPU/GPURenderPassEncoder.ts b/packages/canvas/WebGPU/GPURenderPassEncoder.ts index 42da448e1..617205fe1 100644 --- a/packages/canvas/WebGPU/GPURenderPassEncoder.ts +++ b/packages/canvas/WebGPU/GPURenderPassEncoder.ts @@ -74,6 +74,7 @@ export class GPURenderPassEncoder { this[native_].pushDebugGroup(groupLabel); } + // Index arguments are converted to numbers, as WebIDL does: `for...in` keys arrive as strings. setBindGroup(index: number, bindGroup: GPUBindGroup, dynamicOffsetsData?: number[] | Uint32Array, dynamicOffsetsDataStart?: number, dynamicOffsetsDataLength?: number) { const group = bindGroup?.[native_]; if (!group) { @@ -82,11 +83,11 @@ export class GPURenderPassEncoder { if (Array.isArray(dynamicOffsetsData)) { const data = new Uint32Array(dynamicOffsetsData); - this[native_].setBindGroup(index, group, data, 0, data.length); + this[native_].setBindGroup(Number(index), group, data, 0, data.length); } else if (dynamicOffsetsData instanceof Uint32Array) { - this[native_].setBindGroup(index, group, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength); + this[native_].setBindGroup(Number(index), group, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength); } else { - this[native_].setBindGroup(index, group); + this[native_].setBindGroup(Number(index), group); } } @@ -121,7 +122,7 @@ export class GPURenderPassEncoder { } setVertexBuffer(slot: number, buffer: GPUBuffer, offset?: number, size?: number) { - this[native_].setVertexBuffer(slot, buffer[native_], offset ?? 0, size ?? buffer.size - (offset ?? 0)); + this[native_].setVertexBuffer(Number(slot), buffer[native_], offset ?? 0, size ?? buffer.size - (offset ?? 0)); } setViewport(x, y, width, height, minDepth, maxDepth) { From 149cfd17f622458efed972b8acb6663b18d24028 Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Mon, 28 Sep 2026 10:33:55 -0400 Subject: [PATCH 2/2] fix(webgpu): mask configure usage to the surface's on desktop hosts configure only masked the requested usage to the surface's supported usages on Apple. A Node-API host's swapchain (a DirectX 12 one on Windows) has no TEXTURE_BINDING, so asking for it left the context unconfigured and getCurrentTexture() returned null. --- packages/canvas/WebGPU/GPUCanvasContext.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/canvas/WebGPU/GPUCanvasContext.ts b/packages/canvas/WebGPU/GPUCanvasContext.ts index 57c42bcbf..ff672d864 100644 --- a/packages/canvas/WebGPU/GPUCanvasContext.ts +++ b/packages/canvas/WebGPU/GPUCanvasContext.ts @@ -146,7 +146,8 @@ export class GPUCanvasContext implements CanvasRenderingContext { console.warn(`GPUCanvasContext: configure usage unsupported falling back to ${capabilities.usages}`); } - if (__APPLE__) { + // Keep to the usages the surface supports. + if (__APPLE__ || NAPI_HOST) { const supported = (capabilities && (capabilities as any).usages) || 0; const unsupported = opts.usage & ~supported; if (unsupported !== 0) {