Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/typegpu/src/core/resolve/tgpuResolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 0 additions & 1 deletion packages/typegpu/src/indexNamedExports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
100 changes: 17 additions & 83 deletions packages/typegpu/src/tgpuBindGroupLayout.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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,
Expand All @@ -45,15 +36,15 @@ 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';
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';

Expand Down Expand Up @@ -268,22 +259,12 @@ type UnwrapRuntimeConstructorInner<T extends BaseData | ((_: number) => BaseData
export type UnwrapRuntimeConstructor<T extends BaseData | ((_: number) => BaseData)> =
T extends unknown ? UnwrapRuntimeConstructorInner<T> : never;

interface BindGroupLayoutInternals<Entries extends Record<string, TgpuLayoutEntry | null>> {
bound: { [K in keyof Entries]: BindLayoutEntry<Entries[K]> };
}

export interface TgpuBindGroupLayout<
Entries extends Record<string, TgpuLayoutEntry | null> = Record<string, TgpuLayoutEntry | null>,
> extends TgpuNamable {
readonly [$internal]: BindGroupLayoutInternals<Entries>;
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<Entries[K]>;
};
readonly [$gpuValueOf]: {
[K in keyof Entries]: InferLayoutEntry<Entries[K]>;
};
Expand Down Expand Up @@ -320,22 +301,6 @@ export interface TgpuBindGroupLayout<
unwrap(unwrapper: Unwrapper): GPUBindGroupLayout;
}

type StorageUsageForEntry<T extends TgpuLayoutStorage> = T extends {
access?: infer Access;
} // Is the access defined on the type?
? 'mutable' | 'readonly' extends Access // Is the access ambiguous?
?
| TgpuBufferReadonly<UnwrapRuntimeConstructor<T['storage']>>
| TgpuBufferMutable<UnwrapRuntimeConstructor<T['storage']>>
: 'readonly' extends Access // Is the access strictly 'readonly'?
? TgpuBufferReadonly<UnwrapRuntimeConstructor<T['storage']>>
: 'mutable' extends Access // Is the access strictly 'mutable'?
? TgpuBufferMutable<UnwrapRuntimeConstructor<T['storage']>>
:
| TgpuBufferReadonly<UnwrapRuntimeConstructor<T['storage']>>
| TgpuBufferMutable<UnwrapRuntimeConstructor<T['storage']>>
: TgpuBufferReadonly<UnwrapRuntimeConstructor<T['storage']>>; // <- access is undefined, so default to 'readonly';

export type LayoutEntryToInput<T extends TgpuLayoutEntry | null> =
// Widest type
TgpuLayoutEntry | null extends T
Expand Down Expand Up @@ -375,22 +340,6 @@ export type LayoutEntryToInput<T extends TgpuLayoutEntry | null> =
? GPUExternalTexture
: never;

export type BindLayoutEntry<T extends TgpuLayoutEntry | null> = T extends TgpuLayoutUniform
? TgpuBufferUniform<T['uniform']>
: T extends TgpuLayoutStorage
? StorageUsageForEntry<T>
: T extends TgpuLayoutSampler
? TgpuSampler
: T extends TgpuLayoutComparisonSampler
? TgpuComparisonSampler
: T extends TgpuLayoutTexture<infer TSchema>
? TgpuTextureView<TSchema>
: T extends TgpuLayoutStorageTexture<infer TSchema>
? TgpuTextureView<TSchema>
: T extends TgpuLayoutExternalTexture
? TgpuExternalTexture
: never;

export type InferLayoutEntry<T extends TgpuLayoutEntry | null> = T extends TgpuLayoutUniform
? InferGPU<T['uniform']>
: T extends TgpuLayoutStorage
Expand Down Expand Up @@ -466,7 +415,7 @@ class TgpuBindGroupLayoutImpl<
> implements TgpuBindGroupLayout<Entries> {
#index: number | undefined;

readonly [$internal]: BindGroupLayoutInternals<Entries>;
readonly [$internal]: ResolvableObject[];
readonly resourceType = 'bind-group-layout' as const;

readonly value = {} as {
Expand All @@ -485,65 +434,54 @@ class TgpuBindGroupLayoutImpl<

constructor(entries: Entries) {
this.entries = entries;
this[$internal] = [];

let idx = 0;

const bound = {} as { [K in keyof Entries]: BindLayoutEntry<Entries[K]> };
this[$internal] = { bound };

for (const [key, entry] of Object.entries(entries)) {
if (entry === null) {
idx++;
continue;
}

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++;
}
Expand All @@ -562,10 +500,6 @@ class TgpuBindGroupLayoutImpl<
return this;
}

public get bound() {
return this[$internal].bound;
}

$idx(index?: number): this {
this.#index = index;
return this;
Expand Down
65 changes: 0 additions & 65 deletions packages/typegpu/tests/bindGroupLayout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<TgpuTextureView<d.WgslTexture2d<d.F32>>>();
expectTypeOf(bar).toEqualTypeOf<
TgpuTextureView<d.WgslStorageTexture2d<'bgra8unorm', 'read-only'>>
>();
});
});

describe('texture layout', () => {
let layout2d: TgpuBindGroupLayout<{
foo: { texture: d.WgslTexture2d<d.F32> };
Expand Down Expand Up @@ -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<TgpuTextureView<d.WgslTexture2d<d.F32>>>();
expectTypeOf(bar).toEqualTypeOf<TgpuTextureView<d.WgslTextureCubeArray<d.F32>>>();
});
});

describe('storage texture layout', () => {
Expand Down Expand Up @@ -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<d.WgslStorageTexture3d<'rgba8unorm', 'write-only'>>
>();
expectTypeOf(bar).toEqualTypeOf<
TgpuTextureView<d.WgslStorageTexture2dArray<'rg32sint', 'write-only'>>
>();
});
});

describe('external texture layout', () => {
Expand Down
Loading