From 6225fd4a5f600b9de85bb9092521779ede8a69c3 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sun, 21 Jun 2026 03:30:01 -0700 Subject: [PATCH] feat: accept buffer shorthands directly in createBindGroup --- packages/typegpu/src/tgpuBindGroupLayout.ts | 24 +++++- .../typegpu/tests/bindGroupLayout.test.ts | 74 +++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/packages/typegpu/src/tgpuBindGroupLayout.ts b/packages/typegpu/src/tgpuBindGroupLayout.ts index 77a9b57f13..f3336bbda8 100644 --- a/packages/typegpu/src/tgpuBindGroupLayout.ts +++ b/packages/typegpu/src/tgpuBindGroupLayout.ts @@ -1,4 +1,11 @@ import { isBuffer, type TgpuBuffer, type UniformFlag } from './core/buffer/buffer.ts'; +import { + isBufferShorthand, + type TgpuBufferShorthand, + type TgpuMutable, + type TgpuReadonly, + type TgpuUniform, +} from './core/buffer/bufferShorthand.ts'; import { isUsableAsUniform, type TgpuBufferMutable, @@ -341,6 +348,7 @@ export type LayoutEntryToInput = TgpuLayoutEntry | null extends T ? | TgpuBuffer + | TgpuBufferShorthand | GPUBuffer | TgpuSampler | GPUSampler @@ -350,10 +358,15 @@ export type LayoutEntryToInput = | GPUExternalTexture : // Strict type-checking T extends TgpuLayoutUniform - ? (TgpuBuffer>> & UniformFlag) | GPUBuffer + ? + | (TgpuBuffer>> & UniformFlag) + | TgpuUniform>> + | GPUBuffer : T extends TgpuLayoutStorage ? | (TgpuBuffer>> & StorageFlag) + | TgpuMutable>> + | TgpuReadonly>> | GPUBuffer : T extends TgpuLayoutSampler ? TgpuSampler | GPUSampler @@ -682,7 +695,14 @@ export class TgpuBindGroupImpl< return null; } - const value = this.entries[key as keyof typeof this.entries]; + // Buffer shorthands (uniform/mutable/readonly) are accepted directly + // for buffer-typed entries; unwrap them to their underlying buffer so + // the rest of the bind-group machinery is unchanged. + const entryValue = this.entries[key as keyof typeof this.entries]; + const value = (isBufferShorthand(entryValue) ? entryValue.buffer : entryValue) as Exclude< + (typeof this.entries)[keyof typeof this.entries], + TgpuBufferShorthand + >; if (value === undefined) { throw new Error( diff --git a/packages/typegpu/tests/bindGroupLayout.test.ts b/packages/typegpu/tests/bindGroupLayout.test.ts index b7fe5d5a81..c108b3297c 100644 --- a/packages/typegpu/tests/bindGroupLayout.test.ts +++ b/packages/typegpu/tests/bindGroupLayout.test.ts @@ -266,6 +266,80 @@ describe('TgpuBindGroup', () => { ], }); }); + + it('populates a uniform layout with a buffer shorthand', ({ root }) => { + const uniform = root.createUniform(d.vec3f); + const bindGroup = root.createBindGroup(layout, { foo: uniform }); + + root.unwrap(bindGroup); + + expect(root.device.createBindGroup).toBeCalledWith({ + label: 'example', + layout: root.unwrap(layout), + entries: [ + { + binding: 0, + resource: { + buffer: root.unwrap(uniform.buffer), + }, + }, + ], + }); + }); + }); + + describe('simple storage layout', () => { + let layout: TgpuBindGroupLayout<{ + foo: { storage: d.Vec3f; access: 'mutable' }; + }>; + + beforeEach(() => { + layout = tgpu + .bindGroupLayout({ + foo: { storage: d.vec3f, access: 'mutable' }, + }) + .$name('example'); + }); + + it('populates a storage layout with a mutable shorthand', ({ root }) => { + const mutable = root.createMutable(d.vec3f); + const bindGroup = root.createBindGroup(layout, { foo: mutable }); + + root.unwrap(bindGroup); + + expect(root.device.createBindGroup).toBeCalledWith({ + label: 'example', + layout: root.unwrap(layout), + entries: [ + { + binding: 0, + resource: { + buffer: root.unwrap(mutable.buffer), + }, + }, + ], + }); + }); + + it('populates a storage layout with a readonly shorthand', ({ root }) => { + const readonly = root.createReadonly(d.vec3f); + const bindGroup = root.createBindGroup(layout, { foo: readonly }); + + root.unwrap(bindGroup); + + expect(root.device.createBindGroup).toBeCalledWith({ + label: 'example', + layout: root.unwrap(layout), + entries: [ + { + binding: 0, + resource: { + buffer: root.unwrap(readonly.buffer), + }, + }, + ], + }); + }); }); describe('simple layout with atomics', () => {