Skip to content

feat: Stabilize slots #1424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 30, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const cells3 = root.createReadonly(Cells);
We give slots *camelCase* names with the `Slot` suffix.

```ts
const colorSlot = tgpu['~unstable'].slot(vec4f(1, 0, 0, 1));
const colorSlot = tgpu.slot(vec4f(1, 0, 0, 1));
```

## Accessors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ const tanhSharpen = tgpu['~unstable'].fn([d.f32, d.f32], d.f32)(
(n, sharpness) => tanh(n * (1 + sharpness * 10)),
);

const sharpenFnSlot = tgpu['~unstable'].slot<
TgpuFn<(n: d.F32, sharpness: d.F32) => d.F32>
>(
const sharpenFnSlot = tgpu.slot<TgpuFn<(n: d.F32, sharpness: d.F32) => d.F32>>(
exponentialSharpen,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const patternSolid = patternFn(() => {
return 1;
});

const patternSlot = tgpu['~unstable'].slot(patternSolid);
const patternSlot = tgpu.slot(patternSolid);

// #endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ const gridSize = 256;
const gridAlphaBuffer = root.createBuffer(GridData).$usage('storage');
const gridBetaBuffer = root.createBuffer(GridData).$usage('storage');

const inputGridSlot = tgpu['~unstable'].slot<
const inputGridSlot = tgpu.slot<
TgpuBufferReadonly<GridData> | TgpuBufferMutable<GridData>
>();
const outputGridSlot = tgpu['~unstable'].slot<TgpuBufferMutable<GridData>>();
const outputGridSlot = tgpu.slot<TgpuBufferMutable<GridData>>();

const MAX_OBSTACLES = 4;
const BoxObstacleArray = d.arrayOf(BoxObstacle, MAX_OBSTACLES);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,8 @@ export const renderSkyBoxVertexLayout = tgpu.vertexLayout((n) =>
);

export const cameraAccess = tgpu['~unstable'].accessor(Camera);
export const filteringSamplerSlot = tgpu['~unstable'].slot<TgpuSampler>();
export const skyBoxSlot = tgpu['~unstable'].slot<
TgpuSampledTexture<'cube', d.F32>
>();
export const filteringSamplerSlot = tgpu.slot<TgpuSampler>();
export const skyBoxSlot = tgpu.slot<TgpuSampledTexture<'cube', d.F32>>();
export const lightSourceAccess = tgpu['~unstable'].accessor(d.vec3f);
export const timeAccess = tgpu['~unstable'].accessor(Time);

Expand Down
2 changes: 1 addition & 1 deletion packages/typegpu-color/src/oklab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ const gamutClipAdaptiveL0cusp = tgpu['~unstable'].fn([vec3f], vec3f)((lab) => {
return vec3f(L_clipped, C_clipped * a_, C_clipped * b_);
});

export const oklabGamutClipSlot = tgpu['~unstable'].slot(gamutClipAdaptiveL05);
export const oklabGamutClipSlot = tgpu.slot(gamutClipAdaptiveL05);
export const oklabGamutClip = {
preserveChroma: gamutClipPreserveChroma,
adaptiveL05: gamutClipAdaptiveL05,
Expand Down
5 changes: 3 additions & 2 deletions packages/typegpu-noise/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ export const BPETER: StatefulGenerator = (() => {
// The default (Can change between releases to improve uniformity).
export const DefaultGenerator: StatefulGenerator = BPETER;

export const randomGeneratorSlot: TgpuSlot<StatefulGenerator> =
tgpu['~unstable'].slot(DefaultGenerator);
export const randomGeneratorSlot: TgpuSlot<StatefulGenerator> = tgpu.slot(
DefaultGenerator,
);
2 changes: 1 addition & 1 deletion packages/typegpu-noise/src/perlin-2d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const computeJunctionGradient = tgpu['~unstable'].fn([d.vec2i], d.vec2f)(
},
);

export const getJunctionGradientSlot = tgpu['~unstable'].slot(
export const getJunctionGradientSlot = tgpu.slot(
computeJunctionGradient,
);

Expand Down
4 changes: 1 addition & 3 deletions packages/typegpu-noise/src/perlin-3d/algorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ export const computeJunctionGradient = tgpu['~unstable'].fn([d.vec3i], d.vec3f)(
},
);

export const getJunctionGradientSlot = tgpu['~unstable'].slot(
computeJunctionGradient,
);
export const getJunctionGradientSlot = tgpu.slot(computeJunctionGradient);

const dotProdGrid = tgpu['~unstable'].fn([d.vec3f, d.vec3f], d.f32)(
(pos, junction) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/typegpu-noise/src/perlin-3d/dynamic-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export function dynamicCacheConfig<Prefix extends string>(
): DynamicPerlin3DCacheConfig<Prefix> {
const { prefix = DefaultPerlin3DLayoutPrefix as Prefix } = options ?? {};

const valuesSlot = tgpu['~unstable'].slot<LayoutValue<Prefix>>();
const valuesSlot = tgpu.slot<LayoutValue<Prefix>>();

const cleanValuesSlot = tgpu['~unstable'].derived(() => {
return {
Expand Down
4 changes: 4 additions & 0 deletions packages/typegpu/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { bindGroupLayout } from './tgpuBindGroupLayout.ts';
export const tgpu = {
bindGroupLayout,
vertexLayout,
slot,

init,
initFromDevice,
Expand All @@ -39,6 +40,9 @@ export const tgpu = {
*/
vertexLayout,
derived,
/**
* @deprecated This feature is now stable, use tgpu.slot.
*/
slot,
accessor,
privateVar,
Expand Down
23 changes: 11 additions & 12 deletions packages/typegpu/tests/derived.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { parse, parseResolved } from './utils/parseResolved.ts';

describe('TgpuDerived', () => {
it('memoizes results of transitive "derived"', () => {
const foo = tgpu['~unstable'].slot<number>(1).$name('foo');
const foo = tgpu.slot<number>(1);
const computeDouble = vi.fn(() => {
return foo.value * 2;
});
Expand All @@ -33,7 +33,7 @@ describe('TgpuDerived', () => {
});

it('memoizes functions using derived values', () => {
const foo = tgpu['~unstable'].slot<number>();
const foo = tgpu.slot<number>();
const double = tgpu['~unstable'].derived(() => foo.value * 2);

const getDouble = tgpu['~unstable']
Expand Down Expand Up @@ -74,7 +74,7 @@ describe('TgpuDerived', () => {
});

it('can use slot values from its surrounding context', () => {
const gridSizeSlot = tgpu['~unstable'].slot<number>().$name('gridSize');
const gridSizeSlot = tgpu.slot<number>();

const fill = tgpu['~unstable'].derived(() => {
const gridSize = gridSizeSlot.value;
Expand All @@ -97,8 +97,7 @@ describe('TgpuDerived', () => {
fillWith2.value(exampleArray);
fillWith3.value(exampleArray);
})
.with(gridSizeSlot, 1)
.$name('main');
.with(gridSizeSlot, 1);

expect(parseResolved({ main })).toBe(
parse(/* wgsl */ `
Expand All @@ -116,7 +115,7 @@ describe('TgpuDerived', () => {
});

it('allows access to value in tgsl functions through the .value property ', ({ root }) => {
const vectorSlot = tgpu['~unstable'].slot(d.vec3f(1, 2, 3));
const vectorSlot = tgpu.slot(d.vec3f(1, 2, 3));
const doubledVectorSlot = tgpu['~unstable'].derived(() => {
const vec = vectorSlot.value;

Expand Down Expand Up @@ -216,14 +215,14 @@ describe('TgpuDerived', () => {
});

it('does not allow defining derived values at resolution', () => {
const slot = tgpu['~unstable'].slot<number>(2).$name('gridSize');
const derived = tgpu['~unstable'].derived(() =>
slot.value > 0
? tgpu['~unstable'].derived(() => slot.value).value
: tgpu['~unstable'].derived(() => -slot.value).value
const gridSizeSlot = tgpu.slot<number>(2);
const absGridSize = tgpu['~unstable'].derived(() =>
gridSizeSlot.value > 0
? tgpu['~unstable'].derived(() => gridSizeSlot.value).value
: tgpu['~unstable'].derived(() => -gridSizeSlot.value).value
);
const fn = tgpu['~unstable'].fn([], d.u32)(() => {
return derived.value;
return absGridSize.$;
});

expect(() => parseResolved({ fn })).toThrow(
Expand Down
30 changes: 15 additions & 15 deletions packages/typegpu/tests/slot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const GREEN = 'vec3f(0., 1., 0.)';

describe('tgpu.slot', () => {
it('resolves to default value if no value provided', () => {
const colorSlot = tgpu['~unstable'].slot(RED); // red by default
const colorSlot = tgpu.slot(RED); // red by default

const getColor = tgpu['~unstable']
.fn([], d.vec3f)(/* wgsl */ `() -> vec3f {
Expand All @@ -30,7 +30,7 @@ describe('tgpu.slot', () => {
});

it('resolves to provided value rather than default value', () => {
const colorSlot = tgpu['~unstable'].slot(RED); // red by default
const colorSlot = tgpu.slot(RED); // red by default

const getColor = tgpu['~unstable']
.fn([], d.vec3f)(/* wgsl */ `() -> vec3f {
Expand Down Expand Up @@ -64,7 +64,7 @@ describe('tgpu.slot', () => {
});

it('resolves to provided value', () => {
const colorSlot = tgpu['~unstable'].slot<string>(); // no default
const colorSlot = tgpu.slot<string>(); // no default

const getColor = tgpu['~unstable']
.fn([], d.vec3f)(/* wgsl */ `() -> vec3f {
Expand Down Expand Up @@ -100,7 +100,7 @@ describe('tgpu.slot', () => {
});

it('throws error when no default nor value provided', () => {
const colorSlot = tgpu['~unstable'].slot<string>().$name('color');
const colorSlot = tgpu.slot<string>().$name('color');

const getColor = tgpu['~unstable']
.fn([], d.vec3f)(`() -> vec3f {
Expand All @@ -119,7 +119,7 @@ describe('tgpu.slot', () => {
});

it('prefers closer scope', () => {
const colorSlot = tgpu['~unstable'].slot<string>(); // no default
const colorSlot = tgpu.slot<string>(); // no default

const getColor = tgpu['~unstable']
.fn([], d.vec3f)(/* wgsl */ `() -> vec3f {
Expand Down Expand Up @@ -172,8 +172,8 @@ describe('tgpu.slot', () => {
});

it('reuses common nested functions', () => {
const sizeSlot = tgpu['~unstable'].slot<1 | 100>();
const colorSlot = tgpu['~unstable'].slot<typeof RED | typeof GREEN>();
const sizeSlot = tgpu.slot<1 | 100>();
const colorSlot = tgpu.slot<typeof RED | typeof GREEN>();

const getSize = tgpu['~unstable']
.fn([], d.f32)(/* wgsl */ `() -> f32 {
Expand Down Expand Up @@ -299,10 +299,10 @@ describe('tgpu.slot', () => {
});

it('unwraps layers of slots', () => {
const slotA = tgpu['~unstable'].slot(1);
const slotB = tgpu['~unstable'].slot(2);
const slotC = tgpu['~unstable'].slot(3);
const slotD = tgpu['~unstable'].slot(4);
const slotA = tgpu.slot(1);
const slotB = tgpu.slot(2);
const slotC = tgpu.slot(3);
const slotD = tgpu.slot(4);

const fn1 = tgpu['~unstable']
.fn([])('() { let value = slotA; }')
Expand Down Expand Up @@ -341,7 +341,7 @@ describe('tgpu.slot', () => {
});

it('allows access to value in tgsl functions through the .value property ', ({ root }) => {
const vectorSlot = tgpu['~unstable'].slot(d.vec3f(1, 2, 3));
const vectorSlot = tgpu.slot(d.vec3f(1, 2, 3));
const Boid = d
.struct({
pos: d.vec3f,
Expand All @@ -351,16 +351,16 @@ describe('tgpu.slot', () => {

const buffer = root.createBuffer(Boid).$usage('uniform').$name('boid');
const uniform = buffer.as('uniform');
const uniformSlot = tgpu['~unstable'].slot(uniform);
const uniformSlotSlot = tgpu['~unstable'].slot(uniformSlot);
const uniformSlot = tgpu.slot(uniform);
const uniformSlotSlot = tgpu.slot(uniformSlot);

const colorAccessorFn = tgpu['~unstable'].accessor(
d.vec3f,
tgpu['~unstable']
.fn([], d.vec3f)(() => d.vec3f(1, 2, 3))
.$name('getColor'),
);
const colorAccessorSlot = tgpu['~unstable'].slot(colorAccessorFn);
const colorAccessorSlot = tgpu.slot(colorAccessorFn);

const func = tgpu['~unstable'].fn([])(() => {
const pos = vectorSlot.value;
Expand Down
4 changes: 2 additions & 2 deletions packages/typegpu/tests/tgsl/wgslGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const { NodeTypeCatalog: NODE } = tinyest;

const transpiler = new JitTranspiler();

const numberSlot = tgpu['~unstable'].slot(44);
const numberSlot = tgpu.slot(44);
const derivedV4u = tgpu['~unstable'].derived(() =>
std.mul(d.u32(numberSlot.value), d.vec4u(1, 2, 3, 4))
);
Expand Down Expand Up @@ -723,7 +723,7 @@ describe('wgslGenerator', () => {
const testBuffer = root.createBuffer(UnfortunateStruct).$usage('storage');

const testUsage = testBuffer.as('mutable');
const testSlot = tgpu['~unstable'].slot(testUsage);
const testSlot = tgpu.slot(testUsage);
const testFn = tgpu['~unstable']
.fn([], d.f32)(() => {
const value = testSlot.value.value;
Expand Down
4 changes: 2 additions & 2 deletions packages/typegpu/tests/unplugin/autoname.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ import { it } from '../utils/extendedIt.ts';

describe('autonaming', () => {
it('autonames resources created using tgpu', () => {
const mySlot = tgpu.slot<number>();
const myLayout = tgpu.bindGroupLayout({ foo: { uniform: d.vec3f } });
const myVertexLayout = tgpu.vertexLayout((n: number) =>
d.arrayOf(d.i32, n)
);

expect(getName(mySlot)).toBe('mySlot');
expect(getName(myLayout)).toBe('myLayout');
expect(getName(myVertexLayout)).toBe('myVertexLayout');
});

it("autonames resources created using tgpu['~unstable']", () => {
const mySlot = tgpu['~unstable'].slot<number>();
const myAccessor = tgpu['~unstable'].accessor(d.f32);
const myPrivateVar = tgpu['~unstable'].privateVar(d.vec2f);
const myWorkgroupVar = tgpu['~unstable'].workgroupVar(d.f32);
const myConst = tgpu['~unstable'].const(d.f32, 1);

expect(getName(mySlot)).toBe('mySlot');
expect(getName(myAccessor)).toBe('myAccessor');
expect(getName(myPrivateVar)).toBe('myPrivateVar');
expect(getName(myWorkgroupVar)).toBe('myWorkgroupVar');
Expand Down