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 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
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.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 @@ -23,10 +23,10 @@ const BoxObstacle = d.struct({

const gridSize = 256;

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.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 @@ -50,5 +50,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 @@ -9,7 +9,7 @@ export const computeJunctionGradient = tgpu.fn([d.vec2i], d.vec2f)((pos) => {
return randOnUnitCircle();
});

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 @@ -9,9 +9,7 @@ export const computeJunctionGradient = tgpu.fn([d.vec3i], d.vec3f)((pos) => {
return randOnUnitSphere();
});

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

const dotProdGrid = tgpu.fn([d.vec3f, d.vec3f], d.f32)((pos, junction) => {
const relative = sub(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 @@ -23,6 +23,7 @@ export const tgpu = {
fn,
bindGroupLayout,
vertexLayout,
slot,

init,
initFromDevice,
Expand All @@ -43,6 +44,9 @@ export const tgpu = {
*/
vertexLayout,
derived,
/**
* @deprecated This feature is now stable, use tgpu.slot.
*/
slot,
accessor,
privateVar,
Expand Down
14 changes: 7 additions & 7 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);
const foo = tgpu.slot<number>(1);
const computeDouble = vi.fn(() => {
return foo.value * 2;
});
Expand All @@ -31,7 +31,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.fn([], d.f32)(() => {
Expand Down Expand Up @@ -68,7 +68,7 @@ describe('TgpuDerived', () => {
});

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

const fill = tgpu['~unstable'].derived(() => {
const gridSize = gridSizeSlot.value;
Expand Down Expand Up @@ -106,7 +106,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 @@ -200,13 +200,13 @@ describe('TgpuDerived', () => {
});

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

expect(() => parseResolved({ fn })).toThrow(
'Cannot create tgpu.derived objects at the resolution stage.',
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.fn([], d.vec3f)`() {
return colorSlot;
Expand All @@ -28,7 +28,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.fn([], d.vec3f)`() {
return colorSlot;
Expand Down Expand Up @@ -59,7 +59,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.fn([], d.vec3f)`() {
return colorSlot;
Expand Down Expand Up @@ -91,7 +91,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.fn([], d.vec3f)`() {
return colorSlot;
Expand All @@ -108,7 +108,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.fn([], d.vec3f)`() -> vec3f {
return colorSlot;
Expand Down Expand Up @@ -155,8 +155,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.fn([], d.f32)`() { return sizeSlot; }`
.$uses({ sizeSlot });
Expand Down Expand Up @@ -270,10 +270,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.fn([])`() { let value = slotA; }`
.$uses({ slotA });
Expand Down Expand Up @@ -302,7 +302,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 @@ -312,12 +312,12 @@ 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 getColor = tgpu.fn([], d.vec3f)(() => d.vec3f(1, 2, 3));
const colorAccess = tgpu['~unstable'].accessor(d.vec3f, getColor);
const colorAccessSlot = tgpu['~unstable'].slot(colorAccess);
const colorAccessSlot = tgpu.slot(colorAccess);

const func = tgpu.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 @@ -711,7 +711,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.fn([], d.f32)(() => {
const value = testSlot.value.value;
return value.x + value.y + value.z;
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