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
4 changes: 2 additions & 2 deletions packages/typegpu/src/core/function/dualImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { AnyData } from '../../data/dataTypes.ts';
type MapValueToDataType<T> = { [K in keyof T]: AnyData };

interface DualImplOptions<T extends (...args: never[]) => unknown> {
readonly name: string;
readonly name: string | undefined;
readonly normalImpl: T | string;
readonly codegenImpl: (...args: MapValueToSnippet<Parameters<T>>) => string;
readonly signature:
Expand Down Expand Up @@ -105,7 +105,7 @@ export function dualImpl<T extends (...args: never[]) => unknown>(
}) as T;

setName(impl, options.name);
impl.toString = () => options.name;
impl.toString = () => options.name ?? '<unknown>';
Object.defineProperty(impl, $internal, {
value: {
jsImpl: options.normalImpl,
Expand Down
16 changes: 9 additions & 7 deletions packages/typegpu/src/core/function/tgpuFn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ function createFn<ImplSchema extends AnyFn>(
} as This;

const call = dualImpl<InferImplSchema<ImplSchema>>({
name: 'tgpuFnCall',
name: undefined, // the name is forwarded to the core anyway
noComptime: true,
signature: { argTypes: shell.argTypes, returnType: shell.returnType },
normalImpl: (...args) =>
Expand Down Expand Up @@ -271,9 +271,7 @@ function createBoundFunction<ImplSchema extends AnyFn>(
innerFn: TgpuFn<ImplSchema>,
pairs: SlotValuePair[],
): TgpuFn<ImplSchema> {
type This = TgpuFnBase<ImplSchema> & {
[$getNameForward]: TgpuFn<ImplSchema>;
};
type This = TgpuFnBase<ImplSchema>;

const fnBase: This = {
resourceType: 'function',
Expand All @@ -288,7 +286,6 @@ function createBoundFunction<ImplSchema extends AnyFn>(
return this;
},

[$getNameForward]: innerFn,
$name(label: string): This {
setName(this, label);
return this;
Expand All @@ -306,7 +303,7 @@ function createBoundFunction<ImplSchema extends AnyFn>(
};

const call = dualImpl<InferImplSchema<ImplSchema>>({
name: 'tgpuFnCall',
name: undefined, // setting name here would override autonaming
noComptime: true,
signature: {
argTypes: innerFn.shell.argTypes,
Expand All @@ -327,11 +324,16 @@ function createBoundFunction<ImplSchema extends AnyFn>(

Object.defineProperty(fn, 'toString', {
value() {
const fnLabel = getName(innerFn) ?? '<unnamed>';
const fnLabel = getName(this) ?? '<unnamed>';

return `fn:${fnLabel}[${pairs.map(stringifyPair).join(', ')}]`;
},
});

const innerName = getName(innerFn);
if (innerName) {
setName(fn, innerName);
}

return fn;
}
2 changes: 1 addition & 1 deletion packages/typegpu/src/core/slot/slotTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export type Eventual<T> = T | TgpuSlot<T> | TgpuDerived<T>;
export type SlotValuePair<T = unknown> = [TgpuSlot<T>, T];

export type Providing = {
inner: unknown;
inner: object;
pairs: SlotValuePair[];
};

Expand Down
24 changes: 20 additions & 4 deletions packages/typegpu/src/resolutionCtx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ import type {
} from './types.ts';
import { CodegenState, isSelfResolvable, NormalState } from './types.ts';
import type { WgslExtension } from './wgslExtensions.ts';
import { hasTinyestMetadata } from './shared/meta.ts';
import { getName, hasTinyestMetadata, setName } from './shared/meta.ts';
import { FuncParameterType } from 'tinyest';

/**
Expand Down Expand Up @@ -602,6 +602,17 @@ export class ResolutionCtxImpl implements ResolutionCtx {
}
}

withRenamed<T>(item: object, name: string | undefined, callback: () => T): T {
if (!name) {
return callback();
}
const oldName = getName(item);
setName(item, name);
const result = callback();
setName(item, oldName);
return result;
}

unwrap<T>(eventual: Eventual<T>): T {
if (isProviding(eventual)) {
return this.withSlots(
Expand Down Expand Up @@ -766,9 +777,14 @@ export class ResolutionCtxImpl implements ResolutionCtx {
}

if (isProviding(item)) {
return this.withSlots(
item[$providing].pairs,
() => this.resolve(item[$providing].inner, schema),
return this.withRenamed(
item[$providing].inner,
getName(item),
() =>
this.withSlots(
item[$providing].pairs,
() => this.resolve(item[$providing].inner, schema),
),
);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/typegpu/src/shared/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export function getName(definition: unknown): string | undefined {
return getMetaData(definition)?.name;
}

export function setName(definition: object, name: string): void {
export function setName(definition: object, name: string | undefined): void {
if (isForwarded(definition)) {
setName(definition[$getNameForward] as object, name);
}
Expand Down
10 changes: 10 additions & 0 deletions packages/typegpu/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,16 @@ export interface ResolutionCtx {

get varyingLocations(): Record<string, number> | undefined;

/**
* Temporarily renames the item.
* Useful for resolutions with slots,
* since functions with different slots should have different names,
* and all hold the same inner function that is being resolved multiple times.
* @param item the item to rename
* @param name the temporary name to assign to the item (if missing, just returns `callback()`)
*/
withRenamed<T>(item: object, name: string | undefined, callback: () => T): T;

getUniqueName(resource: object): string;
makeNameValid(name: string): string;
}
Expand Down
48 changes: 48 additions & 0 deletions packages/typegpu/tests/slot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as d from '../src/data/index.ts';
import tgpu from '../src/index.ts';
import * as std from '../src/std/index.ts';
import { it } from './utils/extendedIt.ts';
import { getName } from '../src/shared/meta.ts';

const RED = 'vec3f(1., 0., 0.)';
const GREEN = 'vec3f(0., 1., 0.)';
Expand Down Expand Up @@ -391,4 +392,51 @@ describe('tgpu.slot', () => {
`"fn:getSize[slot=vec4f(1, 2, 3, 4)]"`,
);
});

it('sets names only for bound functions', () => {
const colorSlot = tgpu.slot<d.v3f>();

const getColor = tgpu.fn([], d.vec3f)(() => colorSlot.$).$name('colorFn');
const getRed = getColor.with(colorSlot, d.vec3f(1, 0, 0)).$name('redFn');
const getBlue = getColor.with(colorSlot, d.vec3f(0, 0, 1)).$name('blueFn');

expect(getName(getColor)).toBe('colorFn');
expect(getName(getRed)).toBe('redFn');
expect(getName(getBlue)).toBe('blueFn');
});

it('uses bound name for code generation', () => {
const colorSlot = tgpu.slot<d.v3f>(d.vec3f(0, 0, 0));

const getColor = tgpu.fn([], d.vec3f)(() => colorSlot.$).$name('colorFn');
const getRed = getColor.with(colorSlot, d.vec3f(1, 0, 0)).$name('redFn');
const getBlue = getColor.with(colorSlot, d.vec3f(0, 0, 1)).$name('blueFn');

const main = () => {
'use gpu';
getColor();
getRed();
getBlue();
};

expect(tgpu.resolve([main])).toMatchInlineSnapshot(`
"fn colorFn() -> vec3f {
return vec3f();
}

fn redFn() -> vec3f {
return vec3f(1, 0, 0);
}

fn blueFn() -> vec3f {
return vec3f(0, 0, 1);
}

fn main() {
colorFn();
redFn();
blueFn();
}"
`);
});
});