From dd2ad9fc5e76d6597d733ad963eefb653440719c Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Thu, 16 Jan 2025 14:07:06 +0100 Subject: [PATCH 01/13] Return proxy from `.value` properties --- .../typegpu/src/core/buffer/bufferUsage.ts | 7 +- .../typegpu/src/core/constant/tgpuConstant.ts | 3 +- packages/typegpu/src/core/slot/accessor.ts | 3 +- .../typegpu/src/core/variable/tgpuVariable.ts | 3 +- .../typegpu/src/shared/valueProxyHandler.ts | 22 +++++ packages/typegpu/src/smol/wgslGenerator.ts | 9 -- packages/typegpu/tests/bufferUsage.test.ts | 89 ++++++++++++++++++- 7 files changed, 120 insertions(+), 16 deletions(-) create mode 100644 packages/typegpu/src/shared/valueProxyHandler.ts diff --git a/packages/typegpu/src/core/buffer/bufferUsage.ts b/packages/typegpu/src/core/buffer/bufferUsage.ts index 04bb25f0..3652b0a7 100644 --- a/packages/typegpu/src/core/buffer/bufferUsage.ts +++ b/packages/typegpu/src/core/buffer/bufferUsage.ts @@ -2,6 +2,7 @@ import type { AnyWgslData, BaseWgslData } from '../../data/wgslTypes'; import { type Storage, isUsableAsStorage } from '../../extension'; import { inGPUMode } from '../../gpuMode'; import type { Infer } from '../../shared/repr'; +import { valueProxyHandler } from '../../shared/valueProxyHandler'; import type { LayoutMembership } from '../../tgpuBindGroupLayout'; import type { BindableBufferUsage, @@ -103,10 +104,10 @@ class TgpuFixedBufferImpl< if (!inGPUMode()) { throw new Error(`Cannot access buffer's value directly in JS.`); } - return this as Infer; + + return new Proxy(this, valueProxyHandler) as Infer; } } - export class TgpuLaidOutBufferImpl< TData extends BaseWgslData, TUsage extends BindableBufferUsage, @@ -146,7 +147,7 @@ export class TgpuLaidOutBufferImpl< if (!inGPUMode()) { throw new Error(`Cannot access buffer's value directly in JS.`); } - return this as Infer; + return new Proxy(this, valueProxyHandler) as Infer; } } diff --git a/packages/typegpu/src/core/constant/tgpuConstant.ts b/packages/typegpu/src/core/constant/tgpuConstant.ts index 80c480ff..d67e07f4 100644 --- a/packages/typegpu/src/core/constant/tgpuConstant.ts +++ b/packages/typegpu/src/core/constant/tgpuConstant.ts @@ -2,6 +2,7 @@ import type { AnyWgslData } from '../../data/wgslTypes'; import { inGPUMode } from '../../gpuMode'; import type { TgpuNamable } from '../../namable'; import type { Infer } from '../../shared/repr'; +import { valueProxyHandler } from '../../shared/valueProxyHandler'; import type { ResolutionCtx, SelfResolvable } from '../../types'; import type { Exotic } from './../../data/exotic'; @@ -65,6 +66,6 @@ class TgpuConstImpl if (!inGPUMode()) { return this._value; } - return this as Infer; + return new Proxy(this, valueProxyHandler) as Infer; } } diff --git a/packages/typegpu/src/core/slot/accessor.ts b/packages/typegpu/src/core/slot/accessor.ts index c5d04baa..7da78d8b 100644 --- a/packages/typegpu/src/core/slot/accessor.ts +++ b/packages/typegpu/src/core/slot/accessor.ts @@ -2,6 +2,7 @@ import type { AnyWgslData } from '../../data'; import type { Exotic } from '../../data/exotic'; import { getResolutionCtx } from '../../gpuMode'; import type { Infer } from '../../shared/repr'; +import { valueProxyHandler } from '../../shared/valueProxyHandler'; import type { ResolutionCtx, SelfResolvable } from '../../types'; import { type TgpuBufferUsage, isBufferUsage } from '../buffer/bufferUsage'; import { type TgpuFn, isTgpuFn } from '../function/tgpuFn'; @@ -62,7 +63,7 @@ export class TgpuAccessorImpl ); } - return this as unknown as Infer; + return new Proxy(this, valueProxyHandler) as unknown as Infer; } '~resolve'(ctx: ResolutionCtx): string { diff --git a/packages/typegpu/src/core/variable/tgpuVariable.ts b/packages/typegpu/src/core/variable/tgpuVariable.ts index 07bedaa0..6a0d6275 100644 --- a/packages/typegpu/src/core/variable/tgpuVariable.ts +++ b/packages/typegpu/src/core/variable/tgpuVariable.ts @@ -3,6 +3,7 @@ import type { AnyWgslData } from '../../data/wgslTypes'; import { inGPUMode } from '../../gpuMode'; import type { TgpuNamable } from '../../namable'; import type { Infer } from '../../shared/repr'; +import { valueProxyHandler } from '../../shared/valueProxyHandler'; import type { ResolutionCtx, SelfResolvable } from '../../types'; // ---------- @@ -92,6 +93,6 @@ class TgpuVarImpl if (!inGPUMode()) { throw new Error(`Cannot access tgpu.var's value directly in JS.`); } - return this as Infer; + return new Proxy(this, valueProxyHandler) as Infer; } } diff --git a/packages/typegpu/src/shared/valueProxyHandler.ts b/packages/typegpu/src/shared/valueProxyHandler.ts new file mode 100644 index 00000000..4f09a484 --- /dev/null +++ b/packages/typegpu/src/shared/valueProxyHandler.ts @@ -0,0 +1,22 @@ +import type { Labelled, ResolutionCtx, SelfResolvable } from '../types'; + +export const valueProxyHandler: ProxyHandler = { + get(target, prop) { + if (prop in target) { + return Reflect.get(target, prop); + } + + return new Proxy( + { + '~resolve'(ctx: ResolutionCtx) { + return `${ctx.resolve(target)}.${String(prop)}`; + }, + + toString() { + return `.value:${target.label ?? ''}`; + }, + }, + valueProxyHandler, + ); + }, +}; diff --git a/packages/typegpu/src/smol/wgslGenerator.ts b/packages/typegpu/src/smol/wgslGenerator.ts index 166a7772..c3d5394b 100644 --- a/packages/typegpu/src/smol/wgslGenerator.ts +++ b/packages/typegpu/src/smol/wgslGenerator.ts @@ -122,15 +122,6 @@ function generateExpression( } if (isWgsl(target.value)) { - // NOTE: Temporary solution, assuming that access to `.value` of resolvables should always resolve to just the target. - if (propertyStr === 'value') { - return { - value: resolveRes(ctx, target), - // TODO: Infer data type - dataType: UnknownData, - }; - } - return { // biome-ignore lint/suspicious/noExplicitAny: value: (target.value as any)[propertyStr], diff --git a/packages/typegpu/tests/bufferUsage.test.ts b/packages/typegpu/tests/bufferUsage.test.ts index aff33920..c906f1a7 100644 --- a/packages/typegpu/tests/bufferUsage.test.ts +++ b/packages/typegpu/tests/bufferUsage.test.ts @@ -1,5 +1,7 @@ -import { describe, expectTypeOf } from 'vitest'; +import { parse } from 'tgpu-wgsl-parser'; +import { describe, expect, expectTypeOf } from 'vitest'; +import tgpu from '../src'; import { asUniform } from '../src/core/buffer/bufferUsage'; import * as d from '../src/data'; import type { Infer } from '../src/shared/repr'; @@ -12,4 +14,89 @@ describe('TgpuBufferUniform', () => { expectTypeOf>().toEqualTypeOf(); }); + + it('resolves to buffer binding in code', ({ root }) => { + const buffer = root.createBuffer(d.f32).$usage('uniform').$name('param'); + const uniform = asUniform(buffer); + + const resolved = tgpu.resolve({ + template: ` + fn m() { + let y = hello; + }`, + externals: { hello: uniform }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + @group(0) @binding(0) var param: f32; + + fn m() { + let y = param; + }`), + ); + }); + + it('resolves to buffer binding in tgsl functions', ({ root }) => { + const buffer = root.createBuffer(d.f32).$usage('uniform').$name('param'); + const uniform = asUniform(buffer); + + const func = tgpu['~unstable'].fn([]).does(() => { + const x = uniform.value; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + @group(0) @binding(0) var param: f32; + + fn func() { + var x = param; + }`), + ); + }); + + it('allows accessing fields in a struct stored in its buffer', ({ root }) => { + const Boid = d + .struct({ + pos: d.vec3f, + vel: d.vec3u, + }) + .$name('Boid'); + + const buffer = root.createBuffer(Boid).$usage('uniform').$name('boid'); + const uniform = asUniform(buffer); + + const func = tgpu['~unstable'].fn([]).does(() => { + const pos = uniform.value.pos; + const velX = uniform.value.vel.x; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + console.log(resolved); + + expect(parse(resolved)).toEqual( + parse(` + struct Boid { + pos: vec3f, + vel: vec3u, + } + + @group(0) @binding(0) var boid: Boid; + + fn func() { + var pos = boid.pos; + var velX = boid.vel.x; + }`), + ); + }); }); From b194dd3cadcd519375d8cf1cf1408a9a5a462d4c Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Fri, 17 Jan 2025 13:08:52 +0100 Subject: [PATCH 02/13] Fix for slots + more tests --- packages/typegpu/src/core/slot/derived.ts | 5 +- packages/typegpu/src/core/slot/slot.ts | 5 +- packages/typegpu/tests/bufferUsage.test.ts | 2 - packages/typegpu/tests/derived.test.ts | 64 +++++++++++++++++++++- packages/typegpu/tests/slot.test.ts | 50 ++++++++++++++++- 5 files changed, 119 insertions(+), 7 deletions(-) diff --git a/packages/typegpu/src/core/slot/derived.ts b/packages/typegpu/src/core/slot/derived.ts index 6acde9f0..48b38f0a 100644 --- a/packages/typegpu/src/core/slot/derived.ts +++ b/packages/typegpu/src/core/slot/derived.ts @@ -76,7 +76,10 @@ function createBoundDerived( ); } - return ctx.unwrap(this) as Infer; + const unwrapped = ctx.unwrap(this); + return (unwrapped && typeof unwrapped === 'object' && 'value' in unwrapped + ? unwrapped.value // TODO: user defined 'value' properties shouldn't be accessed here + : unwrapped) as unknown as Infer; }, with( diff --git a/packages/typegpu/src/core/slot/slot.ts b/packages/typegpu/src/core/slot/slot.ts index 526d7635..8f334231 100644 --- a/packages/typegpu/src/core/slot/slot.ts +++ b/packages/typegpu/src/core/slot/slot.ts @@ -39,6 +39,9 @@ class TgpuSlotImpl implements TgpuSlot { throw new Error(`Cannot access tgpu.slot's value outside of resolution.`); } - return ctx.unwrap(this) as Infer; + const unwrapped = ctx.unwrap(this); + return (unwrapped && typeof unwrapped === 'object' && 'value' in unwrapped + ? unwrapped.value // TODO: user defined 'value' properties shouldn't be accessed here + : unwrapped) as unknown as Infer; } } diff --git a/packages/typegpu/tests/bufferUsage.test.ts b/packages/typegpu/tests/bufferUsage.test.ts index c906f1a7..ad37131b 100644 --- a/packages/typegpu/tests/bufferUsage.test.ts +++ b/packages/typegpu/tests/bufferUsage.test.ts @@ -82,8 +82,6 @@ describe('TgpuBufferUniform', () => { names: 'strict', }); - console.log(resolved); - expect(parse(resolved)).toEqual( parse(` struct Boid { diff --git a/packages/typegpu/tests/derived.test.ts b/packages/typegpu/tests/derived.test.ts index c7c96b06..bde3bff8 100644 --- a/packages/typegpu/tests/derived.test.ts +++ b/packages/typegpu/tests/derived.test.ts @@ -1,7 +1,8 @@ import { parse } from 'tgpu-wgsl-parser'; import { describe, expect, vi } from 'vitest'; -import tgpu from '../src'; +import tgpu, { unstable_asUniform } from '../src'; import * as d from '../src/data'; +import { mul } from '../src/std'; import { it } from './utils/extendedIt'; import { parseResolved } from './utils/parseResolved'; @@ -119,4 +120,65 @@ 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 doubledVectorSlot = tgpu['~unstable'].derived(() => { + const vec = vectorSlot.value; + + return mul(2, vec); + }); + + const Boid = d + .struct({ + pos: d.vec3f, + vel: d.vec3u, + }) + .$name('Boid'); + + const buffer = root.createBuffer(Boid).$usage('uniform').$name('boid'); + const uniform = unstable_asUniform(buffer); + const uniformSlot = tgpu['~unstable'].slot(uniform); + + const derivedUniformSlot = tgpu['~unstable'].derived(() => { + const uniform = uniformSlot.value; + return uniform; + }); + + const derivedDerivedUniformSlot = tgpu['~unstable'].derived(() => { + const uniform = derivedUniformSlot.value; + return uniform; + }); + + const func = tgpu['~unstable'].fn([]).does(() => { + const pos = doubledVectorSlot.value; + const posX = doubledVectorSlot.value.x; + const vel = derivedDerivedUniformSlot.value.vel; + const velX = derivedDerivedUniformSlot.value.vel.x; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + struct Boid { + pos: vec3f, + vel: vec3u, + } + + @group(0) @binding(0) var boid: Boid; + + fn func(){ + var pos = (2 * vec3f(1, 2, 3)); + var posX = (2 * vec3f(1, 2, 3)).x; + var vel = boid.vel; + var velX = boid.vel.x; + }`), + ); + }); }); diff --git a/packages/typegpu/tests/slot.test.ts b/packages/typegpu/tests/slot.test.ts index 059bf563..27b429d7 100644 --- a/packages/typegpu/tests/slot.test.ts +++ b/packages/typegpu/tests/slot.test.ts @@ -1,8 +1,9 @@ import { parse } from 'tgpu-wgsl-parser'; -import { describe, expect, it } from 'vitest'; -import tgpu from '../src'; +import { describe, expect } from 'vitest'; +import tgpu, { unstable_asUniform } from '../src'; import * as d from '../src/data'; import { MissingSlotValueError, ResolutionError } from '../src/errors'; +import { it } from './utils/extendedIt'; import { parseResolved } from './utils/parseResolved'; const RED = 'vec3f(1., 0., 0.)'; @@ -373,4 +374,49 @@ describe('tgpu.slot', () => { expect(actual).toEqual(expected); }); + + it('allows access to value in tgsl functions through the .value property ', ({ + root, + }) => { + const vectorSlot = tgpu['~unstable'].slot(d.vec3f(1, 2, 3)); + const Boid = d + .struct({ + pos: d.vec3f, + vel: d.vec3u, + }) + .$name('Boid'); + + const buffer = root.createBuffer(Boid).$usage('uniform').$name('boid'); + const uniform = unstable_asUniform(buffer); + const uniformSlot = tgpu['~unstable'].slot(uniform); + + const func = tgpu['~unstable'].fn([]).does(() => { + const pos = vectorSlot.value; + const posX = vectorSlot.value.x; + const vel = uniformSlot.value.vel; + const velX = uniformSlot.value.vel.x; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + struct Boid { + pos: vec3f, + vel: vec3u, + } + + @group(0) @binding(0) var boid: Boid; + + fn func(){ + var pos = vec3f(1, 2, 3); + var posX = 1; + var vel = boid.vel; + var velX = boid.vel.x; + }`), + ); + }); }); From 0249b334635a4e0fc0413a80e44705ba6aa98c2e Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 20 Jan 2025 11:43:13 +0100 Subject: [PATCH 03/13] Fix proxy resolution --- .../typegpu/src/core/buffer/bufferUsage.ts | 17 ++++++- .../typegpu/src/core/constant/tgpuConstant.ts | 9 +++- packages/typegpu/src/core/slot/derived.ts | 5 ++- .../typegpu/src/core/variable/tgpuVariable.ts | 9 +++- .../typegpu/src/shared/valueProxyHandler.ts | 10 ++--- packages/typegpu/tests/variable.test.ts | 45 +++++++++++++++++++ 6 files changed, 84 insertions(+), 11 deletions(-) diff --git a/packages/typegpu/src/core/buffer/bufferUsage.ts b/packages/typegpu/src/core/buffer/bufferUsage.ts index 3652b0a7..d626b434 100644 --- a/packages/typegpu/src/core/buffer/bufferUsage.ts +++ b/packages/typegpu/src/core/buffer/bufferUsage.ts @@ -105,7 +105,13 @@ class TgpuFixedBufferImpl< throw new Error(`Cannot access buffer's value directly in JS.`); } - return new Proxy(this, valueProxyHandler) as Infer; + return new Proxy( + { + '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), + toString: (() => `.value:${this.label ?? ''}`).bind(this), + }, + valueProxyHandler, + ) as Infer; } } export class TgpuLaidOutBufferImpl< @@ -147,7 +153,14 @@ export class TgpuLaidOutBufferImpl< if (!inGPUMode()) { throw new Error(`Cannot access buffer's value directly in JS.`); } - return new Proxy(this, valueProxyHandler) as Infer; + + return new Proxy( + { + '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), + toString: (() => `.value:${this.label ?? ''}`).bind(this), + }, + valueProxyHandler, + ) as Infer; } } diff --git a/packages/typegpu/src/core/constant/tgpuConstant.ts b/packages/typegpu/src/core/constant/tgpuConstant.ts index d67e07f4..4d29e7ca 100644 --- a/packages/typegpu/src/core/constant/tgpuConstant.ts +++ b/packages/typegpu/src/core/constant/tgpuConstant.ts @@ -66,6 +66,13 @@ class TgpuConstImpl if (!inGPUMode()) { return this._value; } - return new Proxy(this, valueProxyHandler) as Infer; + + return new Proxy( + { + '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), + toString: (() => `.value:${this.label ?? ''}`).bind(this), + }, + valueProxyHandler, + ) as Infer as Infer; } } diff --git a/packages/typegpu/src/core/slot/derived.ts b/packages/typegpu/src/core/slot/derived.ts index 48b38f0a..149961c1 100644 --- a/packages/typegpu/src/core/slot/derived.ts +++ b/packages/typegpu/src/core/slot/derived.ts @@ -32,7 +32,10 @@ function createDerived(compute: () => T): TgpuDerived { ); } - return ctx.unwrap(this) as Infer; + const unwrapped = ctx.unwrap(this); + return (unwrapped && typeof unwrapped === 'object' && 'value' in unwrapped + ? unwrapped.value // TODO: user defined 'value' properties shouldn't be accessed here + : unwrapped) as unknown as Infer; }, with( diff --git a/packages/typegpu/src/core/variable/tgpuVariable.ts b/packages/typegpu/src/core/variable/tgpuVariable.ts index 6a0d6275..5e6602cb 100644 --- a/packages/typegpu/src/core/variable/tgpuVariable.ts +++ b/packages/typegpu/src/core/variable/tgpuVariable.ts @@ -93,6 +93,13 @@ class TgpuVarImpl if (!inGPUMode()) { throw new Error(`Cannot access tgpu.var's value directly in JS.`); } - return new Proxy(this, valueProxyHandler) as Infer; + + return new Proxy( + { + '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), + toString: (() => `.value:${this.label ?? ''}`).bind(this), + }, + valueProxyHandler, + ) as Infer; } } diff --git a/packages/typegpu/src/shared/valueProxyHandler.ts b/packages/typegpu/src/shared/valueProxyHandler.ts index 4f09a484..a8d71727 100644 --- a/packages/typegpu/src/shared/valueProxyHandler.ts +++ b/packages/typegpu/src/shared/valueProxyHandler.ts @@ -8,13 +8,11 @@ export const valueProxyHandler: ProxyHandler = { return new Proxy( { - '~resolve'(ctx: ResolutionCtx) { - return `${ctx.resolve(target)}.${String(prop)}`; - }, + '~resolve': (ctx: ResolutionCtx) => + `${ctx.resolve(target)}.${String(prop)}`, - toString() { - return `.value:${target.label ?? ''}`; - }, + toString: () => + `.value(...).${String(prop)}:${target.label ?? ''}`, }, valueProxyHandler, ); diff --git a/packages/typegpu/tests/variable.test.ts b/packages/typegpu/tests/variable.test.ts index c948dc84..8c77ee99 100644 --- a/packages/typegpu/tests/variable.test.ts +++ b/packages/typegpu/tests/variable.test.ts @@ -11,6 +11,7 @@ describe('var', () => { const fn1 = tgpu['~unstable'] .fn([]) .does(`() { + let y = x; return x; }`) .$uses({ x }) @@ -20,6 +21,7 @@ describe('var', () => { parse(` var x: u32 = 2; fn fn1() { + let y = x; return x; } `), @@ -97,4 +99,47 @@ describe('var', () => { var x: array = array(s(1, vec2i(2, 3)), s(4, vec2i(5, 6)));`, ); }); + + it('allows accessing variables in tgsl through .value', () => { + const Boid = d + .struct({ + pos: d.vec3f, + vel: d.vec3u, + }) + .$name('Boid'); + + const boidVariable = tgpu['~unstable'] + .privateVar(Boid, { + pos: d.vec3f(1, 2, 3), + vel: d.vec3u(4, 5, 6), + }) + .$name('boid'); + + const func = tgpu['~unstable'].fn([]).does(() => { + const pos = boidVariable.value; + const vel = boidVariable.value.vel; + const velX = boidVariable.value.vel.x; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + struct Boid { + pos: vec3f, + vel: vec3u, + } + + var boid: Boid = Boid(vec3f(1, 2, 3),vec3u(4, 5, 6)); + + fn func() { + var pos = boid; + var vel = boid.vel; + var velX = boid.vel.x; + }`), + ); + }); }); From bba1807e89fb1196e0340a441341c1ae48657127 Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 20 Jan 2025 12:03:57 +0100 Subject: [PATCH 04/13] More fixes --- packages/typegpu/src/core/constant/tgpuConstant.ts | 2 +- packages/typegpu/src/core/slot/accessor.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/typegpu/src/core/constant/tgpuConstant.ts b/packages/typegpu/src/core/constant/tgpuConstant.ts index 4d29e7ca..10e94929 100644 --- a/packages/typegpu/src/core/constant/tgpuConstant.ts +++ b/packages/typegpu/src/core/constant/tgpuConstant.ts @@ -73,6 +73,6 @@ class TgpuConstImpl toString: (() => `.value:${this.label ?? ''}`).bind(this), }, valueProxyHandler, - ) as Infer as Infer; + ) as Infer; } } diff --git a/packages/typegpu/src/core/slot/accessor.ts b/packages/typegpu/src/core/slot/accessor.ts index 7da78d8b..e31d4194 100644 --- a/packages/typegpu/src/core/slot/accessor.ts +++ b/packages/typegpu/src/core/slot/accessor.ts @@ -63,7 +63,13 @@ export class TgpuAccessorImpl ); } - return new Proxy(this, valueProxyHandler) as unknown as Infer; + return new Proxy( + { + '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), + toString: (() => `.value:${this.label ?? ''}`).bind(this), + }, + valueProxyHandler, + ) as Infer; } '~resolve'(ctx: ResolutionCtx): string { From 43329fef302cd1f01deb0eac00cdb06d80e71b83 Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 20 Jan 2025 14:38:07 +0100 Subject: [PATCH 05/13] More buffer usage tests --- packages/typegpu/tests/bufferUsage.test.ts | 190 ++++++++++++++++++++- 1 file changed, 189 insertions(+), 1 deletion(-) diff --git a/packages/typegpu/tests/bufferUsage.test.ts b/packages/typegpu/tests/bufferUsage.test.ts index ad37131b..91b883b9 100644 --- a/packages/typegpu/tests/bufferUsage.test.ts +++ b/packages/typegpu/tests/bufferUsage.test.ts @@ -2,7 +2,11 @@ import { parse } from 'tgpu-wgsl-parser'; import { describe, expect, expectTypeOf } from 'vitest'; import tgpu from '../src'; -import { asUniform } from '../src/core/buffer/bufferUsage'; +import { + asMutable, + asReadonly, + asUniform, +} from '../src/core/buffer/bufferUsage'; import * as d from '../src/data'; import type { Infer } from '../src/shared/repr'; import { it } from './utils/extendedIt'; @@ -98,3 +102,187 @@ describe('TgpuBufferUniform', () => { ); }); }); + +describe('TgpuBufferMutable', () => { + it('represents a `number` value', ({ root }) => { + const buffer = root.createBuffer(d.f32).$usage('storage'); + const mutable = asMutable(buffer); + + expectTypeOf>().toEqualTypeOf(); + }); + + it('resolves to buffer binding in code', ({ root }) => { + const buffer = root.createBuffer(d.f32).$usage('storage').$name('param'); + const mutable = asMutable(buffer); + + const resolved = tgpu.resolve({ + template: ` + fn m() { + let y = hello; + }`, + externals: { hello: mutable }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + @group(0) @binding(0) var param: f32; + + fn m() { + let y = param; + }`), + ); + }); + + it('resolves to buffer binding in tgsl functions', ({ root }) => { + const buffer = root.createBuffer(d.f32).$usage('storage').$name('param'); + const mutable = asMutable(buffer); + + const func = tgpu['~unstable'].fn([]).does(() => { + const x = mutable.value; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + @group(0) @binding(0) var param: f32; + + fn func() { + var x = param; + }`), + ); + }); + + it('allows accessing fields in a struct stored in its buffer', ({ root }) => { + const Boid = d + .struct({ + pos: d.vec3f, + vel: d.vec3u, + }) + .$name('Boid'); + + const buffer = root.createBuffer(Boid).$usage('storage').$name('boid'); + const mutable = asMutable(buffer); + + const func = tgpu['~unstable'].fn([]).does(() => { + const pos = mutable.value.pos; + const velX = mutable.value.vel.x; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + struct Boid { + pos: vec3f, + vel: vec3u, + } + + @group(0) @binding(0) var boid: Boid; + + fn func() { + var pos = boid.pos; + var velX = boid.vel.x; + }`), + ); + }); +}); + +describe('TgpuBufferReadonly', () => { + it('represents a `number` value', ({ root }) => { + const buffer = root.createBuffer(d.f32).$usage('storage'); + const readonly = asReadonly(buffer); + + expectTypeOf>().toEqualTypeOf(); + }); + + it('resolves to buffer binding in code', ({ root }) => { + const buffer = root.createBuffer(d.f32).$usage('storage').$name('param'); + const readonly = asReadonly(buffer); + + const resolved = tgpu.resolve({ + template: ` + fn m() { + let y = hello; + }`, + externals: { hello: readonly }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + @group(0) @binding(0) var param: f32; + + fn m() { + let y = param; + }`), + ); + }); + + it('resolves to buffer binding in tgsl functions', ({ root }) => { + const buffer = root.createBuffer(d.f32).$usage('storage').$name('param'); + const readonly = asReadonly(buffer); + + const func = tgpu['~unstable'].fn([]).does(() => { + const x = readonly.value; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + @group(0) @binding(0) var param: f32; + + fn func() { + var x = param; + }`), + ); + }); + + it('allows accessing fields in a struct stored in its buffer', ({ root }) => { + const Boid = d + .struct({ + pos: d.vec3f, + vel: d.vec3u, + }) + .$name('Boid'); + + const buffer = root.createBuffer(Boid).$usage('storage').$name('boid'); + const readonly = asReadonly(buffer); + + const func = tgpu['~unstable'].fn([]).does(() => { + const pos = readonly.value.pos; + const velX = readonly.value.vel.x; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + struct Boid { + pos: vec3f, + vel: vec3u, + } + + @group(0) @binding(0) var boid: Boid; + + fn func() { + var pos = boid.pos; + var velX = boid.vel.x; + }`), + ); + }); +}); From cf5551d2baf5b6475279d2e7377540e09244322e Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 20 Jan 2025 15:17:25 +0100 Subject: [PATCH 06/13] More tests + fix .value.value for slots --- packages/typegpu/src/core/slot/derived.ts | 3 ++ packages/typegpu/src/core/slot/slot.ts | 1 + packages/typegpu/src/core/slot/slotTypes.ts | 2 + packages/typegpu/tests/constant.test.ts | 43 +++++++++++++++++++++ packages/typegpu/tests/derived.test.ts | 24 ++++++------ packages/typegpu/tests/slot.test.ts | 7 ++++ packages/typegpu/tests/variable.test.ts | 2 +- 7 files changed, 69 insertions(+), 13 deletions(-) diff --git a/packages/typegpu/src/core/slot/derived.ts b/packages/typegpu/src/core/slot/derived.ts index 149961c1..bf318897 100644 --- a/packages/typegpu/src/core/slot/derived.ts +++ b/packages/typegpu/src/core/slot/derived.ts @@ -23,6 +23,7 @@ function createDerived(compute: () => T): TgpuDerived { const result = { resourceType: 'derived' as const, '~compute': compute, + '~repr': undefined as Infer, get value(): Infer { const ctx = getResolutionCtx(); @@ -60,6 +61,8 @@ function createBoundDerived( ): TgpuDerived { const result = { resourceType: 'derived' as const, + '~repr': undefined as Infer, + '~compute'() { const ctx = getResolutionCtx(); if (!ctx) { diff --git a/packages/typegpu/src/core/slot/slot.ts b/packages/typegpu/src/core/slot/slot.ts index 8f334231..c46fc4ee 100644 --- a/packages/typegpu/src/core/slot/slot.ts +++ b/packages/typegpu/src/core/slot/slot.ts @@ -17,6 +17,7 @@ export function slot(defaultValue?: T): TgpuSlot { class TgpuSlotImpl implements TgpuSlot { readonly resourceType = 'slot'; public label?: string | undefined; + '~repr' = undefined as Infer; constructor(public defaultValue: T | undefined = undefined) {} diff --git a/packages/typegpu/src/core/slot/slotTypes.ts b/packages/typegpu/src/core/slot/slotTypes.ts index 345cb31e..686303ec 100644 --- a/packages/typegpu/src/core/slot/slotTypes.ts +++ b/packages/typegpu/src/core/slot/slotTypes.ts @@ -7,6 +7,7 @@ import type { TgpuBufferUsage } from './../buffer/bufferUsage'; export interface TgpuSlot extends TgpuNamable, Labelled { readonly resourceType: 'slot'; + '~repr': Infer; readonly defaultValue: T | undefined; @@ -22,6 +23,7 @@ export interface TgpuSlot extends TgpuNamable, Labelled { export interface TgpuDerived { readonly resourceType: 'derived'; readonly value: Infer; + '~repr': Infer; with(slot: TgpuSlot, value: Eventual): TgpuDerived; diff --git a/packages/typegpu/tests/constant.test.ts b/packages/typegpu/tests/constant.test.ts index ce255804..0107e4cb 100644 --- a/packages/typegpu/tests/constant.test.ts +++ b/packages/typegpu/tests/constant.test.ts @@ -24,4 +24,47 @@ describe('tgpu.const', () => { `), ); }); + + it('allows accessing constants in tgsl through .value', () => { + const Boid = d + .struct({ + pos: d.vec3f, + vel: d.vec3u, + }) + .$name('Boid'); + + const boidConst = tgpu['~unstable'] + .const(Boid, { + pos: d.vec3f(1, 2, 3), + vel: d.vec3u(4, 5, 6), + }) + .$name('boid'); + + const func = tgpu['~unstable'].fn([]).does(() => { + const pos = boidConst.value; + const vel = boidConst.value.vel; + const velX = boidConst.value.vel.x; + }); + + const resolved = tgpu.resolve({ + externals: { func }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(` + struct Boid { + pos: vec3f, + vel: vec3u, + } + + const boid = Boid(vec3f(1, 2, 3), vec3u(4, 5, 6)); + + fn func() { + var pos = boid; + var vel = boid.vel; + var velX = boid.vel.x; + }`), + ); + }); }); diff --git a/packages/typegpu/tests/derived.test.ts b/packages/typegpu/tests/derived.test.ts index bde3bff8..48c4f5fc 100644 --- a/packages/typegpu/tests/derived.test.ts +++ b/packages/typegpu/tests/derived.test.ts @@ -140,23 +140,20 @@ describe('TgpuDerived', () => { const buffer = root.createBuffer(Boid).$usage('uniform').$name('boid'); const uniform = unstable_asUniform(buffer); - const uniformSlot = tgpu['~unstable'].slot(uniform); - const derivedUniformSlot = tgpu['~unstable'].derived(() => { - const uniform = uniformSlot.value; - return uniform; - }); - - const derivedDerivedUniformSlot = tgpu['~unstable'].derived(() => { - const uniform = derivedUniformSlot.value; - return uniform; - }); + const derivedUniformSlot = tgpu['~unstable'].derived(() => uniform); + const derivedDerivedUniformSlot = tgpu['~unstable'].derived( + () => derivedUniformSlot, + ); const func = tgpu['~unstable'].fn([]).does(() => { const pos = doubledVectorSlot.value; const posX = doubledVectorSlot.value.x; - const vel = derivedDerivedUniformSlot.value.vel; - const velX = derivedDerivedUniformSlot.value.vel.x; + const vel = derivedUniformSlot.value.vel; + const velX = derivedUniformSlot.value.vel.x; + + const vel_ = derivedDerivedUniformSlot.value.vel; + const velX_ = derivedDerivedUniformSlot.value.vel.x; }); const resolved = tgpu.resolve({ @@ -178,6 +175,9 @@ describe('TgpuDerived', () => { var posX = (2 * vec3f(1, 2, 3)).x; var vel = boid.vel; var velX = boid.vel.x; + + var vel_ = boid.vel; + var velX_ = boid.vel.x; }`), ); }); diff --git a/packages/typegpu/tests/slot.test.ts b/packages/typegpu/tests/slot.test.ts index 27b429d7..09605300 100644 --- a/packages/typegpu/tests/slot.test.ts +++ b/packages/typegpu/tests/slot.test.ts @@ -389,12 +389,16 @@ describe('tgpu.slot', () => { const buffer = root.createBuffer(Boid).$usage('uniform').$name('boid'); const uniform = unstable_asUniform(buffer); const uniformSlot = tgpu['~unstable'].slot(uniform); + const uniformSlotSlot = tgpu['~unstable'].slot(uniformSlot); const func = tgpu['~unstable'].fn([]).does(() => { const pos = vectorSlot.value; const posX = vectorSlot.value.x; const vel = uniformSlot.value.vel; const velX = uniformSlot.value.vel.x; + + const vel_ = uniformSlotSlot.value.vel; + const velX_ = uniformSlotSlot.value.vel.x; }); const resolved = tgpu.resolve({ @@ -416,6 +420,9 @@ describe('tgpu.slot', () => { var posX = 1; var vel = boid.vel; var velX = boid.vel.x; + + var vel_ = boid.vel; + var velX_ = boid.vel.x; }`), ); }); diff --git a/packages/typegpu/tests/variable.test.ts b/packages/typegpu/tests/variable.test.ts index 8c77ee99..a2492fe9 100644 --- a/packages/typegpu/tests/variable.test.ts +++ b/packages/typegpu/tests/variable.test.ts @@ -133,7 +133,7 @@ describe('var', () => { vel: vec3u, } - var boid: Boid = Boid(vec3f(1, 2, 3),vec3u(4, 5, 6)); + var boid: Boid = Boid(vec3f(1, 2, 3), vec3u(4, 5, 6)); fn func() { var pos = boid; From 391bddfcd8481f3742716475440d00fe5938c90f Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 20 Jan 2025 16:04:27 +0100 Subject: [PATCH 07/13] Change how unwrapping works in .value for slots and such --- .../typegpu/src/core/buffer/bufferUsage.ts | 2 +- .../typegpu/src/core/constant/tgpuConstant.ts | 2 +- packages/typegpu/src/core/slot/accessor.ts | 3 +- packages/typegpu/src/core/slot/derived.ts | 11 ++--- packages/typegpu/src/core/slot/slot.ts | 6 +-- packages/typegpu/src/core/slot/slotTypes.ts | 1 + .../src/{shared => core}/valueProxyHandler.ts | 17 +++++++ .../typegpu/src/core/variable/tgpuVariable.ts | 2 +- packages/typegpu/tests/accessor.test.ts | 48 +++++++++++++++++++ packages/typegpu/tests/slot.test.ts | 17 +++++++ 10 files changed, 93 insertions(+), 16 deletions(-) rename packages/typegpu/src/{shared => core}/valueProxyHandler.ts (56%) diff --git a/packages/typegpu/src/core/buffer/bufferUsage.ts b/packages/typegpu/src/core/buffer/bufferUsage.ts index d626b434..5972bcff 100644 --- a/packages/typegpu/src/core/buffer/bufferUsage.ts +++ b/packages/typegpu/src/core/buffer/bufferUsage.ts @@ -2,13 +2,13 @@ import type { AnyWgslData, BaseWgslData } from '../../data/wgslTypes'; import { type Storage, isUsableAsStorage } from '../../extension'; import { inGPUMode } from '../../gpuMode'; import type { Infer } from '../../shared/repr'; -import { valueProxyHandler } from '../../shared/valueProxyHandler'; import type { LayoutMembership } from '../../tgpuBindGroupLayout'; import type { BindableBufferUsage, ResolutionCtx, SelfResolvable, } from '../../types'; +import { valueProxyHandler } from '../valueProxyHandler'; import { type TgpuBuffer, type Uniform, isUsableAsUniform } from './buffer'; // ---------- diff --git a/packages/typegpu/src/core/constant/tgpuConstant.ts b/packages/typegpu/src/core/constant/tgpuConstant.ts index 10e94929..9eef925e 100644 --- a/packages/typegpu/src/core/constant/tgpuConstant.ts +++ b/packages/typegpu/src/core/constant/tgpuConstant.ts @@ -2,8 +2,8 @@ import type { AnyWgslData } from '../../data/wgslTypes'; import { inGPUMode } from '../../gpuMode'; import type { TgpuNamable } from '../../namable'; import type { Infer } from '../../shared/repr'; -import { valueProxyHandler } from '../../shared/valueProxyHandler'; import type { ResolutionCtx, SelfResolvable } from '../../types'; +import { valueProxyHandler } from '../valueProxyHandler'; import type { Exotic } from './../../data/exotic'; // ---------- diff --git a/packages/typegpu/src/core/slot/accessor.ts b/packages/typegpu/src/core/slot/accessor.ts index e31d4194..0b44d207 100644 --- a/packages/typegpu/src/core/slot/accessor.ts +++ b/packages/typegpu/src/core/slot/accessor.ts @@ -2,10 +2,10 @@ import type { AnyWgslData } from '../../data'; import type { Exotic } from '../../data/exotic'; import { getResolutionCtx } from '../../gpuMode'; import type { Infer } from '../../shared/repr'; -import { valueProxyHandler } from '../../shared/valueProxyHandler'; import type { ResolutionCtx, SelfResolvable } from '../../types'; import { type TgpuBufferUsage, isBufferUsage } from '../buffer/bufferUsage'; import { type TgpuFn, isTgpuFn } from '../function/tgpuFn'; +import { valueProxyHandler } from '../valueProxyHandler'; import { slot } from './slot'; import type { TgpuAccessor, TgpuSlot } from './slotTypes'; @@ -31,6 +31,7 @@ export class TgpuAccessorImpl implements TgpuAccessor, SelfResolvable { readonly resourceType = 'accessor'; + '~repr' = undefined as Infer; public label?: string | undefined; public slot: TgpuSlot | TgpuBufferUsage | Infer>; diff --git a/packages/typegpu/src/core/slot/derived.ts b/packages/typegpu/src/core/slot/derived.ts index bf318897..22f5e845 100644 --- a/packages/typegpu/src/core/slot/derived.ts +++ b/packages/typegpu/src/core/slot/derived.ts @@ -1,5 +1,6 @@ import { getResolutionCtx } from '../../gpuMode'; import type { Infer } from '../../shared/repr'; +import { unwrapProxy } from '../valueProxyHandler'; import type { Eventual, SlotValuePair, @@ -33,10 +34,7 @@ function createDerived(compute: () => T): TgpuDerived { ); } - const unwrapped = ctx.unwrap(this); - return (unwrapped && typeof unwrapped === 'object' && 'value' in unwrapped - ? unwrapped.value // TODO: user defined 'value' properties shouldn't be accessed here - : unwrapped) as unknown as Infer; + return unwrapProxy(ctx.unwrap(this)); }, with( @@ -82,10 +80,7 @@ function createBoundDerived( ); } - const unwrapped = ctx.unwrap(this); - return (unwrapped && typeof unwrapped === 'object' && 'value' in unwrapped - ? unwrapped.value // TODO: user defined 'value' properties shouldn't be accessed here - : unwrapped) as unknown as Infer; + return unwrapProxy(ctx.unwrap(this)); }, with( diff --git a/packages/typegpu/src/core/slot/slot.ts b/packages/typegpu/src/core/slot/slot.ts index c46fc4ee..05a2c39a 100644 --- a/packages/typegpu/src/core/slot/slot.ts +++ b/packages/typegpu/src/core/slot/slot.ts @@ -1,5 +1,6 @@ import { getResolutionCtx } from '../../gpuMode'; import type { Infer } from '../../shared/repr'; +import { unwrapProxy } from '../valueProxyHandler'; import type { TgpuSlot } from './slotTypes'; // ---------- @@ -40,9 +41,6 @@ class TgpuSlotImpl implements TgpuSlot { throw new Error(`Cannot access tgpu.slot's value outside of resolution.`); } - const unwrapped = ctx.unwrap(this); - return (unwrapped && typeof unwrapped === 'object' && 'value' in unwrapped - ? unwrapped.value // TODO: user defined 'value' properties shouldn't be accessed here - : unwrapped) as unknown as Infer; + return unwrapProxy(ctx.unwrap(this)); } } diff --git a/packages/typegpu/src/core/slot/slotTypes.ts b/packages/typegpu/src/core/slot/slotTypes.ts index 686303ec..9ef65201 100644 --- a/packages/typegpu/src/core/slot/slotTypes.ts +++ b/packages/typegpu/src/core/slot/slotTypes.ts @@ -37,6 +37,7 @@ export interface TgpuAccessor extends TgpuNamable, Labelled { readonly resourceType: 'accessor'; + '~repr': Infer; readonly schema: T; readonly defaultValue: diff --git a/packages/typegpu/src/shared/valueProxyHandler.ts b/packages/typegpu/src/core/valueProxyHandler.ts similarity index 56% rename from packages/typegpu/src/shared/valueProxyHandler.ts rename to packages/typegpu/src/core/valueProxyHandler.ts index a8d71727..2a9bee8d 100644 --- a/packages/typegpu/src/shared/valueProxyHandler.ts +++ b/packages/typegpu/src/core/valueProxyHandler.ts @@ -1,4 +1,6 @@ import type { Labelled, ResolutionCtx, SelfResolvable } from '../types'; +import { isBufferUsage } from './buffer/bufferUsage'; +import { isAccessor, isDerived, isSlot } from './slot/slotTypes'; export const valueProxyHandler: ProxyHandler = { get(target, prop) { @@ -18,3 +20,18 @@ export const valueProxyHandler: ProxyHandler = { ); }, }; + +export function unwrapProxy(value: unknown): T { + let unwrapped = value; + + while ( + isSlot(unwrapped) || + isDerived(unwrapped) || + isAccessor(unwrapped) || + isBufferUsage(unwrapped) + ) { + unwrapped = unwrapped.value; + } + + return unwrapped as T; +} diff --git a/packages/typegpu/src/core/variable/tgpuVariable.ts b/packages/typegpu/src/core/variable/tgpuVariable.ts index 5e6602cb..05e2d98b 100644 --- a/packages/typegpu/src/core/variable/tgpuVariable.ts +++ b/packages/typegpu/src/core/variable/tgpuVariable.ts @@ -3,8 +3,8 @@ import type { AnyWgslData } from '../../data/wgslTypes'; import { inGPUMode } from '../../gpuMode'; import type { TgpuNamable } from '../../namable'; import type { Infer } from '../../shared/repr'; -import { valueProxyHandler } from '../../shared/valueProxyHandler'; import type { ResolutionCtx, SelfResolvable } from '../../types'; +import { valueProxyHandler } from '../valueProxyHandler'; // ---------- // Public API diff --git a/packages/typegpu/tests/accessor.test.ts b/packages/typegpu/tests/accessor.test.ts index 5192632a..a478da0f 100644 --- a/packages/typegpu/tests/accessor.test.ts +++ b/packages/typegpu/tests/accessor.test.ts @@ -180,4 +180,52 @@ describe('tgpu.accessor', () => { ]), ); }); + + it('resolves in tgsl functions, using .value', ({ root }) => { + const colorAccessorValue = tgpu['~unstable'].accessor(d.vec3f, RED); + const colorAccessorUsage = tgpu['~unstable'].accessor( + d.vec3f, + unstable_asUniform( + root.createBuffer(d.vec3f, RED).$usage('uniform').$name('colorUniform'), + ), + ); + + const colorAccessorFn = tgpu['~unstable'].accessor( + d.vec3f, + tgpu['~unstable'] + .fn([], d.vec3f) + .does(() => RED) + .$name('getColor'), + ); + + const main = tgpu['~unstable'] + .fn([]) + .does(() => { + const color = colorAccessorValue.value; + const color2 = colorAccessorUsage.value; + const color3 = colorAccessorFn.value; + }) + .$name('main'); + + const resolved = tgpu.resolve({ + externals: { main }, + names: 'strict', + }); + + expect(parse(resolved)).toEqual( + parse(/* wgsl */ ` + @group(0) @binding(0) var colorUniform: vec3f; + + fn getColor() -> vec3f { + return vec3f(1, 0, 0); + } + + fn main() { + var color = vec3f(1, 0, 0); + var color2 = colorUniform; + var color3 = getColor(); + } + `), + ); + }); }); diff --git a/packages/typegpu/tests/slot.test.ts b/packages/typegpu/tests/slot.test.ts index 09605300..57a88332 100644 --- a/packages/typegpu/tests/slot.test.ts +++ b/packages/typegpu/tests/slot.test.ts @@ -391,6 +391,15 @@ describe('tgpu.slot', () => { const uniformSlot = tgpu['~unstable'].slot(uniform); const uniformSlotSlot = tgpu['~unstable'].slot(uniformSlot); + const colorAccessorFn = tgpu['~unstable'].accessor( + d.vec3f, + tgpu['~unstable'] + .fn([], d.vec3f) + .does(() => d.vec3f(1, 2, 3)) + .$name('getColor'), + ); + const colorAccessorSlot = tgpu['~unstable'].slot(colorAccessorFn); + const func = tgpu['~unstable'].fn([]).does(() => { const pos = vectorSlot.value; const posX = vectorSlot.value.x; @@ -399,6 +408,8 @@ describe('tgpu.slot', () => { const vel_ = uniformSlotSlot.value.vel; const velX_ = uniformSlotSlot.value.vel.x; + + const color = colorAccessorSlot.value; }); const resolved = tgpu.resolve({ @@ -415,6 +426,10 @@ describe('tgpu.slot', () => { @group(0) @binding(0) var boid: Boid; + fn getColor() -> vec3f { + return vec3f(1, 2, 3); + } + fn func(){ var pos = vec3f(1, 2, 3); var posX = 1; @@ -423,6 +438,8 @@ describe('tgpu.slot', () => { var vel_ = boid.vel; var velX_ = boid.vel.x; + + var color = getColor(); }`), ); }); From 8bcf033fcaabeac4e3530d3b3edd13b3b1164d07 Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 20 Jan 2025 16:06:20 +0100 Subject: [PATCH 08/13] Rename file to valueProxyUtils --- packages/typegpu/src/core/buffer/bufferUsage.ts | 2 +- packages/typegpu/src/core/constant/tgpuConstant.ts | 2 +- packages/typegpu/src/core/slot/accessor.ts | 2 +- packages/typegpu/src/core/slot/derived.ts | 2 +- packages/typegpu/src/core/slot/slot.ts | 2 +- .../src/core/{valueProxyHandler.ts => valueProxyUtils.ts} | 0 packages/typegpu/src/core/variable/tgpuVariable.ts | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename packages/typegpu/src/core/{valueProxyHandler.ts => valueProxyUtils.ts} (100%) diff --git a/packages/typegpu/src/core/buffer/bufferUsage.ts b/packages/typegpu/src/core/buffer/bufferUsage.ts index 5972bcff..d4ed4289 100644 --- a/packages/typegpu/src/core/buffer/bufferUsage.ts +++ b/packages/typegpu/src/core/buffer/bufferUsage.ts @@ -8,7 +8,7 @@ import type { ResolutionCtx, SelfResolvable, } from '../../types'; -import { valueProxyHandler } from '../valueProxyHandler'; +import { valueProxyHandler } from '../valueProxyUtils'; import { type TgpuBuffer, type Uniform, isUsableAsUniform } from './buffer'; // ---------- diff --git a/packages/typegpu/src/core/constant/tgpuConstant.ts b/packages/typegpu/src/core/constant/tgpuConstant.ts index 9eef925e..0f80e2a3 100644 --- a/packages/typegpu/src/core/constant/tgpuConstant.ts +++ b/packages/typegpu/src/core/constant/tgpuConstant.ts @@ -3,7 +3,7 @@ import { inGPUMode } from '../../gpuMode'; import type { TgpuNamable } from '../../namable'; import type { Infer } from '../../shared/repr'; import type { ResolutionCtx, SelfResolvable } from '../../types'; -import { valueProxyHandler } from '../valueProxyHandler'; +import { valueProxyHandler } from '../valueProxyUtils'; import type { Exotic } from './../../data/exotic'; // ---------- diff --git a/packages/typegpu/src/core/slot/accessor.ts b/packages/typegpu/src/core/slot/accessor.ts index 0b44d207..9bf9e073 100644 --- a/packages/typegpu/src/core/slot/accessor.ts +++ b/packages/typegpu/src/core/slot/accessor.ts @@ -5,7 +5,7 @@ import type { Infer } from '../../shared/repr'; import type { ResolutionCtx, SelfResolvable } from '../../types'; import { type TgpuBufferUsage, isBufferUsage } from '../buffer/bufferUsage'; import { type TgpuFn, isTgpuFn } from '../function/tgpuFn'; -import { valueProxyHandler } from '../valueProxyHandler'; +import { valueProxyHandler } from '../valueProxyUtils'; import { slot } from './slot'; import type { TgpuAccessor, TgpuSlot } from './slotTypes'; diff --git a/packages/typegpu/src/core/slot/derived.ts b/packages/typegpu/src/core/slot/derived.ts index 22f5e845..672f35e4 100644 --- a/packages/typegpu/src/core/slot/derived.ts +++ b/packages/typegpu/src/core/slot/derived.ts @@ -1,6 +1,6 @@ import { getResolutionCtx } from '../../gpuMode'; import type { Infer } from '../../shared/repr'; -import { unwrapProxy } from '../valueProxyHandler'; +import { unwrapProxy } from '../valueProxyUtils'; import type { Eventual, SlotValuePair, diff --git a/packages/typegpu/src/core/slot/slot.ts b/packages/typegpu/src/core/slot/slot.ts index 05a2c39a..6353e251 100644 --- a/packages/typegpu/src/core/slot/slot.ts +++ b/packages/typegpu/src/core/slot/slot.ts @@ -1,6 +1,6 @@ import { getResolutionCtx } from '../../gpuMode'; import type { Infer } from '../../shared/repr'; -import { unwrapProxy } from '../valueProxyHandler'; +import { unwrapProxy } from '../valueProxyUtils'; import type { TgpuSlot } from './slotTypes'; // ---------- diff --git a/packages/typegpu/src/core/valueProxyHandler.ts b/packages/typegpu/src/core/valueProxyUtils.ts similarity index 100% rename from packages/typegpu/src/core/valueProxyHandler.ts rename to packages/typegpu/src/core/valueProxyUtils.ts diff --git a/packages/typegpu/src/core/variable/tgpuVariable.ts b/packages/typegpu/src/core/variable/tgpuVariable.ts index 05e2d98b..b44df296 100644 --- a/packages/typegpu/src/core/variable/tgpuVariable.ts +++ b/packages/typegpu/src/core/variable/tgpuVariable.ts @@ -4,7 +4,7 @@ import { inGPUMode } from '../../gpuMode'; import type { TgpuNamable } from '../../namable'; import type { Infer } from '../../shared/repr'; import type { ResolutionCtx, SelfResolvable } from '../../types'; -import { valueProxyHandler } from '../valueProxyHandler'; +import { valueProxyHandler } from '../valueProxyUtils'; // ---------- // Public API From 19fba5b7d6e9403a4d9c77d7be2bc5a4b381d796 Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 20 Jan 2025 16:10:14 +0100 Subject: [PATCH 09/13] Fix imports ? --- packages/typegpu/src/core/buffer/bufferUsage.ts | 9 --------- packages/typegpu/src/core/slot/accessor.ts | 8 ++++++-- packages/typegpu/src/core/valueProxyUtils.ts | 10 +++++++--- packages/typegpu/src/types.ts | 16 +++++++++++++++- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/typegpu/src/core/buffer/bufferUsage.ts b/packages/typegpu/src/core/buffer/bufferUsage.ts index d4ed4289..eb7338b8 100644 --- a/packages/typegpu/src/core/buffer/bufferUsage.ts +++ b/packages/typegpu/src/core/buffer/bufferUsage.ts @@ -38,15 +38,6 @@ export interface TgpuBufferReadonly export interface TgpuBufferMutable extends TgpuBufferUsage {} -export function isBufferUsage< - T extends - | TgpuBufferUniform - | TgpuBufferReadonly - | TgpuBufferMutable, ->(value: T | unknown): value is T { - return (value as T)?.resourceType === 'buffer-usage'; -} - // -------------- // Implementation // -------------- diff --git a/packages/typegpu/src/core/slot/accessor.ts b/packages/typegpu/src/core/slot/accessor.ts index 9bf9e073..4e659248 100644 --- a/packages/typegpu/src/core/slot/accessor.ts +++ b/packages/typegpu/src/core/slot/accessor.ts @@ -2,8 +2,12 @@ import type { AnyWgslData } from '../../data'; import type { Exotic } from '../../data/exotic'; import { getResolutionCtx } from '../../gpuMode'; import type { Infer } from '../../shared/repr'; -import type { ResolutionCtx, SelfResolvable } from '../../types'; -import { type TgpuBufferUsage, isBufferUsage } from '../buffer/bufferUsage'; +import { + type ResolutionCtx, + type SelfResolvable, + isBufferUsage, +} from '../../types'; +import type { TgpuBufferUsage } from '../buffer/bufferUsage'; import { type TgpuFn, isTgpuFn } from '../function/tgpuFn'; import { valueProxyHandler } from '../valueProxyUtils'; import { slot } from './slot'; diff --git a/packages/typegpu/src/core/valueProxyUtils.ts b/packages/typegpu/src/core/valueProxyUtils.ts index 2a9bee8d..5994214a 100644 --- a/packages/typegpu/src/core/valueProxyUtils.ts +++ b/packages/typegpu/src/core/valueProxyUtils.ts @@ -1,5 +1,9 @@ -import type { Labelled, ResolutionCtx, SelfResolvable } from '../types'; -import { isBufferUsage } from './buffer/bufferUsage'; +import { + type Labelled, + type ResolutionCtx, + type SelfResolvable, + isBufferUsage, +} from '../types'; import { isAccessor, isDerived, isSlot } from './slot/slotTypes'; export const valueProxyHandler: ProxyHandler = { @@ -23,7 +27,7 @@ export const valueProxyHandler: ProxyHandler = { export function unwrapProxy(value: unknown): T { let unwrapped = value; - + while ( isSlot(unwrapped) || isDerived(unwrapped) || diff --git a/packages/typegpu/src/types.ts b/packages/typegpu/src/types.ts index eb28e503..880b077d 100644 --- a/packages/typegpu/src/types.ts +++ b/packages/typegpu/src/types.ts @@ -1,5 +1,10 @@ import type { Block } from 'tinyest'; -import type { TgpuBufferUsage } from './core/buffer/bufferUsage'; +import type { + TgpuBufferMutable, + TgpuBufferReadonly, + TgpuBufferUniform, + TgpuBufferUsage, +} from './core/buffer/bufferUsage'; import type { TgpuConst } from './core/constant/tgpuConstant'; import type { TgpuDeclare } from './core/declare/tgpuDeclare'; import type { TgpuComputeFn } from './core/function/tgpuComputeFn'; @@ -166,3 +171,12 @@ export function isGPUBuffer(value: unknown): value is GPUBuffer { 'mapAsync' in value ); } + +export function isBufferUsage< + T extends + | TgpuBufferUniform + | TgpuBufferReadonly + | TgpuBufferMutable, +>(value: T | unknown): value is T { + return (value as T)?.resourceType === 'buffer-usage'; +} From 5d79063b52b3f097eece336c14fbd72bbb21ca4d Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 27 Jan 2025 08:12:33 +0100 Subject: [PATCH 10/13] Review fixes --- packages/typegpu/src/core/buffer/bufferUsage.ts | 16 ++++++++++------ .../typegpu/src/core/constant/tgpuConstant.ts | 4 ++-- packages/typegpu/src/core/slot/accessor.ts | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/typegpu/src/core/buffer/bufferUsage.ts b/packages/typegpu/src/core/buffer/bufferUsage.ts index eb7338b8..06e2d9ac 100644 --- a/packages/typegpu/src/core/buffer/bufferUsage.ts +++ b/packages/typegpu/src/core/buffer/bufferUsage.ts @@ -81,7 +81,9 @@ class TgpuFixedBufferImpl< const usage = usageToVarTemplateMap[this.usage]; ctx.addDeclaration( - `@group(${group}) @binding(${binding}) var<${usage}> ${id}: ${ctx.resolve(this.buffer.dataType)};`, + `@group(${group}) @binding(${binding}) var<${usage}> ${id}: ${ctx.resolve( + this.buffer.dataType, + )};`, ); return id; @@ -98,8 +100,8 @@ class TgpuFixedBufferImpl< return new Proxy( { - '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), - toString: (() => `.value:${this.label ?? ''}`).bind(this), + '~resolve': (ctx: ResolutionCtx) => ctx.resolve(this), + toString: () => `.value:${this.label ?? ''}`, }, valueProxyHandler, ) as Infer; @@ -130,7 +132,9 @@ export class TgpuLaidOutBufferImpl< const usage = usageToVarTemplateMap[this.usage]; ctx.addDeclaration( - `@group(${group}) @binding(${this._membership.idx}) var<${usage}> ${id}: ${ctx.resolve(this.dataType as AnyWgslData)};`, + `@group(${group}) @binding(${ + this._membership.idx + }) var<${usage}> ${id}: ${ctx.resolve(this.dataType as AnyWgslData)};`, ); return id; @@ -147,8 +151,8 @@ export class TgpuLaidOutBufferImpl< return new Proxy( { - '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), - toString: (() => `.value:${this.label ?? ''}`).bind(this), + '~resolve': (ctx: ResolutionCtx) => ctx.resolve(this), + toString: () => `.value:${this.label ?? ''}`, }, valueProxyHandler, ) as Infer; diff --git a/packages/typegpu/src/core/constant/tgpuConstant.ts b/packages/typegpu/src/core/constant/tgpuConstant.ts index 0f80e2a3..e4797728 100644 --- a/packages/typegpu/src/core/constant/tgpuConstant.ts +++ b/packages/typegpu/src/core/constant/tgpuConstant.ts @@ -69,8 +69,8 @@ class TgpuConstImpl return new Proxy( { - '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), - toString: (() => `.value:${this.label ?? ''}`).bind(this), + '~resolve': (ctx: ResolutionCtx) => ctx.resolve(this), + toString: () => `.value:${this.label ?? ''}`, }, valueProxyHandler, ) as Infer; diff --git a/packages/typegpu/src/core/slot/accessor.ts b/packages/typegpu/src/core/slot/accessor.ts index 4e659248..fce69ac7 100644 --- a/packages/typegpu/src/core/slot/accessor.ts +++ b/packages/typegpu/src/core/slot/accessor.ts @@ -70,8 +70,8 @@ export class TgpuAccessorImpl return new Proxy( { - '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), - toString: (() => `.value:${this.label ?? ''}`).bind(this), + '~resolve': (ctx: ResolutionCtx) => ctx.resolve(this), + toString: () => `.value:${this.label ?? ''}`, }, valueProxyHandler, ) as Infer; From 88c2cf3047bfeb4e8950851e9b012fd43c5eddf2 Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Mon, 27 Jan 2025 10:21:37 +0100 Subject: [PATCH 11/13] Try to fix tests failing after merge part 1 --- packages/typegpu/src/core/valueProxyUtils.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/typegpu/src/core/valueProxyUtils.ts b/packages/typegpu/src/core/valueProxyUtils.ts index 5994214a..a5b5ff00 100644 --- a/packages/typegpu/src/core/valueProxyUtils.ts +++ b/packages/typegpu/src/core/valueProxyUtils.ts @@ -12,6 +12,10 @@ export const valueProxyHandler: ProxyHandler = { return Reflect.get(target, prop); } + if (prop === '~providing') { + return undefined; + } + return new Proxy( { '~resolve': (ctx: ResolutionCtx) => From db8bc445747c1d5402f07bc2e1567e85a4204f28 Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Tue, 28 Jan 2025 12:14:41 +0100 Subject: [PATCH 12/13] Fix failing test, expand accessor test + remove unnecessary bind --- packages/typegpu/src/core/variable/tgpuVariable.ts | 4 ++-- packages/typegpu/tests/accessor.test.ts | 8 ++++++++ packages/typegpu/tests/derived.test.ts | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/typegpu/src/core/variable/tgpuVariable.ts b/packages/typegpu/src/core/variable/tgpuVariable.ts index b44df296..d2e744fb 100644 --- a/packages/typegpu/src/core/variable/tgpuVariable.ts +++ b/packages/typegpu/src/core/variable/tgpuVariable.ts @@ -96,8 +96,8 @@ class TgpuVarImpl return new Proxy( { - '~resolve': ((ctx: ResolutionCtx) => ctx.resolve(this)).bind(this), - toString: (() => `.value:${this.label ?? ''}`).bind(this), + '~resolve': (ctx: ResolutionCtx) => ctx.resolve(this), + toString: () => `.value:${this.label ?? ''}`, }, valueProxyHandler, ) as Infer; diff --git a/packages/typegpu/tests/accessor.test.ts b/packages/typegpu/tests/accessor.test.ts index a478da0f..cb57f3c5 100644 --- a/packages/typegpu/tests/accessor.test.ts +++ b/packages/typegpu/tests/accessor.test.ts @@ -204,6 +204,10 @@ describe('tgpu.accessor', () => { const color = colorAccessorValue.value; const color2 = colorAccessorUsage.value; const color3 = colorAccessorFn.value; + + const colorX = colorAccessorValue.value.x; + const color2X = colorAccessorUsage.value.x; + const color3X = colorAccessorFn.value.x; }) .$name('main'); @@ -224,6 +228,10 @@ describe('tgpu.accessor', () => { var color = vec3f(1, 0, 0); var color2 = colorUniform; var color3 = getColor(); + + var colorX = vec3f(1, 0, 0).x; + var color2X = colorUniform.x; + var color3X = getColor().x; } `), ); diff --git a/packages/typegpu/tests/derived.test.ts b/packages/typegpu/tests/derived.test.ts index a1793b5b..b133382d 100644 --- a/packages/typegpu/tests/derived.test.ts +++ b/packages/typegpu/tests/derived.test.ts @@ -190,6 +190,7 @@ describe('TgpuDerived', () => { return utgpu .fn([], d.f32) .does(() => valueSlot.value) + .with(valueSlot, valueSlot.value) .$name('innerFn'); }); From 8ee4ae02e336ebd00f089b55d42f28fdc827a1b3 Mon Sep 17 00:00:00 2001 From: mhawryluk Date: Tue, 28 Jan 2025 13:42:05 +0100 Subject: [PATCH 13/13] Add comments to the cursed test --- packages/typegpu/tests/derived.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/typegpu/tests/derived.test.ts b/packages/typegpu/tests/derived.test.ts index b133382d..0a27bcd7 100644 --- a/packages/typegpu/tests/derived.test.ts +++ b/packages/typegpu/tests/derived.test.ts @@ -182,6 +182,9 @@ describe('TgpuDerived', () => { ); }); + // TODO: rethink this behavior of derived returning a function, + // in context of whether the function should automatically have + // slot values set on derived and how to achieve that it('allows slot bindings to pass downstream from derived (#697)', () => { const utgpu = tgpu['~unstable']; const valueSlot = utgpu.slot(1).$name('valueSlot'); @@ -190,7 +193,7 @@ describe('TgpuDerived', () => { return utgpu .fn([], d.f32) .does(() => valueSlot.value) - .with(valueSlot, valueSlot.value) + .with(valueSlot, valueSlot.value) // currently necessary to work :/ .$name('innerFn'); });