From f287578744a4ff5126dd29580e3f2771d4fd186b Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:35:50 +0200 Subject: [PATCH 1/3] Remove setName from mergeExternals --- .../individual-example-tests/shifting-gradient.test.ts | 8 ++++---- .../individual-example-tests/spinning-triangle.test.ts | 6 +++--- packages/typegpu/src/core/resolve/externals.ts | 9 --------- packages/typegpu/src/core/root/init.ts | 5 ++++- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/apps/typegpu-docs/tests/individual-example-tests/shifting-gradient.test.ts b/apps/typegpu-docs/tests/individual-example-tests/shifting-gradient.test.ts index c848d13d86..1c4373cfaf 100644 --- a/apps/typegpu-docs/tests/individual-example-tests/shifting-gradient.test.ts +++ b/apps/typegpu-docs/tests/individual-example-tests/shifting-gradient.test.ts @@ -34,7 +34,7 @@ describe('react/shifting-gradient example', () => { return fullScreenTriangle_Output(vec4f(pos[vertexIndex], 0, 1), uv[vertexIndex]); } - @group(0) @binding(0) var time: f32; + @group(0) @binding(0) var item: f32; fn computeMaxSaturation(a: f32, b: f32) -> f32 { var k0 = 0f; @@ -218,11 +218,11 @@ describe('react/shifting-gradient example', () => { @fragment fn fragment(_arg_0: FragmentIn) -> @location(0) vec4f { let fromStart = vec3f(0.6279553771018982, 0.22486300766468048, 0.1258462816476822); let fromEnd = vec3f(0.45201370120048523, -0.03245693817734718, -0.31152817606925964); - let from_1 = mix(fromStart, fromEnd, ((sin(time) * 0.5f) + 0.5f)); + let from_1 = mix(fromStart, fromEnd, ((sin(item) * 0.5f) + 0.5f)); let toStart = vec3f(0.8664395809173584, -0.2338874489068985, 0.17949843406677246); let toEnd = vec3f(0.7016738653182983, 0.27456632256507874, -0.16915608942508698); - let to = mix(toStart, toEnd, ((cos((time * 1.5f)) * 0.5f) + 0.5f)); - let mixed = mix(from_1, to, ((((_arg_0.uv.x * 2f) - 1f) * 0.5f) + (sin((time + (_arg_0.uv.y * 3f))) * 0.5f))); + let to = mix(toStart, toEnd, ((cos((item * 1.5f)) * 0.5f) + 0.5f)); + let mixed = mix(from_1, to, ((((_arg_0.uv.x * 2f) - 1f) * 0.5f) + (sin((item + (_arg_0.uv.y * 3f))) * 0.5f))); return vec4f(oklabToRgb(mixed), 1f); }" `); diff --git a/apps/typegpu-docs/tests/individual-example-tests/spinning-triangle.test.ts b/apps/typegpu-docs/tests/individual-example-tests/spinning-triangle.test.ts index b6d81fc47a..6f21c2472d 100644 --- a/apps/typegpu-docs/tests/individual-example-tests/spinning-triangle.test.ts +++ b/apps/typegpu-docs/tests/individual-example-tests/spinning-triangle.test.ts @@ -24,7 +24,7 @@ describe('react/spinning-triangle example', () => { expect(shaderCodes).toMatchInlineSnapshot(` "const vertices: array = array(vec2f(0, 1), vec2f(-0.8660253882408142, -0.5), vec2f(0.8660253882408142, -0.5)); - @group(0) @binding(0) var time: f32; + @group(0) @binding(0) var item: f32; fn rotate(v: vec2f, angle: f32) -> vec2f { let pos = vec2f(((v.x * cos(angle)) - (v.y * sin(angle))), ((v.x * sin(angle)) + (v.y * cos(angle)))); @@ -44,7 +44,7 @@ describe('react/spinning-triangle example', () => { @vertex fn vertex(_arg_0: VertexIn) -> VertexOut { let local = vertices[_arg_0.vertexIndex]; - let rotated = rotate(local, (time * 0.1f)); + let rotated = rotate(local, (item * 0.1f)); return VertexOut(vec4f((rotated * 0.7f), 0f, 1f), length((local - vertices[0i])), length((local - vertices[1i])), length((local - vertices[2i]))); } @@ -235,7 +235,7 @@ describe('react/spinning-triangle example', () => { @fragment fn fragment(_arg_0: FragmentIn) -> @location(0) vec4f { let dist = (1f / (1.4f - min(min(_arg_0.dist0, _arg_0.dist1), _arg_0.dist2))); - let albedo = getGradientColor((((fract(((dist * 2f) - time)) * 2f) - 1f) + cos(time))); + let albedo = getGradientColor((((fract(((dist * 2f) - item)) * 2f) - 1f) + cos(item))); return vec4f(albedo, 1f); }" `); diff --git a/packages/typegpu/src/core/resolve/externals.ts b/packages/typegpu/src/core/resolve/externals.ts index f95dc67c6f..a3f74d6e9c 100644 --- a/packages/typegpu/src/core/resolve/externals.ts +++ b/packages/typegpu/src/core/resolve/externals.ts @@ -42,15 +42,6 @@ export function mergeExternals(existing: ExternalMap, newExternals: ExternalMap) } else { existing[key] = value; } - - // Giving name to external value, if it does not already have one. - if ( - value && - (typeof value === 'object' || typeof value === 'function') && - getName(value) === undefined - ) { - setName(value, key); - } } } diff --git a/packages/typegpu/src/core/root/init.ts b/packages/typegpu/src/core/root/init.ts index 075ac5e36a..3fb6af3bff 100644 --- a/packages/typegpu/src/core/root/init.ts +++ b/packages/typegpu/src/core/root/init.ts @@ -95,7 +95,7 @@ import { vec3f, vec3u } from '../../data/vector.ts'; import { u32 } from '../../data/numeric.ts'; import { ceil } from '../../std/numeric.ts'; import { allEq } from '../../std/boolean.ts'; -import { setName } from '../../shared/meta.ts'; +import { getName, setName } from '../../shared/meta.ts'; /** * Changes the given array to a vec of 3 numbers, filling missing values with 1. @@ -259,6 +259,9 @@ class WithBindingImpl implements WithBinding { const workgroupSize = workgroupSizeConfigs[callback.length] as v3u; const wrappedCallback = fn([u32, u32, u32])(callback as (...args: number[]) => void); + if (getName(wrappedCallback) === undefined) { + wrappedCallback.$name('wrappedCallback'); + } const sizeUniform = root.createUniform(vec3u); From 85a3bc39a89712eadc28134fb2d4da6843474ede Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:36:06 +0200 Subject: [PATCH 2/3] Add setName to replaceExternalsInWgsl --- .../typegpu/src/core/resolve/externals.ts | 5 ++- packages/typegpu/tests/rawFn.test.ts | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/typegpu/src/core/resolve/externals.ts b/packages/typegpu/src/core/resolve/externals.ts index a3f74d6e9c..ab3c885d0c 100644 --- a/packages/typegpu/src/core/resolve/externals.ts +++ b/packages/typegpu/src/core/resolve/externals.ts @@ -1,6 +1,6 @@ import { isLooseData } from '../../data/dataTypes.ts'; import { isWgslStruct } from '../../data/wgslTypes.ts'; -import { getName, hasTinyestMetadata, setName } from '../../shared/meta.ts'; +import { getName, hasTinyestMetadata, isNamable, setName } from '../../shared/meta.ts'; import { isWgsl, type ResolutionCtx } from '../../types.ts'; /** @@ -106,6 +106,9 @@ export function replaceExternalsInWgsl( } if (isResolvable(external)) { + if (isNamable(external) && getName(external) === undefined) { + setName(external, externalName.split('.').at(-1) as string); + } return acc.replaceAll(externalRegex, ctx.resolve(external).value); } diff --git a/packages/typegpu/tests/rawFn.test.ts b/packages/typegpu/tests/rawFn.test.ts index b63036de3b..19e310bfc2 100644 --- a/packages/typegpu/tests/rawFn.test.ts +++ b/packages/typegpu/tests/rawFn.test.ts @@ -539,6 +539,42 @@ describe('tgpu.fn with raw wgsl and missing types', () => { }" `); }); + + it('names resolved externals', () => { + const c1 = (() => tgpu.const(d.vec2u, d.vec2u(1)))(); // unnamed + const c2 = (() => tgpu.const(d.vec2u, d.vec2u(2)))(); // unnamed + const c3 = (() => tgpu.const(d.vec2u, d.vec2u(3)))(); // unnamed + const fn = tgpu.fn([])`() { + let a = n1; + let b = ext.n2; + let c = ext.n3.x; +}`.$uses({ + n1: c1, + ext: { n2: c2, n3: c3 }, + }); + + expect(tgpu.resolve([fn])).toMatchInlineSnapshot(` + "const n1: vec2u = vec2u(1); + + const n2: vec2u = vec2u(2); + + const n3: vec2u = vec2u(3); + + fn fn_1() { + let a = n1; + let b = n2; + let c = n3.x; + }" + `); + }); + + it("resolved externals's names stay the same", () => { + const c1 = (() => tgpu.const(d.vec2u, d.vec2u(1)))(); // unnamed + tgpu.resolve([tgpu.fn([])`() { let a = myConst; }`.$uses({ myConst: c1 })]); + tgpu.resolve([tgpu.fn([])`() { let a = otherName; }`.$uses({ otherName: c1 })]); + + expect(getName(c1)).toMatchInlineSnapshot(`"myConst"`); + }); }); describe('tgpu.computeFn with raw string WGSL implementation', () => { From 2df4b2c35853e8b94de227f830c743caa022d34a Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Tue, 16 Jun 2026 13:43:46 +0200 Subject: [PATCH 3/3] Name unnamed externals in propAccess and getById --- .../shifting-gradient.test.ts | 8 ++--- .../spinning-triangle.test.ts | 6 ++-- packages/typegpu/src/resolutionCtx.ts | 5 ++- packages/typegpu/src/tgsl/accessProp.ts | 7 +++- packages/typegpu/tests/tgslFn.test.ts | 32 +++++++++++++++++++ 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/apps/typegpu-docs/tests/individual-example-tests/shifting-gradient.test.ts b/apps/typegpu-docs/tests/individual-example-tests/shifting-gradient.test.ts index 1c4373cfaf..c848d13d86 100644 --- a/apps/typegpu-docs/tests/individual-example-tests/shifting-gradient.test.ts +++ b/apps/typegpu-docs/tests/individual-example-tests/shifting-gradient.test.ts @@ -34,7 +34,7 @@ describe('react/shifting-gradient example', () => { return fullScreenTriangle_Output(vec4f(pos[vertexIndex], 0, 1), uv[vertexIndex]); } - @group(0) @binding(0) var item: f32; + @group(0) @binding(0) var time: f32; fn computeMaxSaturation(a: f32, b: f32) -> f32 { var k0 = 0f; @@ -218,11 +218,11 @@ describe('react/shifting-gradient example', () => { @fragment fn fragment(_arg_0: FragmentIn) -> @location(0) vec4f { let fromStart = vec3f(0.6279553771018982, 0.22486300766468048, 0.1258462816476822); let fromEnd = vec3f(0.45201370120048523, -0.03245693817734718, -0.31152817606925964); - let from_1 = mix(fromStart, fromEnd, ((sin(item) * 0.5f) + 0.5f)); + let from_1 = mix(fromStart, fromEnd, ((sin(time) * 0.5f) + 0.5f)); let toStart = vec3f(0.8664395809173584, -0.2338874489068985, 0.17949843406677246); let toEnd = vec3f(0.7016738653182983, 0.27456632256507874, -0.16915608942508698); - let to = mix(toStart, toEnd, ((cos((item * 1.5f)) * 0.5f) + 0.5f)); - let mixed = mix(from_1, to, ((((_arg_0.uv.x * 2f) - 1f) * 0.5f) + (sin((item + (_arg_0.uv.y * 3f))) * 0.5f))); + let to = mix(toStart, toEnd, ((cos((time * 1.5f)) * 0.5f) + 0.5f)); + let mixed = mix(from_1, to, ((((_arg_0.uv.x * 2f) - 1f) * 0.5f) + (sin((time + (_arg_0.uv.y * 3f))) * 0.5f))); return vec4f(oklabToRgb(mixed), 1f); }" `); diff --git a/apps/typegpu-docs/tests/individual-example-tests/spinning-triangle.test.ts b/apps/typegpu-docs/tests/individual-example-tests/spinning-triangle.test.ts index 6f21c2472d..b6d81fc47a 100644 --- a/apps/typegpu-docs/tests/individual-example-tests/spinning-triangle.test.ts +++ b/apps/typegpu-docs/tests/individual-example-tests/spinning-triangle.test.ts @@ -24,7 +24,7 @@ describe('react/spinning-triangle example', () => { expect(shaderCodes).toMatchInlineSnapshot(` "const vertices: array = array(vec2f(0, 1), vec2f(-0.8660253882408142, -0.5), vec2f(0.8660253882408142, -0.5)); - @group(0) @binding(0) var item: f32; + @group(0) @binding(0) var time: f32; fn rotate(v: vec2f, angle: f32) -> vec2f { let pos = vec2f(((v.x * cos(angle)) - (v.y * sin(angle))), ((v.x * sin(angle)) + (v.y * cos(angle)))); @@ -44,7 +44,7 @@ describe('react/spinning-triangle example', () => { @vertex fn vertex(_arg_0: VertexIn) -> VertexOut { let local = vertices[_arg_0.vertexIndex]; - let rotated = rotate(local, (item * 0.1f)); + let rotated = rotate(local, (time * 0.1f)); return VertexOut(vec4f((rotated * 0.7f), 0f, 1f), length((local - vertices[0i])), length((local - vertices[1i])), length((local - vertices[2i]))); } @@ -235,7 +235,7 @@ describe('react/spinning-triangle example', () => { @fragment fn fragment(_arg_0: FragmentIn) -> @location(0) vec4f { let dist = (1f / (1.4f - min(min(_arg_0.dist0, _arg_0.dist1), _arg_0.dist2))); - let albedo = getGradientColor((((fract(((dist * 2f) - item)) * 2f) - 1f) + cos(item))); + let albedo = getGradientColor((((fract(((dist * 2f) - time)) * 2f) - 1f) + cos(time))); return vec4f(albedo, 1f); }" `); diff --git a/packages/typegpu/src/resolutionCtx.ts b/packages/typegpu/src/resolutionCtx.ts index f95399c67a..3956e0ecb6 100644 --- a/packages/typegpu/src/resolutionCtx.ts +++ b/packages/typegpu/src/resolutionCtx.ts @@ -51,7 +51,7 @@ import type { } from './types.ts'; import { CodegenState, isSelfResolvable, NormalState } from './types.ts'; import type { WgslEnableExtension } from './wgslExtensions.ts'; -import { getName, hasTinyestMetadata, setName } from './shared/meta.ts'; +import { getName, hasTinyestMetadata, isNamable, setName } from './shared/meta.ts'; import { FuncParameterType } from 'tinyest'; import { accessProp } from './tgsl/accessProp.ts'; import { createIoSchema } from './core/function/ioSchema.ts'; @@ -197,6 +197,9 @@ class ItemStateStackImpl implements ItemStateStack { } const external = layer.externalMap[id]; + if (isNamable(external) && getName(external) === undefined) { + setName(external, id); + } if (external !== undefined && external !== null) { return coerceToSnippet(external); diff --git a/packages/typegpu/src/tgsl/accessProp.ts b/packages/typegpu/src/tgsl/accessProp.ts index 147eddd2a7..b3cfc1fb2e 100644 --- a/packages/typegpu/src/tgsl/accessProp.ts +++ b/packages/typegpu/src/tgsl/accessProp.ts @@ -34,6 +34,7 @@ import { isKnownAtComptime } from '../types.ts'; import { coerceToSnippet, numericLiteralToSnippet } from './generationHelpers.ts'; import { InfixDispatch, infixOperators, type InfixOperatorName } from './infixDispatch.ts'; import { accessStructProp } from './accessStructProp.ts'; +import { getName, isNamable, setName } from '../shared/meta.ts'; const infixKinds = [ 'vec2f', @@ -205,7 +206,11 @@ export function accessProp(target: Snippet, propName: string): Snippet | undefin if (isKnownAtComptime(target) || target.dataType === UnknownData) { // oxlint-disable-next-line typescript/no-explicit-any -- we either know exactly what it is, or have no idea at all - return coerceToSnippet((target.value as any)[propName]); + const prop = (target.value as any)[propName]; + if (isNamable(prop) && getName(prop) === undefined) { + setName(prop, propName); + } + return coerceToSnippet(prop); } return undefined; diff --git a/packages/typegpu/tests/tgslFn.test.ts b/packages/typegpu/tests/tgslFn.test.ts index ba4d5165f2..85529afe89 100644 --- a/packages/typegpu/tests/tgslFn.test.ts +++ b/packages/typegpu/tests/tgslFn.test.ts @@ -1077,4 +1077,36 @@ describe('tgsl fn when using plugin', () => { }" `); }); + + it('names used externals', () => { + const myConst = (() => tgpu.const(d.u32, 1))(); // unnamed + const fn = () => { + 'use gpu'; + return myConst.$; + }; + + expect(tgpu.resolve([fn])).toMatchInlineSnapshot(` + "const myConst: u32 = 1u; + + fn fn_1() -> u32 { + return myConst; + }" + `); + }); + + it('names used nested externals', () => { + const EXT = { myConst: (() => tgpu.const(d.u32, 1))() /* unnamed */ }; + const fn = () => { + 'use gpu'; + return EXT.myConst.$; + }; + + expect(tgpu.resolve([fn])).toMatchInlineSnapshot(` + "const myConst: u32 = 1u; + + fn fn_1() -> u32 { + return myConst; + }" + `); + }); });