From 7604113ba5109e032e72706933e1cb39703d3e4a Mon Sep 17 00:00:00 2001 From: Iwo Plaza Date: Mon, 20 Apr 2026 13:21:48 +0200 Subject: [PATCH] feat: makeDereferencable --- .../typegpu/src/tgsl/makeDereferencable.ts | 49 +++++++++++++++++ packages/typegpu/src/tgsl/makeResolvable.ts | 52 +++++++++++++++++++ .../src/tgsl/shaderGenerator_members.ts | 2 + 3 files changed, 103 insertions(+) create mode 100644 packages/typegpu/src/tgsl/makeDereferencable.ts create mode 100644 packages/typegpu/src/tgsl/makeResolvable.ts diff --git a/packages/typegpu/src/tgsl/makeDereferencable.ts b/packages/typegpu/src/tgsl/makeDereferencable.ts new file mode 100644 index 0000000000..4c2e56b325 --- /dev/null +++ b/packages/typegpu/src/tgsl/makeDereferencable.ts @@ -0,0 +1,49 @@ +import { snip, type Origin } from '../data/snippet.ts'; +import type { BaseData } from '../data/wgslTypes.ts'; +import { $gpuValueOf, $internal, $ownSnippet, $resolve } from '../shared/symbols.ts'; +import { valueProxyHandler } from '../core/valueProxyUtils.ts'; +import type { SelfResolvable } from '../types.ts'; +import { inCodegenMode } from '../execMode.ts'; + +export function makeDereferencable( + value: T, + options: makeDereferencable.Options, +): T { + Object.defineProperty(value, $gpuValueOf, { + get() { + const [dataType, origin] = options.getDataTypeAndOrigin.apply(this); + + return new Proxy( + { + [$internal]: true, + get [$ownSnippet]() { + return snip(this, dataType, origin); + }, + [$resolve]: (ctx) => ctx.resolve(this), + toString: () => `${this.toString()}.$`, + }, + valueProxyHandler, + ); + }, + }); + + Object.defineProperty(value, '$', { + get() { + if (inCodegenMode()) { + return this[$gpuValueOf]; + } + // TODO: Add proper error message + throw new Error( + 'Cannot read WebGL uniform outside of shader code. Use `.write()` to update it.', + ); + }, + }); + + return value; +} + +export namespace makeDereferencable { + export interface Options { + getDataTypeAndOrigin(this: T): [dataType: BaseData, origin: Origin]; + } +} diff --git a/packages/typegpu/src/tgsl/makeResolvable.ts b/packages/typegpu/src/tgsl/makeResolvable.ts new file mode 100644 index 0000000000..5fcacc8c46 --- /dev/null +++ b/packages/typegpu/src/tgsl/makeResolvable.ts @@ -0,0 +1,52 @@ +import { snip, type Origin, type ResolvedSnippet } from '../data/snippet.ts'; +import { type BaseData } from '../data/wgslTypes.ts'; +import { $internal, $resolve, isMarkedInternal } from '../shared/symbols.ts'; +import type { ResolutionCtx, SelfResolvable } from '../types.ts'; + +/** + * + * @param value + * @param options + * @returns + */ +export function makeResolvable( + value: T, + options: makeResolvable.Options, +): T & SelfResolvable { + if (!isMarkedInternal(value)) { + Object.defineProperty(value, $internal, { + value: true, + }); + } + + Object.defineProperty(value, 'toString', { + value() { + return options.asString.apply(this); + }, + }); + + Object.defineProperty(value, $resolve, { + value(ctx: ResolutionCtx): ResolvedSnippet { + const protoSnippet = options.resolve.apply(this, [ctx]); + + return snip(protoSnippet.value, protoSnippet.dataType, protoSnippet.origin); + }, + }); + + return value as T & SelfResolvable; +} + +interface ProtoSnippet { + value: string; + dataType: BaseData; + origin: Origin; +} + +export namespace makeResolvable { + export interface Options { + resolve(this: T, ctx: ResolutionCtx): ProtoSnippet; + asString(this: T): string; + } + + export type Resolvable = SelfResolvable; +} diff --git a/packages/typegpu/src/tgsl/shaderGenerator_members.ts b/packages/typegpu/src/tgsl/shaderGenerator_members.ts index 0dddb1a85f..90e02a8dbb 100644 --- a/packages/typegpu/src/tgsl/shaderGenerator_members.ts +++ b/packages/typegpu/src/tgsl/shaderGenerator_members.ts @@ -6,6 +6,8 @@ import type { Snippet } from '../data/snippet.ts'; export { UnknownData } from '../data/dataTypes.ts'; export { getName } from '../shared/meta.ts'; +export { makeDereferencable } from './makeDereferencable.ts'; +export { makeResolvable } from './makeResolvable.ts'; // types export type { ResolutionCtx, FunctionArgument, TgpuShaderStage } from '../types.ts';