From dd4b95af4349aae5e853100831fae928cc486031 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Tue, 30 Jun 2026 11:17:02 +0200 Subject: [PATCH] Remove .bound from layout --- .../typegpu/src/core/resolve/tgpuResolve.ts | 2 +- packages/typegpu/src/indexNamedExports.ts | 1 - packages/typegpu/src/tgpuBindGroupLayout.ts | 100 +++--------------- .../typegpu/tests/bindGroupLayout.test.ts | 65 ------------ 4 files changed, 18 insertions(+), 150 deletions(-) diff --git a/packages/typegpu/src/core/resolve/tgpuResolve.ts b/packages/typegpu/src/core/resolve/tgpuResolve.ts index 71ff0487f6..ecd6b42df4 100644 --- a/packages/typegpu/src/core/resolve/tgpuResolve.ts +++ b/packages/typegpu/src/core/resolve/tgpuResolve.ts @@ -233,7 +233,7 @@ function resolveFromArray( for (const item of items) { // Support for: tgpu.resolve([layout]) if (isBindGroupLayout(item)) { - for (const binding of Object.values(item[$internal].bound)) { + for (const binding of Object.values(item[$internal])) { ctx.resolve(binding); } } else { diff --git a/packages/typegpu/src/indexNamedExports.ts b/packages/typegpu/src/indexNamedExports.ts index 8a4ba4d469..b100534b84 100644 --- a/packages/typegpu/src/indexNamedExports.ts +++ b/packages/typegpu/src/indexNamedExports.ts @@ -92,7 +92,6 @@ export type { } from './core/sampler/sampler.ts'; export type { TgpuQuerySet } from './core/querySet/querySet.ts'; export type { - BindLayoutEntry, ExtractBindGroupInputFromLayout, LayoutEntryToInput, TgpuBindGroup, diff --git a/packages/typegpu/src/tgpuBindGroupLayout.ts b/packages/typegpu/src/tgpuBindGroupLayout.ts index 77a9b57f13..0f69e1c4a8 100644 --- a/packages/typegpu/src/tgpuBindGroupLayout.ts +++ b/packages/typegpu/src/tgpuBindGroupLayout.ts @@ -1,11 +1,5 @@ import { isBuffer, type TgpuBuffer, type UniformFlag } from './core/buffer/buffer.ts'; -import { - isUsableAsUniform, - type TgpuBufferMutable, - type TgpuBufferReadonly, - type TgpuBufferUniform, - TgpuLaidOutBufferImpl, -} from './core/buffer/bufferUsage.ts'; +import { isUsableAsUniform, TgpuLaidOutBufferImpl } from './core/buffer/bufferUsage.ts'; import { isComparisonSampler, isSampler, @@ -17,10 +11,7 @@ import { comparisonSampler as wgslComparisonSampler, sampler as wgslSampler, } from './data/sampler.ts'; -import { - type TgpuExternalTexture, - TgpuExternalTextureImpl, -} from './core/texture/externalTexture.ts'; +import { TgpuExternalTextureImpl } from './core/texture/externalTexture.ts'; import { isTexture, isTextureView, @@ -45,7 +36,7 @@ import { } from './data/texture.ts'; import type { StorageTextureFormats } from './core/texture/textureFormats.ts'; import type { AnyWgslData, BaseData, F32, I32, U32 } from './data/wgslTypes.ts'; -import { NotUniformError } from './errors.ts'; +import { invariant, NotUniformError } from './errors.ts'; import { isUsableAsStorage, NotStorageError, type StorageFlag } from './extension.ts'; import type { TgpuNamable } from './shared/meta.ts'; import { getName, setName } from './shared/meta.ts'; @@ -53,7 +44,7 @@ import type { InferGPU, MemIdentity } from './shared/repr.ts'; import { safeStringify } from './shared/stringify.ts'; import { $gpuValueOf, $internal } from './shared/symbols.ts'; import type { Default, NullableToOptional, Prettify } from './shared/utilityTypes.ts'; -import type { TgpuShaderStage } from './types.ts'; +import type { ResolvableObject, TgpuShaderStage } from './types.ts'; import type { Unwrapper } from './unwrapper.ts'; import type { WgslComparisonSampler, WgslSampler } from './data/sampler.ts'; @@ -268,22 +259,12 @@ type UnwrapRuntimeConstructorInner BaseData export type UnwrapRuntimeConstructor BaseData)> = T extends unknown ? UnwrapRuntimeConstructorInner : never; -interface BindGroupLayoutInternals> { - bound: { [K in keyof Entries]: BindLayoutEntry }; -} - export interface TgpuBindGroupLayout< Entries extends Record = Record, > extends TgpuNamable { - readonly [$internal]: BindGroupLayoutInternals; + readonly [$internal]: ResolvableObject[]; readonly resourceType: 'bind-group-layout'; readonly entries: Entries; - /** - * @deprecated Use `layout.$.foo` instead of `layout.bound.foo.$` - */ - readonly bound: { - [K in keyof Entries]: BindLayoutEntry; - }; readonly [$gpuValueOf]: { [K in keyof Entries]: InferLayoutEntry; }; @@ -320,22 +301,6 @@ export interface TgpuBindGroupLayout< unwrap(unwrapper: Unwrapper): GPUBindGroupLayout; } -type StorageUsageForEntry = T extends { - access?: infer Access; -} // Is the access defined on the type? - ? 'mutable' | 'readonly' extends Access // Is the access ambiguous? - ? - | TgpuBufferReadonly> - | TgpuBufferMutable> - : 'readonly' extends Access // Is the access strictly 'readonly'? - ? TgpuBufferReadonly> - : 'mutable' extends Access // Is the access strictly 'mutable'? - ? TgpuBufferMutable> - : - | TgpuBufferReadonly> - | TgpuBufferMutable> - : TgpuBufferReadonly>; // <- access is undefined, so default to 'readonly'; - export type LayoutEntryToInput = // Widest type TgpuLayoutEntry | null extends T @@ -375,22 +340,6 @@ export type LayoutEntryToInput = ? GPUExternalTexture : never; -export type BindLayoutEntry = T extends TgpuLayoutUniform - ? TgpuBufferUniform - : T extends TgpuLayoutStorage - ? StorageUsageForEntry - : T extends TgpuLayoutSampler - ? TgpuSampler - : T extends TgpuLayoutComparisonSampler - ? TgpuComparisonSampler - : T extends TgpuLayoutTexture - ? TgpuTextureView - : T extends TgpuLayoutStorageTexture - ? TgpuTextureView - : T extends TgpuLayoutExternalTexture - ? TgpuExternalTexture - : never; - export type InferLayoutEntry = T extends TgpuLayoutUniform ? InferGPU : T extends TgpuLayoutStorage @@ -466,7 +415,7 @@ class TgpuBindGroupLayoutImpl< > implements TgpuBindGroupLayout { #index: number | undefined; - readonly [$internal]: BindGroupLayoutInternals; + readonly [$internal]: ResolvableObject[]; readonly resourceType = 'bind-group-layout' as const; readonly value = {} as { @@ -485,12 +434,10 @@ class TgpuBindGroupLayoutImpl< constructor(entries: Entries) { this.entries = entries; + this[$internal] = []; let idx = 0; - const bound = {} as { [K in keyof Entries]: BindLayoutEntry }; - this[$internal] = { bound }; - for (const [key, entry] of Object.entries(entries)) { if (entry === null) { idx++; @@ -498,52 +445,43 @@ class TgpuBindGroupLayoutImpl< } const membership: LayoutMembership = { layout: this, key, idx }; + let item: undefined | (ResolvableObject & { $: unknown }) = undefined; if ('uniform' in entry) { - // oxlint-disable-next-line typescript/no-explicit-any -- no need for type magic - (bound[key] as any) = new TgpuLaidOutBufferImpl('uniform', entry.uniform, membership); + item = new TgpuLaidOutBufferImpl('uniform', entry.uniform, membership); } if ('storage' in entry) { const dataType = 'type' in entry.storage ? entry.storage : entry.storage(0); - - // oxlint-disable-next-line typescript/no-explicit-any -- no need for type magic - (bound[key] as any) = new TgpuLaidOutBufferImpl( - entry.access ?? 'readonly', - dataType, - membership, - ); + item = new TgpuLaidOutBufferImpl(entry.access ?? 'readonly', dataType, membership); } if ('texture' in entry) { - // oxlint-disable-next-line typescript/no-explicit-any -- no need for type magic - (bound[key] as any) = new TgpuLaidOutTextureViewImpl(entry.texture, membership); + item = new TgpuLaidOutTextureViewImpl(entry.texture, membership); } if ('storageTexture' in entry) { - // oxlint-disable-next-line typescript/no-explicit-any -- no need for type magic - (bound[key] as any) = new TgpuLaidOutTextureViewImpl(entry.storageTexture, membership); + item = new TgpuLaidOutTextureViewImpl(entry.storageTexture, membership); } if ('externalTexture' in entry) { - // oxlint-disable-next-line typescript/no-explicit-any -- no need for type magic - (bound[key] as any) = new TgpuExternalTextureImpl(entry.externalTexture, membership); + item = new TgpuExternalTextureImpl(entry.externalTexture, membership); } if ('sampler' in entry) { - // oxlint-disable-next-line typescript/no-explicit-any -- no need for type magic - (bound[key] as any) = new TgpuLaidOutSamplerImpl( + item = new TgpuLaidOutSamplerImpl( entry.sampler === 'comparison' ? wgslComparisonSampler() : wgslSampler(), membership, ); } + invariant(item !== undefined, 'Internal error, expected item to be defined'); Object.defineProperty(this.value, key, { get: () => { - // oxlint-disable-next-line typescript/no-explicit-any -- no need for type magic - return (bound[key] as any).value; + return item.$; }, }); + this[$internal].push(item); idx++; } @@ -562,10 +500,6 @@ class TgpuBindGroupLayoutImpl< return this; } - public get bound() { - return this[$internal].bound; - } - $idx(index?: number): this { this.#index = index; return this; diff --git a/packages/typegpu/tests/bindGroupLayout.test.ts b/packages/typegpu/tests/bindGroupLayout.test.ts index 95b83fef3a..d0309261ff 100644 --- a/packages/typegpu/tests/bindGroupLayout.test.ts +++ b/packages/typegpu/tests/bindGroupLayout.test.ts @@ -550,38 +550,6 @@ describe('TgpuBindGroup', () => { }); }); - describe('legacy texture layout', () => { - it('supports legacy texture definitions and converts the types correctly', ({ root }) => { - const layout = tgpu.bindGroupLayout({ - foo: { texture: 'float', viewDimension: '2d' }, - bar: { storageTexture: 'bgra8unorm', access: 'readonly' }, - }); - - const bg = root.createBindGroup(layout, { - foo: root - .createTexture({ - size: [64, 64], - format: 'rgba8unorm', - }) - .$usage('sampled'), - bar: root - .createTexture({ - size: [64, 64], - format: 'bgra8unorm', - }) - .$usage('storage'), - }); - - expect(bg).toBeDefined(); - - const { foo, bar } = layout.bound; - expectTypeOf(foo).toEqualTypeOf>>(); - expectTypeOf(bar).toEqualTypeOf< - TgpuTextureView> - >(); - }); - }); - describe('texture layout', () => { let layout2d: TgpuBindGroupLayout<{ foo: { texture: d.WgslTexture2d }; @@ -744,21 +712,6 @@ describe('TgpuBindGroup', () => { // foo: texture3d, // }); }); - - it('properly fill the bound property', () => { - const layout = tgpu.bindGroupLayout({ - foo: { texture: d.texture2d(d.f32) }, - bar: { - texture: d.textureCubeArray(d.f32), - sampleType: 'unfilterable-float', - }, - }); - - const { foo, bar } = layout.bound; - - expectTypeOf(foo).toEqualTypeOf>>(); - expectTypeOf(bar).toEqualTypeOf>>(); - }); }); describe('storage texture layout', () => { @@ -939,24 +892,6 @@ describe('TgpuBindGroup', () => { // foo: texture2d, // }); }); - - it('properly fill the bound property', () => { - const layout = tgpu.bindGroupLayout({ - foo: { storageTexture: d.textureStorage3d('rgba8unorm', 'write-only') }, - bar: { - storageTexture: d.textureStorage2dArray('rg32sint', 'write-only'), - }, - }); - - const { foo, bar } = layout.bound; - - expectTypeOf(foo).toEqualTypeOf< - TgpuTextureView> - >(); - expectTypeOf(bar).toEqualTypeOf< - TgpuTextureView> - >(); - }); }); describe('external texture layout', () => {