diff --git a/apps/typegpu-docs/src/content/docs/fundamentals/functions/index.mdx b/apps/typegpu-docs/src/content/docs/fundamentals/functions/index.mdx index 42855466e..3acc605b4 100644 --- a/apps/typegpu-docs/src/content/docs/fundamentals/functions/index.mdx +++ b/apps/typegpu-docs/src/content/docs/fundamentals/functions/index.mdx @@ -24,19 +24,17 @@ Then the actual WGSL implementation is passed in as an argument to a shell invoc The following code defines a function that accepts one argument and returns one value. ```ts -const getGradientColor = tgpu['~unstable'] - .fn([d.f32], d.vec4f)(/* wgsl */ `(ratio: f32) -> vec4f { - let color = mix(vec4f(0.769, 0.392, 1.0, 1), vec4f(0.114, 0.447, 0.941, 1), ratio); - return color; - }`); +const getGradientColor = tgpu.fn([d.f32], d.vec4f)(/* wgsl */ `(ratio: f32) -> vec4f { + let color = mix(vec4f(0.769, 0.392, 1.0, 1), vec4f(0.114, 0.447, 0.941, 1), ratio); + return color; +}`); // or -const getGradientColor = tgpu['~unstable'] - .fn([d.f32], d.vec4f) /* wgsl */`(ratio: f32) -> vec4f { - let color = mix(vec4f(0.769, 0.392, 1.0, 1), vec4f(0.114, 0.447, 0.941, 1), ratio); - return color; - }; +const getGradientColor = tgpu.fn([d.f32], d.vec4f) /* wgsl */`(ratio: f32) -> vec4f { + let color = mix(vec4f(0.769, 0.392, 1.0, 1), vec4f(0.114, 0.447, 0.941, 1), ratio); + return color; +}; ``` If you're using Visual Studio Code, you can use an [extension](https://marketplace.visualstudio.com/items?itemName=ggsimm.wgsl-literal) that brings syntax highlighting to the code fragments marked with `/* wgsl */` comments. @@ -47,18 +45,17 @@ Functions can use external resources passed inside a record via the `$uses` meth Externals can be any value or TypeGPU resource that can be resolved to WGSL (functions, buffer usages, slots, accessors, constants, variables, declarations, vectors, matrices, textures, samplers etc.). ```ts -const getBlue = tgpu['~unstable'].fn([], d.vec4f)`() -> vec4f { +const getBlue = tgpu.fn([], d.vec4f)`() -> vec4f { return vec4f(0.114, 0.447, 0.941, 1); }`; const purple = d.vec4f(0.769, 0.392, 1.0, 1); -const getGradientColor = tgpu['~unstable'] - .fn([d.f32], d.vec4f)`(ratio: f32) -> vec4f { - let color = mix(purple, getBlue(), ratio); - return color; - } - `.$uses({ purple, getBlue }); +const getGradientColor = tgpu.fn([d.f32], d.vec4f)`(ratio: f32) -> vec4f { + let color = mix(purple, getBlue(), ratio); + return color; +} +`.$uses({ purple, getBlue }); ``` The `getGradientColor` function, when resolved to WGSL, includes the definitions of all used external resources: diff --git a/apps/typegpu-docs/src/content/docs/fundamentals/tgsl.mdx b/apps/typegpu-docs/src/content/docs/fundamentals/tgsl.mdx index 2c8188230..468bcec10 100644 --- a/apps/typegpu-docs/src/content/docs/fundamentals/tgsl.mdx +++ b/apps/typegpu-docs/src/content/docs/fundamentals/tgsl.mdx @@ -35,24 +35,22 @@ const obstacles = root enabled: d.u32, }), MAX_OBSTACLES)); -const isInsideObstacle = tgpu['~unstable'].fn([d.i32, d.i32], d.bool)( - (x, y) => { - for (let obsIdx = 0; obsIdx < MAX_OBSTACLES; obsIdx++) { - const obs = obstacles.$[obsIdx]; - if (obs.enabled === 0) { - continue; - } - const minX = std.max(0, obs.center.x - d.i32(obs.size.x / 2)); - const maxX = std.min(gridSize, obs.center.x + d.i32(obs.size.x / 2)); - const minY = std.max(0, obs.center.y - d.i32(obs.size.y / 2)); - const maxY = std.min(gridSize, obs.center.y + d.i32(obs.size.y / 2)); - if (x >= minX && x <= maxX && y >= minY && y <= maxY) { - return true; - } +const isInsideObstacle = tgpu.fn([d.i32, d.i32], d.bool)((x, y) => { + for (let obsIdx = 0; obsIdx < MAX_OBSTACLES; obsIdx++) { + const obs = obstacles.$[obsIdx]; + if (obs.enabled === 0) { + continue; } - return false; - }, -); + const minX = std.max(0, obs.center.x - d.i32(obs.size.x / 2)); + const maxX = std.min(gridSize, obs.center.x + d.i32(obs.size.x / 2)); + const minY = std.max(0, obs.center.y - d.i32(obs.size.y / 2)); + const maxY = std.min(gridSize, obs.center.y + d.i32(obs.size.y / 2)); + if (x >= minX && x <= maxX && y >= minY && y <= maxY) { + return true; + } + } + return false; +}); ``` ```ts diff --git a/apps/typegpu-docs/src/content/docs/tooling/unplugin-typegpu.mdx b/apps/typegpu-docs/src/content/docs/tooling/unplugin-typegpu.mdx index 2784c5f53..9afaea831 100644 --- a/apps/typegpu-docs/src/content/docs/tooling/unplugin-typegpu.mdx +++ b/apps/typegpu-docs/src/content/docs/tooling/unplugin-typegpu.mdx @@ -23,16 +23,14 @@ The package includes the following functionalities: import tgpu from 'typegpu'; import * as d from 'typegpu/data'; - const add = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)( - (a, b) => a + b, - ); + const add = tgpu.fn([d.u32, d.u32], d.u32)((a, b) => a + b); ``` However, if the implementation function, or the shell, is referenced via a variable, the plugin will not recognize it as TGSL, thus to make it work, the function needs to be marked with a `"kernel"` directive. ```ts - const addFn = tgpu['~unstable'].fn([d.u32, d.u32], d.u32); + const addFn = tgpu.fn([d.u32, d.u32], d.u32); const add = addFn((a, b) => { 'kernel'; @@ -46,14 +44,14 @@ The package includes the following functionalities: return a + b; }; - const add = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)(addImpl); + const add = tgpu.fn([d.u32, d.u32], d.u32)(addImpl); ``` After transpiling the function, the JS implementation is removed from the bundle in order to save space. To be able to invoke the function both on GPU and CPU, it needs to be marked with `"kernel & js"` directive; ```ts - const add = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)((a, b) => { + const add = tgpu.fn([d.u32, d.u32], d.u32)((a, b) => { 'kernel & js'; return a + b; }); diff --git a/apps/typegpu-docs/src/content/examples/algorithms/matrix-next/computeShared.ts b/apps/typegpu-docs/src/content/examples/algorithms/matrix-next/computeShared.ts index 555a8ff22..61d55e627 100644 --- a/apps/typegpu-docs/src/content/examples/algorithms/matrix-next/computeShared.ts +++ b/apps/typegpu-docs/src/content/examples/algorithms/matrix-next/computeShared.ts @@ -7,13 +7,13 @@ import { computeLayout } from './types.ts'; const tileA = tgpu['~unstable'].workgroupVar(d.arrayOf(d.i32, TILE_SIZE ** 2)); const tileB = tgpu['~unstable'].workgroupVar(d.arrayOf(d.i32, TILE_SIZE ** 2)); -export const getIndex = tgpu['~unstable'].fn([d.u32, d.u32, d.u32], d.u32)( +export const getIndex = tgpu.fn([d.u32, d.u32, d.u32], d.u32)( (row, col, columns) => { return col + row * columns; }, ); -const getTileIndex = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)((row, col) => { +const getTileIndex = tgpu.fn([d.u32, d.u32], d.u32)((row, col) => { return col + row * TILE_SIZE; }); diff --git a/apps/typegpu-docs/src/content/examples/image-processing/ascii-filter/index.ts b/apps/typegpu-docs/src/content/examples/image-processing/ascii-filter/index.ts index ef68db2a3..f9224a32d 100644 --- a/apps/typegpu-docs/src/content/examples/image-processing/ascii-filter/index.ts +++ b/apps/typegpu-docs/src/content/examples/image-processing/ascii-filter/index.ts @@ -34,7 +34,7 @@ const displayModes = { * Adapted from the original Shadertoy implementation by movAX13h: * https://www.shadertoy.com/view/lssGDj */ -const characterFn = tgpu['~unstable'].fn([d.u32, d.vec2f], d.f32)((n, p) => { +const characterFn = tgpu.fn([d.u32, d.vec2f], d.f32)((n, p) => { // Transform texture coordinates to character bitmap coordinates (5x5 grid) const pos = std.floor(std.add(std.mul(p, d.vec2f(-4, 4)), 2.5)); diff --git a/apps/typegpu-docs/src/content/examples/rendering/3d-fish/render.ts b/apps/typegpu-docs/src/content/examples/rendering/3d-fish/render.ts index 97e3cc8b6..f9a65909c 100644 --- a/apps/typegpu-docs/src/content/examples/rendering/3d-fish/render.ts +++ b/apps/typegpu-docs/src/content/examples/rendering/3d-fish/render.ts @@ -93,10 +93,7 @@ export const vertexShader = tgpu['~unstable'].vertexFn({ }; }); -const sampleTexture = tgpu['~unstable'].fn( - [d.vec2f], - d.vec4f, -) /* wgsl */`(uv: vec2f) -> vec4f { +const sampleTexture = tgpu.fn([d.vec2f], d.vec4f)`(uv) { return textureSample(layout.$.modelTexture, layout.$.sampler, uv); } `.$uses({ layout }); diff --git a/apps/typegpu-docs/src/content/examples/rendering/3d-fish/tgsl-helpers.ts b/apps/typegpu-docs/src/content/examples/rendering/3d-fish/tgsl-helpers.ts index 91d60189c..462f0556b 100644 --- a/apps/typegpu-docs/src/content/examples/rendering/3d-fish/tgsl-helpers.ts +++ b/apps/typegpu-docs/src/content/examples/rendering/3d-fish/tgsl-helpers.ts @@ -3,52 +3,51 @@ import * as d from 'typegpu/data'; import * as std from 'typegpu/std'; import { Line3 } from './schemas.ts'; -export const projectPointOnLine = tgpu['~unstable'].fn( - [d.vec3f, Line3], - d.vec3f, -)((point, line) => { - const pointVector = std.sub(point, line.origin); - const projection = std.dot(pointVector, line.dir); - const closestPoint = std.add( - line.origin, - std.mul(projection, line.dir), - ); - return closestPoint; -}); +export const projectPointOnLine = tgpu.fn([d.vec3f, Line3], d.vec3f)( + (point, line) => { + const pointVector = std.sub(point, line.origin); + const projection = std.dot(pointVector, line.dir); + const closestPoint = std.add( + line.origin, + std.mul(projection, line.dir), + ); + return closestPoint; + }, +); export const PosAndNormal = d.struct({ position: d.vec3f, normal: d.vec3f, }); -export const applySinWave = tgpu['~unstable'].fn( - [d.u32, PosAndNormal, d.f32], - PosAndNormal, -)((index, vertex, time) => { - const a = -60.1; - const b = 0.8; - const c = 6.1; - // z += sin(index + (time / a + x) / b) / c +export const applySinWave = tgpu.fn([d.u32, PosAndNormal, d.f32], PosAndNormal)( + (index, vertex, time) => { + const a = -60.1; + const b = 0.8; + const c = 6.1; + // z += sin(index + (time / a + x) / b) / c - const positionModification = d.vec3f( - 0, - 0, - std.sin(d.f32(index) + (time / a + vertex.position.x) / b) / c, - ); + const positionModification = d.vec3f( + 0, + 0, + std.sin(d.f32(index) + (time / a + vertex.position.x) / b) / c, + ); - const coeff = std.cos(d.f32(index) + (time / a + vertex.position.x) / b) / c; - const newOX = std.normalize(d.vec3f(1, 0, coeff)); - const newOZ = d.vec3f(-newOX.z, 0, newOX.x); - const newNormalXZ = std.add( - std.mul(vertex.normal.x, newOX), - std.mul(vertex.normal.z, newOZ), - ); + const coeff = std.cos(d.f32(index) + (time / a + vertex.position.x) / b) / + c; + const newOX = std.normalize(d.vec3f(1, 0, coeff)); + const newOZ = d.vec3f(-newOX.z, 0, newOX.x); + const newNormalXZ = std.add( + std.mul(vertex.normal.x, newOX), + std.mul(vertex.normal.z, newOZ), + ); - const wavedNormal = d.vec3f(newNormalXZ.x, vertex.normal.y, newNormalXZ.z); - const wavedPosition = std.add(vertex.position, positionModification); + const wavedNormal = d.vec3f(newNormalXZ.x, vertex.normal.y, newNormalXZ.z); + const wavedPosition = std.add(vertex.position, positionModification); - return PosAndNormal({ - position: wavedPosition, - normal: wavedNormal, - }); -}); + return PosAndNormal({ + position: wavedPosition, + normal: wavedNormal, + }); + }, +); diff --git a/apps/typegpu-docs/src/content/examples/rendering/box-raytracing/index.ts b/apps/typegpu-docs/src/content/examples/rendering/box-raytracing/index.ts index bd10d5729..77219c318 100644 --- a/apps/typegpu-docs/src/content/examples/rendering/box-raytracing/index.ts +++ b/apps/typegpu-docs/src/content/examples/rendering/box-raytracing/index.ts @@ -93,10 +93,10 @@ const uniforms = root.createUniform(Uniforms); // functions -const getBoxIntersection = tgpu['~unstable'].fn( +const getBoxIntersection = tgpu.fn( [AxisAlignedBounds, Ray], IntersectionStruct, -) /* wgsl */`(bounds: AxisAlignedBounds, ray: Ray) -> IntersectionStruct { +) /* wgsl */`(bounds, ray) { var tMin: f32; var tMax: f32; var tMinY: f32; @@ -154,7 +154,7 @@ const getBoxIntersection = tgpu['~unstable'].fn( return IntersectionStruct(tMin > 0 && tMax > 0, tMin, tMax); }` - .$uses({ AxisAlignedBounds, Ray, IntersectionStruct }); + .$uses({ IntersectionStruct }); const Varying = { rayWorldOrigin: d.vec3f, diff --git a/apps/typegpu-docs/src/content/examples/rendering/caustics/index.ts b/apps/typegpu-docs/src/content/examples/rendering/caustics/index.ts index b1ecaec5a..8b2cff2ef 100644 --- a/apps/typegpu-docs/src/content/examples/rendering/caustics/index.ts +++ b/apps/typegpu-docs/src/content/examples/rendering/caustics/index.ts @@ -20,14 +20,14 @@ const mainVertex = tgpu['~unstable'].vertexFn({ * Given a coordinate, it returns a grayscale floor tile pattern at that * location. */ -const tilePattern = tgpu['~unstable'].fn([d.vec2f], d.f32)((uv) => { +const tilePattern = tgpu.fn([d.vec2f], d.f32)((uv) => { const tiledUv = std.fract(uv); const proximity = std.abs(std.sub(std.mul(tiledUv, 2), 1)); const maxProximity = std.max(proximity.x, proximity.y); return std.clamp(std.pow(1 - maxProximity, 0.6) * 5, 0, 1); }); -const caustics = tgpu['~unstable'].fn([d.vec2f, d.f32, d.vec3f], d.vec3f)( +const caustics = tgpu.fn([d.vec2f, d.f32, d.vec3f], d.vec3f)( (uv, time, profile) => { const distortion = perlin3d.sample(d.vec3f(std.mul(uv, 0.5), time * 0.2)); // Distorting UV coordinates @@ -37,20 +37,18 @@ const caustics = tgpu['~unstable'].fn([d.vec2f, d.f32, d.vec3f], d.vec3f)( }, ); -const clamp01 = tgpu['~unstable'].fn([d.f32], d.f32)((v) => { - return std.clamp(v, 0, 1); -}); +const clamp01 = tgpu.fn([d.f32], d.f32)((v) => std.clamp(v, 0, 1)); /** * Returns a transformation matrix that represents an `angle` rotation * in the XY plane (around the imaginary Z axis) */ -const rotateXY = tgpu['~unstable'].fn([d.f32], d.mat2x2f)((angle) => { - return d.mat2x2f( +const rotateXY = tgpu.fn([d.f32], d.mat2x2f)((angle) => + d.mat2x2f( /* right */ d.vec2f(std.cos(angle), std.sin(angle)), /* up */ d.vec2f(-std.sin(angle), std.cos(angle)), - ); -}); + ) +); const root = await tgpu.init(); diff --git a/apps/typegpu-docs/src/content/examples/rendering/cubemap-reflection/helpers.ts b/apps/typegpu-docs/src/content/examples/rendering/cubemap-reflection/helpers.ts index 7c41f0905..78d5026a4 100644 --- a/apps/typegpu-docs/src/content/examples/rendering/cubemap-reflection/helpers.ts +++ b/apps/typegpu-docs/src/content/examples/rendering/cubemap-reflection/helpers.ts @@ -2,34 +2,27 @@ import tgpu from 'typegpu'; import * as d from 'typegpu/data'; import * as std from 'typegpu/std'; -export const unpackVec2u = tgpu['~unstable'].fn([d.vec2u], d.vec4f)( - (packed) => { - const xy = std.unpack2x16float(packed.x); - const zw = std.unpack2x16float(packed.y); - return d.vec4f(xy, zw); - }, -); +export const unpackVec2u = tgpu.fn([d.vec2u], d.vec4f)((packed) => { + const xy = std.unpack2x16float(packed.x); + const zw = std.unpack2x16float(packed.y); + return d.vec4f(xy, zw); +}); -export const packVec2u = tgpu['~unstable'].fn([d.vec4f], d.vec2u)((toPack) => { +export const packVec2u = tgpu.fn([d.vec4f], d.vec2u)((toPack) => { const xy = std.pack2x16float(toPack.xy); const zw = std.pack2x16float(toPack.zw); return d.vec2u(xy, zw); }); -export const getAverageNormal = tgpu['~unstable'].fn([ - d.vec4f, - d.vec4f, - d.vec4f, -], d.vec4f)((v1, v2, v3) => { - 'kernel & js'; - const edge1 = std.sub(v2.xyz, v1.xyz); - const edge2 = std.sub(v3.xyz, v1.xyz); - return std.normalize(d.vec4f(std.cross(edge1, edge2), 0)); -}); +export const getAverageNormal = tgpu.fn([d.vec4f, d.vec4f, d.vec4f], d.vec4f)( + (v1, v2, v3) => { + 'kernel & js'; + const edge1 = std.sub(v2.xyz, v1.xyz); + const edge2 = std.sub(v3.xyz, v1.xyz); + return std.normalize(d.vec4f(std.cross(edge1, edge2), 0)); + }, +); -export const calculateMidpoint = tgpu['~unstable'].fn( - [d.vec4f, d.vec4f], - d.vec4f, -)((v1, v2) => { - return d.vec4f(std.mul(0.5, std.add(v1.xyz, v2.xyz)), 1); -}); +export const calculateMidpoint = tgpu.fn([d.vec4f, d.vec4f], d.vec4f)( + (v1, v2) => d.vec4f(std.mul(0.5, std.add(v1.xyz, v2.xyz)), 1), +); diff --git a/apps/typegpu-docs/src/content/examples/rendering/perlin-noise/index.ts b/apps/typegpu-docs/src/content/examples/rendering/perlin-noise/index.ts index bbc0220d9..c94bae841 100644 --- a/apps/typegpu-docs/src/content/examples/rendering/perlin-noise/index.ts +++ b/apps/typegpu-docs/src/content/examples/rendering/perlin-noise/index.ts @@ -22,12 +22,12 @@ const gridSizeAccess = tgpu['~unstable'].accessor(d.f32); const timeAccess = tgpu['~unstable'].accessor(d.f32); const sharpnessAccess = tgpu['~unstable'].accessor(d.f32); -const exponentialSharpen = tgpu['~unstable'].fn([d.f32, d.f32], d.f32)( - (n, sharpness) => sign(n) * pow(abs(n), 1 - sharpness), +const exponentialSharpen = tgpu.fn([d.f32, d.f32], d.f32)((n, sharpness) => + sign(n) * pow(abs(n), 1 - sharpness) ); -const tanhSharpen = tgpu['~unstable'].fn([d.f32, d.f32], d.f32)( - (n, sharpness) => tanh(n * (1 + sharpness * 10)), +const tanhSharpen = tgpu.fn([d.f32, d.f32], d.f32)((n, sharpness) => + tanh(n * (1 + sharpness * 10)) ); const sharpenFnSlot = tgpu['~unstable'].slot< diff --git a/apps/typegpu-docs/src/content/examples/rendering/xor-dev-centrifuge-2/index.ts b/apps/typegpu-docs/src/content/examples/rendering/xor-dev-centrifuge-2/index.ts index 29667b24f..f95a135f0 100644 --- a/apps/typegpu-docs/src/content/examples/rendering/xor-dev-centrifuge-2/index.ts +++ b/apps/typegpu-docs/src/content/examples/rendering/xor-dev-centrifuge-2/index.ts @@ -23,9 +23,9 @@ import { abs, add, atan2, cos, gt, length, mul, normalize, select, sign, sub, ta * For some reason, tanh in WebGPU breaks down hard outside * of the <10, -10> range. */ -const safeTanh3 = tgpu['~unstable'].fn([d.vec3f], d.vec3f)((v) => { - return select(tanh(v), sign(v), gt(abs(v), d.vec3f(10))); -}); +const safeTanh3 = tgpu.fn([d.vec3f], d.vec3f)((v) => + select(tanh(v), sign(v), gt(abs(v), d.vec3f(10))) +); // Roots are your GPU handle, and can be used to allocate memory, dispatch // shaders, etc. diff --git a/apps/typegpu-docs/src/content/examples/rendering/xor-dev-runner/index.ts b/apps/typegpu-docs/src/content/examples/rendering/xor-dev-runner/index.ts index ddf99faba..00d077c38 100644 --- a/apps/typegpu-docs/src/content/examples/rendering/xor-dev-runner/index.ts +++ b/apps/typegpu-docs/src/content/examples/rendering/xor-dev-runner/index.ts @@ -25,9 +25,9 @@ import { abs, add, cos, max, min, mul, normalize, select, sign, sin, sub, tanh } * For some reason, tanh in WebGPU breaks down hard outside * of the <10, -10> range. */ -const safeTanh = tgpu['~unstable'].fn([d.f32], d.f32)((v) => { - return select(tanh(v), sign(v), abs(v) > 10); -}); +const safeTanh = tgpu.fn([d.f32], d.f32)((v) => + select(tanh(v), sign(v), abs(v) > 10) +); // Functions can still be written in WGSL, if that's what you prefer. // You can omit the argument and return types, and we'll generate them @@ -35,7 +35,7 @@ const safeTanh = tgpu['~unstable'].fn([d.f32], d.f32)((v) => { /** * A modulo that replicates the behavior of GLSL's `mod` function. */ -const mod = tgpu['~unstable'].fn([d.vec3f, d.f32], d.vec3f)`(v, a) { +const mod = tgpu.fn([d.vec3f, d.f32], d.vec3f)`(v, a) { return fract(v / a) * a; }`; @@ -43,13 +43,13 @@ const mod = tgpu['~unstable'].fn([d.vec3f, d.f32], d.vec3f)`(v, a) { * Returns a transformation matrix that represents an `angle` rotation * in the XZ plane (around the Y axis) */ -const rotateXZ = tgpu['~unstable'].fn([d.f32], d.mat3x3f)((angle) => { - return d.mat3x3f( +const rotateXZ = tgpu.fn([d.f32], d.mat3x3f)((angle) => + d.mat3x3f( /* right */ d.vec3f(cos(angle), 0, sin(angle)), /* up */ d.vec3f(0, 1, 0), /* forward */ d.vec3f(-sin(angle), 0, cos(angle)), - ); -}); + ) +); // Roots are your GPU handle, and can be used to allocate memory, dispatch // shaders, etc. diff --git a/apps/typegpu-docs/src/content/examples/simple/oklab/index.ts b/apps/typegpu-docs/src/content/examples/simple/oklab/index.ts index 3c9f81e90..a34f7e885 100644 --- a/apps/typegpu-docs/src/content/examples/simple/oklab/index.ts +++ b/apps/typegpu-docs/src/content/examples/simple/oklab/index.ts @@ -60,14 +60,14 @@ const layout = tgpu.bindGroupLayout({ uniforms: { uniform: Uniforms }, }); -const scaleView = tgpu['~unstable'].fn([d.vec2f], d.vec2f)((pos) => { +const scaleView = tgpu.fn([d.vec2f], d.vec2f)((pos) => { 'kernel & js'; return d.vec2f(0.3 * pos.x, (pos.y * 1.2 + 1) * 0.5); }); // #region Patterns -const patternFn = tgpu['~unstable'].fn([d.vec2f, d.vec3f], d.f32); +const patternFn = tgpu.fn([d.vec2f, d.vec3f], d.f32); const patternCheckers = patternFn((uv) => { 'kernel'; @@ -109,9 +109,7 @@ const mainFragment = tgpu['~unstable'].fragmentFn({ return d.vec4f(select(color, mul(patternScaled, color), outOfGamut), 1); }); -const alphaFromUniforms = tgpu['~unstable'].fn([], d.f32)( - () => layout.$.uniforms.alpha, -); +const alphaFromUniforms = tgpu.fn([], d.f32)(() => layout.$.uniforms.alpha); const root = await tgpu.init(); diff --git a/apps/typegpu-docs/src/content/examples/simple/triangle/index.ts b/apps/typegpu-docs/src/content/examples/simple/triangle/index.ts index 052318c19..d9553b6fd 100644 --- a/apps/typegpu-docs/src/content/examples/simple/triangle/index.ts +++ b/apps/typegpu-docs/src/content/examples/simple/triangle/index.ts @@ -1,26 +1,13 @@ import tgpu from 'typegpu'; import * as d from 'typegpu/data'; -const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); -const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const context = canvas.getContext('webgpu') as GPUCanvasContext; - const purple = d.vec4f(0.769, 0.392, 1.0, 1); const blue = d.vec4f(0.114, 0.447, 0.941, 1); -const root = await tgpu.init(); - -context.configure({ - device: root.device, - format: presentationFormat, - alphaMode: 'premultiplied', -}); - -const getGradientColor = tgpu['~unstable'].fn([d.f32], d.vec4f)( - /* wgsl */ `(ratio: f32) -> vec4f { - return mix(purple, blue, ratio); - }`, -).$uses({ purple, blue }); +const getGradientColor = tgpu.fn([d.f32], d.vec4f) /* wgsl */`(ratio) { + return mix(purple, blue, ratio); +} +`.$uses({ purple, blue }); const mainVertex = tgpu['~unstable'].vertexFn({ in: { vertexIndex: d.builtin.vertexIndex }, @@ -49,6 +36,18 @@ const mainFragment = tgpu['~unstable'].fragmentFn({ } `.$uses({ getGradientColor }); +const root = await tgpu.init(); + +const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); +const canvas = document.querySelector('canvas') as HTMLCanvasElement; +const context = canvas.getContext('webgpu') as GPUCanvasContext; + +context.configure({ + device: root.device, + format: presentationFormat, + alphaMode: 'premultiplied', +}); + const pipeline = root['~unstable'] .withVertex(mainVertex, {}) .withFragment(mainFragment, { format: presentationFormat }) diff --git a/apps/typegpu-docs/src/content/examples/simulation/boids-next/index.ts b/apps/typegpu-docs/src/content/examples/simulation/boids-next/index.ts index 6a29b16fa..a3082e68f 100644 --- a/apps/typegpu-docs/src/content/examples/simulation/boids-next/index.ts +++ b/apps/typegpu-docs/src/content/examples/simulation/boids-next/index.ts @@ -5,17 +5,15 @@ import * as std from 'typegpu/std'; const triangleAmount = 1000; const triangleSize = 0.03; -const rotate = tgpu['~unstable'].fn([d.vec2f, d.f32], d.vec2f)((v, angle) => { +const rotate = tgpu.fn([d.vec2f, d.f32], d.vec2f)((v, angle) => { const cos = std.cos(angle); const sin = std.sin(angle); return d.vec2f(v.x * cos - v.y * sin, v.x * sin + v.y * cos); }); -const getRotationFromVelocity = tgpu['~unstable'].fn([d.vec2f], d.f32)( - (velocity) => { - return -std.atan2(velocity.x, velocity.y); - }, -); +const getRotationFromVelocity = tgpu.fn([d.vec2f], d.f32)((velocity) => { + return -std.atan2(velocity.x, velocity.y); +}); const TriangleData = d.struct({ position: d.vec2f, diff --git a/apps/typegpu-docs/src/content/examples/simulation/confetti/index.ts b/apps/typegpu-docs/src/content/examples/simulation/confetti/index.ts index 78cf0443a..3f81c2a94 100644 --- a/apps/typegpu-docs/src/content/examples/simulation/confetti/index.ts +++ b/apps/typegpu-docs/src/content/examples/simulation/confetti/index.ts @@ -1,5 +1,6 @@ import tgpu from 'typegpu'; import * as d from 'typegpu/data'; +import { cos, sin } from 'typegpu/std'; // constants @@ -84,17 +85,14 @@ const dataLayout = tgpu.vertexLayout( // functions -const rotate = tgpu['~unstable'].fn( - [d.vec2f, d.f32], - d.vec2f, -) /* wgsl */`(v: vec2f, angle: f32) -> vec2f { - let pos = vec2( +const rotate = tgpu.fn([d.vec2f, d.f32], d.vec2f)((v, angle) => { + const pos = d.vec2f( (v.x * cos(angle)) - (v.y * sin(angle)), - (v.x * sin(angle)) + (v.y * cos(angle)) + (v.x * sin(angle)) + (v.y * cos(angle)), ); return pos; -}`; +}); const mainVert = tgpu['~unstable'].vertexFn({ in: { diff --git a/apps/typegpu-docs/src/content/examples/simulation/fluid-double-buffering/index.ts b/apps/typegpu-docs/src/content/examples/simulation/fluid-double-buffering/index.ts index 73a6bf060..77667e6de 100644 --- a/apps/typegpu-docs/src/content/examples/simulation/fluid-double-buffering/index.ts +++ b/apps/typegpu-docs/src/content/examples/simulation/fluid-double-buffering/index.ts @@ -3,18 +3,6 @@ import tgpu, { type TgpuBufferMutable, type TgpuBufferReadonly } from 'typegpu'; import * as d from 'typegpu/data'; import * as std from 'typegpu/std'; -const canvas = document.querySelector('canvas') as HTMLCanvasElement; -const context = canvas.getContext('webgpu') as GPUCanvasContext; -const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); - -const root = await tgpu.init(); - -context.configure({ - device: root.device, - format: presentationFormat, - alphaMode: 'premultiplied', -}); - const MAX_GRID_SIZE = 1024; type GridData = typeof GridData; @@ -35,9 +23,6 @@ const BoxObstacle = d.struct({ const gridSize = 256; -const gridAlphaBuffer = root.createBuffer(GridData).$usage('storage'); -const gridBetaBuffer = root.createBuffer(GridData).$usage('storage'); - const inputGridSlot = tgpu['~unstable'].slot< TgpuBufferReadonly | TgpuBufferMutable >(); @@ -46,42 +31,35 @@ const outputGridSlot = tgpu['~unstable'].slot>(); const MAX_OBSTACLES = 4; const BoxObstacleArray = d.arrayOf(BoxObstacle, MAX_OBSTACLES); -const prevObstacles = root.createReadonly(BoxObstacleArray); -const obstacles = root.createReadonly(BoxObstacleArray); - -const isValidCoord = tgpu['~unstable'].fn([d.i32, d.i32], d.bool)((x, y) => +const isValidCoord = tgpu.fn([d.i32, d.i32], d.bool)((x, y) => x < gridSize && x >= 0 && y < gridSize && y >= 0 ); -const coordsToIndex = tgpu['~unstable'].fn([d.i32, d.i32], d.i32)((x, y) => +const coordsToIndex = tgpu.fn([d.i32, d.i32], d.i32)((x, y) => x + y * gridSize ); -const getCell = tgpu['~unstable'].fn([d.i32, d.i32], d.vec4f)((x, y) => +const getCell = tgpu.fn([d.i32, d.i32], d.vec4f)((x, y) => inputGridSlot.$[coordsToIndex(x, y)] ); -const setCell = tgpu['~unstable'].fn([d.i32, d.i32, d.vec4f])((x, y, value) => { +const setCell = tgpu.fn([d.i32, d.i32, d.vec4f])((x, y, value) => { const index = coordsToIndex(x, y); outputGridSlot.$[index] = value; }); -const setVelocity = tgpu['~unstable'].fn([d.i32, d.i32, d.vec2f])( - (x, y, velocity) => { - const index = coordsToIndex(x, y); - outputGridSlot.$[index].x = velocity.x; - outputGridSlot.$[index].y = velocity.y; - }, -); +const setVelocity = tgpu.fn([d.i32, d.i32, d.vec2f])((x, y, velocity) => { + const index = coordsToIndex(x, y); + outputGridSlot.$[index].x = velocity.x; + outputGridSlot.$[index].y = velocity.y; +}); -const addDensity = tgpu['~unstable'].fn([d.i32, d.i32, d.f32])( - (x, y, density) => { - const index = coordsToIndex(x, y); - outputGridSlot.$[index].z = inputGridSlot.$[index].z + density; - }, -); +const addDensity = tgpu.fn([d.i32, d.i32, d.f32])((x, y, density) => { + const index = coordsToIndex(x, y); + outputGridSlot.$[index].z = inputGridSlot.$[index].z + density; +}); -const flowFromCell = tgpu['~unstable'].fn([d.i32, d.i32, d.i32, d.i32], d.f32)( +const flowFromCell = tgpu.fn([d.i32, d.i32, d.i32, d.i32], d.f32)( (myX, myY, x, y) => { if (!isValidCoord(x, y)) { return 0; @@ -110,32 +88,36 @@ const flowFromCell = tgpu['~unstable'].fn([d.i32, d.i32, d.i32, d.i32], d.f32)( }, ); +const root = await tgpu.init(); + +const gridAlphaBuffer = root.createBuffer(GridData).$usage('storage'); +const gridBetaBuffer = root.createBuffer(GridData).$usage('storage'); +const prevObstacles = root.createReadonly(BoxObstacleArray); +const obstacles = root.createReadonly(BoxObstacleArray); const time = root.createUniform(d.f32); -const isInsideObstacle = tgpu['~unstable'].fn([d.i32, d.i32], d.bool)( - (x, y) => { - for (let obsIdx = 0; obsIdx < MAX_OBSTACLES; obsIdx++) { - const obs = obstacles.$[obsIdx]; +const isInsideObstacle = tgpu.fn([d.i32, d.i32], d.bool)((x, y) => { + for (let obsIdx = 0; obsIdx < MAX_OBSTACLES; obsIdx++) { + const obs = obstacles.$[obsIdx]; - if (obs.enabled === 0) { - continue; - } + if (obs.enabled === 0) { + continue; + } - const minX = std.max(0, obs.center.x - d.i32(obs.size.x / 2)); - const maxX = std.min(gridSize, obs.center.x + d.i32(obs.size.x / 2)); - const minY = std.max(0, obs.center.y - d.i32(obs.size.y / 2)); - const maxY = std.min(gridSize, obs.center.y + d.i32(obs.size.y / 2)); + const minX = std.max(0, obs.center.x - d.i32(obs.size.x / 2)); + const maxX = std.min(gridSize, obs.center.x + d.i32(obs.size.x / 2)); + const minY = std.max(0, obs.center.y - d.i32(obs.size.y / 2)); + const maxY = std.min(gridSize, obs.center.y + d.i32(obs.size.y / 2)); - if (x >= minX && x <= maxX && y >= minY && y <= maxY) { - return true; - } + if (x >= minX && x <= maxX && y >= minY && y <= maxY) { + return true; } + } - return false; - }, -); + return false; +}); -const isValidFlowOut = tgpu['~unstable'].fn([d.i32, d.i32], d.bool)((x, y) => { +const isValidFlowOut = tgpu.fn([d.i32, d.i32], d.bool)((x, y) => { if (!isValidCoord(x, y)) { return false; } @@ -147,52 +129,50 @@ const isValidFlowOut = tgpu['~unstable'].fn([d.i32, d.i32], d.bool)((x, y) => { return true; }); -const computeVelocity = tgpu['~unstable'].fn([d.i32, d.i32], d.vec2f)( - (x, y) => { - const gravityCost = 0.5; - - const neighborOffsets = [ - d.vec2i(0, 1), - d.vec2i(0, -1), - d.vec2i(1, 0), - d.vec2i(-1, 0), - ]; - - const cell = getCell(x, y); - let leastCost = cell.z; - - const dirChoices = [ - d.vec2f(0, 0), - d.vec2f(0, 0), - d.vec2f(0, 0), - d.vec2f(0, 0), - ]; - let dirChoiceCount = 1; - - for (let i = 0; i < 4; i++) { - const offset = neighborOffsets[i]; - const neighborDensity = getCell(x + offset.x, y + offset.y); - const cost = neighborDensity.z + d.f32(offset.y) * gravityCost; - - if (!isValidFlowOut(x + offset.x, y + offset.y)) { - continue; - } +const computeVelocity = tgpu.fn([d.i32, d.i32], d.vec2f)((x, y) => { + const gravityCost = 0.5; + + const neighborOffsets = [ + d.vec2i(0, 1), + d.vec2i(0, -1), + d.vec2i(1, 0), + d.vec2i(-1, 0), + ]; + + const cell = getCell(x, y); + let leastCost = cell.z; + + const dirChoices = [ + d.vec2f(0, 0), + d.vec2f(0, 0), + d.vec2f(0, 0), + d.vec2f(0, 0), + ]; + let dirChoiceCount = 1; + + for (let i = 0; i < 4; i++) { + const offset = neighborOffsets[i]; + const neighborDensity = getCell(x + offset.x, y + offset.y); + const cost = neighborDensity.z + d.f32(offset.y) * gravityCost; + + if (!isValidFlowOut(x + offset.x, y + offset.y)) { + continue; + } - if (cost === leastCost) { - dirChoices[dirChoiceCount] = d.vec2f(d.f32(offset.x), d.f32(offset.y)); - dirChoiceCount++; - } else if (cost < leastCost) { - leastCost = cost; - dirChoices[0] = d.vec2f(d.f32(offset.x), d.f32(offset.y)); - dirChoiceCount = 1; - } + if (cost === leastCost) { + dirChoices[dirChoiceCount] = d.vec2f(d.f32(offset.x), d.f32(offset.y)); + dirChoiceCount++; + } else if (cost < leastCost) { + leastCost = cost; + dirChoices[0] = d.vec2f(d.f32(offset.x), d.f32(offset.y)); + dirChoiceCount = 1; } + } - const leastCostDir = - dirChoices[d.u32(randf.sample() * d.f32(dirChoiceCount))]; - return leastCostDir; - }, -); + const leastCostDir = + dirChoices[d.u32(randf.sample() * d.f32(dirChoiceCount))]; + return leastCostDir; +}); const mainInitWorld = tgpu['~unstable'].computeFn({ in: { gid: d.builtin.globalInvocationId }, @@ -336,7 +316,7 @@ const sourceParams = root.createUniform(d.struct({ intensity: d.f32, })); -const getMinimumInFlow = tgpu['~unstable'].fn([d.i32, d.i32], d.f32)((x, y) => { +const getMinimumInFlow = tgpu.fn([d.i32, d.i32], d.f32)((x, y) => { const gridSizeF = d.f32(gridSize); const sourceRadius = std.max(1, sourceParams.$.radius * gridSizeF); const sourcePos = d.vec2f( @@ -476,6 +456,16 @@ const fragmentMain = tgpu['~unstable'].fragmentFn({ ); }); +const canvas = document.querySelector('canvas') as HTMLCanvasElement; +const context = canvas.getContext('webgpu') as GPUCanvasContext; +const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); + +context.configure({ + device: root.device, + format: presentationFormat, + alphaMode: 'premultiplied', +}); + function makePipelines( inputGridReadonly: TgpuBufferReadonly, outputGridMutable: TgpuBufferMutable, diff --git a/apps/typegpu-docs/src/content/examples/simulation/fluid-with-atomics/index.ts b/apps/typegpu-docs/src/content/examples/simulation/fluid-with-atomics/index.ts index 61847d7df..b652538b4 100644 --- a/apps/typegpu-docs/src/content/examples/simulation/fluid-with-atomics/index.ts +++ b/apps/typegpu-docs/src/content/examples/simulation/fluid-with-atomics/index.ts @@ -67,121 +67,113 @@ const squareBuffer = root ]) .$usage('vertex'); -const getIndex = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)((x, y) => { +const getIndex = tgpu.fn([d.u32, d.u32], d.u32)((x, y) => { const h = size.$.y; const w = size.$.x; return (y % h) * w + (x % w); }); -const getCell = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)((x, y) => +const getCell = tgpu.fn([d.u32, d.u32], d.u32)((x, y) => currentStateStorage.$[getIndex(x, y)] ); -const getCellNext = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)((x, y) => +const getCellNext = tgpu.fn([d.u32, d.u32], d.u32)((x, y) => std.atomicLoad(nextState.$[getIndex(x, y)]) ); -const updateCell = tgpu['~unstable'].fn([d.u32, d.u32, d.u32])( - (x, y, value) => { - std.atomicStore(nextState.$[getIndex(x, y)], value); - }, -); +const updateCell = tgpu.fn([d.u32, d.u32, d.u32])((x, y, value) => { + std.atomicStore(nextState.$[getIndex(x, y)], value); +}); -const addToCell = tgpu['~unstable'].fn([d.u32, d.u32, d.u32])((x, y, value) => { +const addToCell = tgpu.fn([d.u32, d.u32, d.u32])((x, y, value) => { const cell = getCellNext(x, y); const waterLevel = cell & MAX_WATER_LEVEL.$; const newWaterLevel = std.min(waterLevel + value, MAX_WATER_LEVEL.$); std.atomicAdd(nextState.$[getIndex(x, y)], newWaterLevel - waterLevel); }); -const subtractFromCell = tgpu['~unstable'].fn([d.u32, d.u32, d.u32])( - (x, y, value) => { - const cell = getCellNext(x, y); - const waterLevel = cell & MAX_WATER_LEVEL.$; - const newWaterLevel = std.max(waterLevel - std.min(value, waterLevel), 0); - std.atomicSub(nextState.$[getIndex(x, y)], waterLevel - newWaterLevel); - }, -); +const subtractFromCell = tgpu.fn([d.u32, d.u32, d.u32])((x, y, value) => { + const cell = getCellNext(x, y); + const waterLevel = cell & MAX_WATER_LEVEL.$; + const newWaterLevel = std.max(waterLevel - std.min(value, waterLevel), 0); + std.atomicSub(nextState.$[getIndex(x, y)], waterLevel - newWaterLevel); +}); -const persistFlags = tgpu['~unstable'].fn([d.u32, d.u32])((x, y) => { +const persistFlags = tgpu.fn([d.u32, d.u32])((x, y) => { const cell = getCell(x, y); const waterLevel = cell & MAX_WATER_LEVEL.$; const flags = cell >> 24; updateCell(x, y, (flags << 24) | waterLevel); }); -const getStableStateBelow = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)( - (upper, lower) => { - const totalMass = upper + lower; - if (totalMass <= MAX_WATER_LEVEL_UNPRESSURIZED.$) { - return totalMass; - } - if (totalMass >= MAX_WATER_LEVEL_UNPRESSURIZED.$ * 2 && upper > lower) { - return totalMass / 2 + MAX_PRESSURE.$; - } - return MAX_WATER_LEVEL_UNPRESSURIZED.$; - }, -); +const getStableStateBelow = tgpu.fn([d.u32, d.u32], d.u32)((upper, lower) => { + const totalMass = upper + lower; + if (totalMass <= MAX_WATER_LEVEL_UNPRESSURIZED.$) { + return totalMass; + } + if (totalMass >= MAX_WATER_LEVEL_UNPRESSURIZED.$ * 2 && upper > lower) { + return totalMass / 2 + MAX_PRESSURE.$; + } + return MAX_WATER_LEVEL_UNPRESSURIZED.$; +}); -const isWall = tgpu['~unstable'].fn([d.u32, d.u32], d.bool)( - (x, y) => getCell(x, y) >> 24 === 1, +const isWall = tgpu.fn([d.u32, d.u32], d.bool)((x, y) => + getCell(x, y) >> 24 === 1 ); -const isWaterSource = tgpu['~unstable'].fn([d.u32, d.u32], d.bool)( - (x, y) => getCell(x, y) >> 24 === 2, +const isWaterSource = tgpu.fn([d.u32, d.u32], d.bool)((x, y) => + getCell(x, y) >> 24 === 2 ); -const isWaterDrain = tgpu['~unstable'].fn([d.u32, d.u32], d.bool)( - (x, y) => getCell(x, y) >> 24 === 3, +const isWaterDrain = tgpu.fn([d.u32, d.u32], d.bool)((x, y) => + getCell(x, y) >> 24 === 3 ); -const isClearCell = tgpu['~unstable'].fn([d.u32, d.u32], d.bool)( - (x, y) => getCell(x, y) >> 24 === 4, +const isClearCell = tgpu.fn([d.u32, d.u32], d.bool)((x, y) => + getCell(x, y) >> 24 === 4 ); -const getWaterLevel = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)( - (x, y) => getCell(x, y) & MAX_WATER_LEVEL.$, +const getWaterLevel = tgpu.fn([d.u32, d.u32], d.u32)((x, y) => + getCell(x, y) & MAX_WATER_LEVEL.$ ); -const checkForFlagsAndBounds = tgpu['~unstable'].fn([d.u32, d.u32], d.bool)( - (x, y) => { - if (isClearCell(x, y)) { - updateCell(x, y, 0); - return true; - } +const checkForFlagsAndBounds = tgpu.fn([d.u32, d.u32], d.bool)((x, y) => { + if (isClearCell(x, y)) { + updateCell(x, y, 0); + return true; + } - if (isWall(x, y)) { - persistFlags(x, y); - return true; - } + if (isWall(x, y)) { + persistFlags(x, y); + return true; + } - if (isWaterSource(x, y)) { - persistFlags(x, y); - addToCell(x, y, 20); - return false; - } + if (isWaterSource(x, y)) { + persistFlags(x, y); + addToCell(x, y, 20); + return false; + } - if (isWaterDrain(x, y)) { - persistFlags(x, y); - updateCell(x, y, 3 << 24); - return true; - } + if (isWaterDrain(x, y)) { + persistFlags(x, y); + updateCell(x, y, 3 << 24); + return true; + } - if ( - y === 0 || - y === size.$.y - 1 || - x === 0 || - x === size.$.x - 1 - ) { - subtractFromCell(x, y, getWaterLevel(x, y)); - return true; - } + if ( + y === 0 || + y === size.$.y - 1 || + x === 0 || + x === size.$.x - 1 + ) { + subtractFromCell(x, y, getWaterLevel(x, y)); + return true; + } - return false; - }, -); + return false; +}); -const decideWaterLevel = tgpu['~unstable'].fn([d.u32, d.u32])((x, y) => { +const decideWaterLevel = tgpu.fn([d.u32, d.u32])((x, y) => { if (checkForFlagsAndBounds(x, y)) { return; } diff --git a/apps/typegpu-docs/src/content/examples/simulation/gravity/compute.ts b/apps/typegpu-docs/src/content/examples/simulation/gravity/compute.ts index a3ae5c2b2..54042282d 100644 --- a/apps/typegpu-docs/src/content/examples/simulation/gravity/compute.ts +++ b/apps/typegpu-docs/src/content/examples/simulation/gravity/compute.ts @@ -8,23 +8,21 @@ import { CelestialBody, computeLayout, timeAccess } from './schemas.ts'; const { none, bounce, merge } = collisionBehaviors; // tiebreaker function for merges and bounces -const isSmaller = tgpu['~unstable'].fn([d.u32, d.u32], d.bool)( - (currentId, otherId) => { - if ( - computeLayout.$.inState[currentId].mass < - computeLayout.$.inState[otherId].mass - ) { - return true; - } - if ( - computeLayout.$.inState[currentId].mass === - computeLayout.$.inState[otherId].mass - ) { - return currentId < otherId; - } - return false; - }, -); +const isSmaller = tgpu.fn([d.u32, d.u32], d.bool)((currentId, otherId) => { + if ( + computeLayout.$.inState[currentId].mass < + computeLayout.$.inState[otherId].mass + ) { + return true; + } + if ( + computeLayout.$.inState[currentId].mass === + computeLayout.$.inState[otherId].mass + ) { + return currentId < otherId; + } + return false; +}); export const computeCollisionsShader = tgpu['~unstable'].computeFn({ in: { gid: d.builtin.globalInvocationId }, diff --git a/apps/typegpu-docs/src/content/examples/simulation/gravity/helpers.ts b/apps/typegpu-docs/src/content/examples/simulation/gravity/helpers.ts index f9e88273c..e6528ab72 100644 --- a/apps/typegpu-docs/src/content/examples/simulation/gravity/helpers.ts +++ b/apps/typegpu-docs/src/content/examples/simulation/gravity/helpers.ts @@ -163,6 +163,6 @@ export async function loadSphereTextures(root: TgpuRoot) { return texture; } -export const radiusOf = tgpu['~unstable'].fn([CelestialBody], d.f32)((body) => { - return std.pow((body.mass * 0.75) / Math.PI, 0.333) * body.radiusMultiplier; -}); +export const radiusOf = tgpu.fn([CelestialBody], d.f32)((body) => + std.pow((body.mass * 0.75) / Math.PI, 0.333) * body.radiusMultiplier +); diff --git a/apps/typegpu-docs/src/content/examples/simulation/stable-fluid/simulation.ts b/apps/typegpu-docs/src/content/examples/simulation/stable-fluid/simulation.ts index afbe8fa61..2bcf0ee7a 100644 --- a/apps/typegpu-docs/src/content/examples/simulation/stable-fluid/simulation.ts +++ b/apps/typegpu-docs/src/content/examples/simulation/stable-fluid/simulation.ts @@ -3,25 +3,24 @@ import * as d from 'typegpu/data'; import * as std from 'typegpu/std'; import * as p from './params.ts'; -const getNeighbors = tgpu['~unstable'].fn( - [d.vec2i, d.vec2i], - d.arrayOf(d.vec2i, 4), -)((coords, bounds) => { - const adjacentOffsets = [ - d.vec2i(-1, 0), - d.vec2i(0, -1), - d.vec2i(1, 0), - d.vec2i(0, 1), - ]; - for (let i = 0; i < 4; i++) { - adjacentOffsets[i] = std.clamp( - std.add(coords, adjacentOffsets[i]), - d.vec2i(), - std.sub(bounds, d.vec2i(1)), - ); - } - return adjacentOffsets; -}); +const getNeighbors = tgpu.fn([d.vec2i, d.vec2i], d.arrayOf(d.vec2i, 4))( + (coords, bounds) => { + const adjacentOffsets = [ + d.vec2i(-1, 0), + d.vec2i(0, -1), + d.vec2i(1, 0), + d.vec2i(0, 1), + ]; + for (let i = 0; i < 4; i++) { + adjacentOffsets[i] = std.clamp( + std.add(coords, adjacentOffsets[i]), + d.vec2i(), + std.sub(bounds, d.vec2i(1)), + ); + } + return adjacentOffsets; + }, +); export const brushLayout = tgpu.bindGroupLayout({ brushParams: { uniform: p.BrushParams }, diff --git a/apps/typegpu-docs/src/content/examples/tests/tgsl-parsing-test/logical-expressions.ts b/apps/typegpu-docs/src/content/examples/tests/tgsl-parsing-test/logical-expressions.ts index 502242198..2e5aa04a2 100644 --- a/apps/typegpu-docs/src/content/examples/tests/tgsl-parsing-test/logical-expressions.ts +++ b/apps/typegpu-docs/src/content/examples/tests/tgsl-parsing-test/logical-expressions.ts @@ -9,11 +9,11 @@ const Schema = d.struct({ bool: d.bool, }); -const negate = tgpu['~unstable'].fn([d.vec3b], d.vec3b)((input) => { - return d.vec3b(!input.x, !input.y, !input.z); -}); +const negate = tgpu.fn([d.vec3b], d.vec3b)((input) => + d.vec3b(!input.x, !input.y, !input.z) +); -const negateStruct = tgpu['~unstable'].fn([Schema], Schema)((input) => { +const negateStruct = tgpu.fn([Schema], Schema)((input) => { const result = Schema({ vec2b: std.not(input.vec2b), vec4b: std.not(input.vec4b), @@ -24,111 +24,110 @@ const negateStruct = tgpu['~unstable'].fn([Schema], Schema)((input) => { }); // TODO: replace `s = s &&` with `s &&=` when implemented -export const logicalExpressionTests = tgpu['~unstable'] - .fn([], d.bool)(() => { - let s = true; - - s = s && std.eq(d.vec2i(1, 3), d.vec2i(1, 3)).x === true; - s = s && std.eq(d.vec2i(1, 3), d.vec2i(1, 3)).y === true; - s = s && std.eq(d.vec2i(1, 3), d.vec2i(1, 2)).x === true; - s = s && std.eq(d.vec2i(1, 3), d.vec2i(1, 2)).y === false; - - s = s && std.all(d.vec4b(true, true, true, true)); - s = s && !std.all(d.vec4b(true, false, true, true)); - - s = s && std.any(d.vec4b(false, false, true, false)); - s = s && !std.any(d.vec4b(false, false, false, false)); - - s = s && std.allEq(d.vec2i(1, 3), d.vec2i(1, 3)); - s = s && !std.allEq(d.vec2i(1, 3), d.vec2i(1, 2)); - - s = s && - std.allEq( - std.ne(d.vec3i(1, 2, 3), d.vec3i(1, 2, 4)), - d.vec3b(false, false, true), - ); - - s = s && - std.allEq( - std.lt(d.vec3f(1.0, -1.0, 0.0), d.vec3f(1.0, 1.0, -1.0)), - d.vec3b(false, true, false), - ); - - s = s && - std.allEq( - std.le(d.vec3f(1.0, -1.0, 0.0), d.vec3f(1.0, 1.0, -1.0)), - d.vec3b(true, true, false), - ); - - s = s && - std.allEq( - std.gt(d.vec3f(1.0, -1.0, 0.0), d.vec3f(1.0, 1.0, -1.0)), - d.vec3b(false, false, true), - ); - - s = s && - std.allEq( - std.ge(d.vec3f(1.0, -1.0, 0.0), d.vec3f(1.0, 1.0, -1.0)), - d.vec3b(true, false, true), - ); - - s = s && std.allEq(std.not(d.vec2b(false, true)), d.vec2b(true, false)); - - s = s && - std.allEq( - std.or( - d.vec4b(true, true, false, false), - d.vec4b(true, false, true, false), - ), - d.vec4b(true, true, true, false), - ); - - s = s && - std.allEq( - std.and( - d.vec4b(true, true, false, false), - d.vec4b(true, false, true, false), - ), - d.vec4b(true, false, false, false), - ); - - s = s && std.isCloseTo(d.vec3f(1.0, 1.0, 1.0), d.vec3f(0.999, 1.0, 1.001)); - s = s && !std.isCloseTo(d.vec3f(1.0, 1.0, 1.0), d.vec3f(0.9, 1.0, 1.1)); - s = s && std.isCloseTo(d.vec3f(1.0, 1.0, 1.0), d.vec3f(0.9, 1.0, 1.1), 0.2); - s = s && - !std.isCloseTo(d.vec3f(1.0, 1.0, 1.0), d.vec3f(0.7, 1.0, 1.3), 0.2); - - s = s && - std.allEq( - std.select(d.vec2i(-1, -2), d.vec2i(1, 2), true), - d.vec2i(1, 2), - ); - - s = s && - std.allEq( - std.select( - d.vec4i(-1, -2, -3, -4), - d.vec4i(1, 2, 3, 4), - d.vec4b(true, true, false, false), - ), - d.vec4i(1, 2, -3, -4), - ); - - const vec = d.vec3b(true, false, true); - s = s && std.allEq(std.not(vec), negate(vec)); - - const inputStruct = Schema({ - vec2b: d.vec2b(false, true), - vec4b: d.vec4b(false, true, false, true), - vec3b: d.vec3b(true, true, false), - bool: true, - }); - - const resultStruct = negateStruct(inputStruct); - s = s && std.allEq(std.not(inputStruct.vec2b), resultStruct.vec2b); - s = s && std.allEq(std.not(inputStruct.vec4b), resultStruct.vec4b); - s = s && std.allEq(std.not(inputStruct.vec3b), resultStruct.vec3b); - s = s && !inputStruct.bool === resultStruct.bool; - - return s; +export const logicalExpressionTests = tgpu.fn([], d.bool)(() => { + let s = true; + + s = s && std.eq(d.vec2i(1, 3), d.vec2i(1, 3)).x === true; + s = s && std.eq(d.vec2i(1, 3), d.vec2i(1, 3)).y === true; + s = s && std.eq(d.vec2i(1, 3), d.vec2i(1, 2)).x === true; + s = s && std.eq(d.vec2i(1, 3), d.vec2i(1, 2)).y === false; + + s = s && std.all(d.vec4b(true, true, true, true)); + s = s && !std.all(d.vec4b(true, false, true, true)); + + s = s && std.any(d.vec4b(false, false, true, false)); + s = s && !std.any(d.vec4b(false, false, false, false)); + + s = s && std.allEq(d.vec2i(1, 3), d.vec2i(1, 3)); + s = s && !std.allEq(d.vec2i(1, 3), d.vec2i(1, 2)); + + s = s && + std.allEq( + std.ne(d.vec3i(1, 2, 3), d.vec3i(1, 2, 4)), + d.vec3b(false, false, true), + ); + + s = s && + std.allEq( + std.lt(d.vec3f(1.0, -1.0, 0.0), d.vec3f(1.0, 1.0, -1.0)), + d.vec3b(false, true, false), + ); + + s = s && + std.allEq( + std.le(d.vec3f(1.0, -1.0, 0.0), d.vec3f(1.0, 1.0, -1.0)), + d.vec3b(true, true, false), + ); + + s = s && + std.allEq( + std.gt(d.vec3f(1.0, -1.0, 0.0), d.vec3f(1.0, 1.0, -1.0)), + d.vec3b(false, false, true), + ); + + s = s && + std.allEq( + std.ge(d.vec3f(1.0, -1.0, 0.0), d.vec3f(1.0, 1.0, -1.0)), + d.vec3b(true, false, true), + ); + + s = s && std.allEq(std.not(d.vec2b(false, true)), d.vec2b(true, false)); + + s = s && + std.allEq( + std.or( + d.vec4b(true, true, false, false), + d.vec4b(true, false, true, false), + ), + d.vec4b(true, true, true, false), + ); + + s = s && + std.allEq( + std.and( + d.vec4b(true, true, false, false), + d.vec4b(true, false, true, false), + ), + d.vec4b(true, false, false, false), + ); + + s = s && std.isCloseTo(d.vec3f(1.0, 1.0, 1.0), d.vec3f(0.999, 1.0, 1.001)); + s = s && !std.isCloseTo(d.vec3f(1.0, 1.0, 1.0), d.vec3f(0.9, 1.0, 1.1)); + s = s && std.isCloseTo(d.vec3f(1.0, 1.0, 1.0), d.vec3f(0.9, 1.0, 1.1), 0.2); + s = s && + !std.isCloseTo(d.vec3f(1.0, 1.0, 1.0), d.vec3f(0.7, 1.0, 1.3), 0.2); + + s = s && + std.allEq( + std.select(d.vec2i(-1, -2), d.vec2i(1, 2), true), + d.vec2i(1, 2), + ); + + s = s && + std.allEq( + std.select( + d.vec4i(-1, -2, -3, -4), + d.vec4i(1, 2, 3, 4), + d.vec4b(true, true, false, false), + ), + d.vec4i(1, 2, -3, -4), + ); + + const vec = d.vec3b(true, false, true); + s = s && std.allEq(std.not(vec), negate(vec)); + + const inputStruct = Schema({ + vec2b: d.vec2b(false, true), + vec4b: d.vec4b(false, true, false, true), + vec3b: d.vec3b(true, true, false), + bool: true, }); + + const resultStruct = negateStruct(inputStruct); + s = s && std.allEq(std.not(inputStruct.vec2b), resultStruct.vec2b); + s = s && std.allEq(std.not(inputStruct.vec4b), resultStruct.vec4b); + s = s && std.allEq(std.not(inputStruct.vec3b), resultStruct.vec3b); + s = s && !inputStruct.bool === resultStruct.bool; + + return s; +}); diff --git a/apps/typegpu-docs/src/content/examples/tests/tgsl-parsing-test/matrix-ops.ts b/apps/typegpu-docs/src/content/examples/tests/tgsl-parsing-test/matrix-ops.ts index bdd00613a..5694ae0b4 100644 --- a/apps/typegpu-docs/src/content/examples/tests/tgsl-parsing-test/matrix-ops.ts +++ b/apps/typegpu-docs/src/content/examples/tests/tgsl-parsing-test/matrix-ops.ts @@ -3,144 +3,128 @@ import * as d from 'typegpu/data'; import * as std from 'typegpu/std'; // TODO: replace `s = s &&` with `s &&=` when implemented -export const matrixOpsTests = tgpu['~unstable'] - .fn([], d.bool)(() => { - let s = true; - - s = s && - std.isCloseTo( - std.mul(d.mat4x4f.translation(d.vec3f(-1, 0, 1)), d.vec4f(1, 2, 3, 1)), - d.vec4f(0, 2, 4, 1), - ); - - s = s && - std.isCloseTo( - std.mul(d.mat4x4f.scaling(d.vec3f(-1, 0, 1)), d.vec4f(1, 2, 3, 1)), - d.vec4f(-1, 0, 3, 1), - ); - - s = s && - std.isCloseTo( - std.mul(d.mat4x4f.rotationX(Math.PI / 2), d.vec4f(1, 2, 3, 1)), - d.vec4f(1, -3, 2, 1), - ); - - s = s && - std.isCloseTo( - std.mul(d.mat4x4f.rotationY(Math.PI / 2), d.vec4f(1, 2, 3, 1)), - d.vec4f(3, 2, -1, 1), - ); - - s = s && - std.isCloseTo( - std.mul(d.mat4x4f.rotationZ(Math.PI / 2), d.vec4f(1, 2, 3, 1)), - d.vec4f(-2, 1, 3, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.translate4(d.mat4x4f.identity(), d.vec3f(-1, 0, 1)), - d.vec4f(1, 2, 3, 1), - ), - d.vec4f(0, 2, 4, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.scale4(d.mat4x4f.identity(), d.vec3f(-1, 0, 1)), - d.vec4f(1, 2, 3, 1), - ), - d.vec4f(-1, 0, 3, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.rotateX4(d.mat4x4f.identity(), Math.PI / 2), - d.vec4f(1, 2, 3, 1), - ), - d.vec4f(1, -3, 2, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.rotateY4(d.mat4x4f.identity(), Math.PI / 2), - d.vec4f(1, 2, 3, 1), - ), - d.vec4f(3, 2, -1, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.rotateZ4(d.mat4x4f.identity(), Math.PI / 2), - d.vec4f(1, 2, 3, 1), - ), - d.vec4f(-2, 1, 3, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.rotateZ4( - std.rotateX4(d.mat4x4f.identity(), Math.PI / 2), - Math.PI / 2, - ), - d.vec4f(1, 0, 0, 1), - ), - d.vec4f(0, 1, 0, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.rotateX4( - std.rotateZ4(d.mat4x4f.identity(), Math.PI / 2), - Math.PI / 2, - ), - d.vec4f(1, 0, 0, 1), - ), - d.vec4f(0, 0, 1, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.translate4( - std.scale4(d.mat4x4f.identity(), d.vec3f(2, 3, 4)), - d.vec3f(0, 1, 0), - ), - d.vec4f(1, 0, 0, 1), - ), - d.vec4f(2, 1, 0, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.scale4( - std.translate4(d.mat4x4f.identity(), d.vec3f(0, 1, 0)), - d.vec3f(2, 3, 4), - ), - d.vec4f(0, 0, 0, 1), - ), - d.vec4f(0, 3, 0, 1), - ); - - s = s && - std.isCloseTo( - std.mul( - std.rotateZ4( - std.rotateY4(d.mat4x4f.identity(), Math.PI / 2), - Math.PI / 2, - ), - d.vec4f(0, 1, 0, 1), - ), - d.vec4f(-1, 0, 0, 1), - ); - - return s; - }); +export const matrixOpsTests = tgpu.fn([], d.bool)(() => { + let s = true; + + s = s && std.isCloseTo( + std.mul(d.mat4x4f.translation(d.vec3f(-1, 0, 1)), d.vec4f(1, 2, 3, 1)), + d.vec4f(0, 2, 4, 1), + ); + + s = s && std.isCloseTo( + std.mul(d.mat4x4f.scaling(d.vec3f(-1, 0, 1)), d.vec4f(1, 2, 3, 1)), + d.vec4f(-1, 0, 3, 1), + ); + + s = s && std.isCloseTo( + std.mul(d.mat4x4f.rotationX(Math.PI / 2), d.vec4f(1, 2, 3, 1)), + d.vec4f(1, -3, 2, 1), + ); + + s = s && std.isCloseTo( + std.mul(d.mat4x4f.rotationY(Math.PI / 2), d.vec4f(1, 2, 3, 1)), + d.vec4f(3, 2, -1, 1), + ); + + s = s && std.isCloseTo( + std.mul(d.mat4x4f.rotationZ(Math.PI / 2), d.vec4f(1, 2, 3, 1)), + d.vec4f(-2, 1, 3, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.translate4(d.mat4x4f.identity(), d.vec3f(-1, 0, 1)), + d.vec4f(1, 2, 3, 1), + ), + d.vec4f(0, 2, 4, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.scale4(d.mat4x4f.identity(), d.vec3f(-1, 0, 1)), + d.vec4f(1, 2, 3, 1), + ), + d.vec4f(-1, 0, 3, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.rotateX4(d.mat4x4f.identity(), Math.PI / 2), + d.vec4f(1, 2, 3, 1), + ), + d.vec4f(1, -3, 2, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.rotateY4(d.mat4x4f.identity(), Math.PI / 2), + d.vec4f(1, 2, 3, 1), + ), + d.vec4f(3, 2, -1, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.rotateZ4(d.mat4x4f.identity(), Math.PI / 2), + d.vec4f(1, 2, 3, 1), + ), + d.vec4f(-2, 1, 3, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.rotateZ4( + std.rotateX4(d.mat4x4f.identity(), Math.PI / 2), + Math.PI / 2, + ), + d.vec4f(1, 0, 0, 1), + ), + d.vec4f(0, 1, 0, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.rotateX4( + std.rotateZ4(d.mat4x4f.identity(), Math.PI / 2), + Math.PI / 2, + ), + d.vec4f(1, 0, 0, 1), + ), + d.vec4f(0, 0, 1, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.translate4( + std.scale4(d.mat4x4f.identity(), d.vec3f(2, 3, 4)), + d.vec3f(0, 1, 0), + ), + d.vec4f(1, 0, 0, 1), + ), + d.vec4f(2, 1, 0, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.scale4( + std.translate4(d.mat4x4f.identity(), d.vec3f(0, 1, 0)), + d.vec3f(2, 3, 4), + ), + d.vec4f(0, 0, 0, 1), + ), + d.vec4f(0, 3, 0, 1), + ); + + s = s && std.isCloseTo( + std.mul( + std.rotateZ4( + std.rotateY4(d.mat4x4f.identity(), Math.PI / 2), + Math.PI / 2, + ), + d.vec4f(0, 1, 0, 1), + ), + d.vec4f(-1, 0, 0, 1), + ); + + return s; +}); diff --git a/apps/typegpu-docs/src/content/examples/tests/wgsl-resolution/index.ts b/apps/typegpu-docs/src/content/examples/tests/wgsl-resolution/index.ts index 330307122..c28ad85da 100644 --- a/apps/typegpu-docs/src/content/examples/tests/wgsl-resolution/index.ts +++ b/apps/typegpu-docs/src/content/examples/tests/wgsl-resolution/index.ts @@ -46,13 +46,13 @@ const paramsBuffer = root const params = paramsBuffer.as('uniform'); // helper functions -const rotate = tgpu['~unstable'].fn([d.vec2f, d.f32], d.vec2f)((v, angle) => { +const rotate = tgpu.fn([d.vec2f, d.f32], d.vec2f)((v, angle) => { const cos = std.cos(angle); const sin = std.sin(angle); return d.vec2f(v.x * cos - v.y * sin, v.x * sin + v.y * cos); }).$name('rotate util'); -const getRotationFromVelocity = tgpu['~unstable'].fn([d.vec2f], d.f32)( +const getRotationFromVelocity = tgpu.fn([d.vec2f], d.f32)( (velocity) => -std.atan2(velocity.x, velocity.y), ).$name('get rotation from velocity util'); diff --git a/packages/tgpu-gen/gen.mjs b/packages/tgpu-gen/gen.mjs index fb99648d1..694928e0a 100644 --- a/packages/tgpu-gen/gen.mjs +++ b/packages/tgpu-gen/gen.mjs @@ -23,7 +23,6 @@ const LENGTH_VAR = 'arrayLength'; * @prop {boolean} toTs * @prop {'commonjs' | 'esmodule'} moduleSyntax * @prop {'keep' | 'overwrite'} [existingFileStrategy] - * @prop {boolean} experimentalFunctions * @prop {Set} [declaredIdentifiers] * @prop {{tgpu?: boolean, data?: boolean }} [usedImports] */ @@ -109,7 +108,6 @@ export function generate( outputPath: '', toTs: true, moduleSyntax: 'esmodule', - experimentalFunctions: true, }, ) { const reflect = new WgslReflect(wgsl); @@ -121,9 +119,7 @@ export function generate( options, ); - const functions = options.experimentalFunctions - ? generateFunctions(reflect.functions, wgsl, options) - : null; + const functions = generateFunctions(reflect.functions, wgsl, options); const imports = generateImports(options); const exports_ = generateExports(options); @@ -534,9 +530,9 @@ function generateFunction(func, wgsl, options) { const body = implementation.match(/\(.*\).*{.*}/s); return body?.[0] - ? `tgpu['~unstable'].fn(${inputs}${ - output ? `, ${output}` : '' - })(/* wgsl */ \`${body[0]}\`)` + ? `tgpu.fn(${inputs}${output ? `, ${output}` : ''})(/* wgsl */ \`${ + body[0] + }\`)` : ''; } diff --git a/packages/tgpu-gen/tests/functions.test.ts b/packages/tgpu-gen/tests/functions.test.ts index eed57b228..3c09d12cc 100644 --- a/packages/tgpu-gen/tests/functions.test.ts +++ b/packages/tgpu-gen/tests/functions.test.ts @@ -13,7 +13,7 @@ fn rotate(v: vec2f, angle: f32) -> vec2f { }`; expect(generate(wgsl)).toContain(`\ -export const rotate = tgpu['~unstable'].fn([d.vec2f, d.f32], d.vec2f)(/* wgsl */ \`(v: vec2f, angle: f32) -> vec2f { +export const rotate = tgpu.fn([d.vec2f, d.f32], d.vec2f)(/* wgsl */ \`(v: vec2f, angle: f32) -> vec2f { let pos = vec2( (v.x * cos(angle)) - (v.y * sin(angle)), (v.x * sin(angle)) + (v.y * cos(angle)), @@ -29,7 +29,7 @@ fn foo() { }`; expect(generate(wgsl)).toContain(`\ -export const foo = tgpu['~unstable'].fn([])(/* wgsl */ \`() { +export const foo = tgpu.fn([])(/* wgsl */ \`() { let x = vec3f(); }\`);`); }); diff --git a/packages/tgpu-gen/tgpu-gen.mjs b/packages/tgpu-gen/tgpu-gen.mjs index f296372aa..e21d5c523 100755 --- a/packages/tgpu-gen/tgpu-gen.mjs +++ b/packages/tgpu-gen/tgpu-gen.mjs @@ -21,7 +21,6 @@ const args = arg({ '--overwrite': Boolean, '--keep': Boolean, '--watch': Boolean, - '--experimental-functions': Boolean, '-v': '--version', '-h': '--help', @@ -57,7 +56,6 @@ Options: --overwrite Overwrite existing files. --keep Keep existing files. - --experimental-functions Generate tgpu.fn (non-entry) function definitions. `); const execute = async () => { @@ -195,7 +193,6 @@ const execute = async () => { outputPath, toTs, moduleSyntax, - experimentalFunctions: args['--experimental-functions'] ?? false, }).catch((error) => { error.file = file; throw error; diff --git a/packages/typegpu-color/src/hsv.ts b/packages/typegpu-color/src/hsv.ts index 67f824703..f5e975a80 100644 --- a/packages/typegpu-color/src/hsv.ts +++ b/packages/typegpu-color/src/hsv.ts @@ -2,87 +2,83 @@ import tgpu from 'typegpu'; import { f32, vec3f } from 'typegpu/data'; import { floor, max, min } from 'typegpu/std'; -export const hsvToRgb = tgpu['~unstable'] - .fn([vec3f], vec3f)((hsv) => { - const h = hsv.x; - const s = hsv.y; - const v = hsv.z; +export const hsvToRgb = tgpu.fn([vec3f], vec3f)((hsv) => { + const h = hsv.x; + const s = hsv.y; + const v = hsv.z; - const i = floor(h * 6); - const f = h * 6 - i; - const p = v * (1 - s); - const q = v * (1 - f * s); - const t = v * (1 - (1 - f) * s); + const i = floor(h * 6); + const f = h * 6 - i; + const p = v * (1 - s); + const q = v * (1 - f * s); + const t = v * (1 - (1 - f) * s); - let r = f32(0); - let g = f32(0); - let b = f32(0); - if (i % 6 === 0) { - r = v; - g = t; - b = p; - } else if (i % 6 === 1) { - r = q; - g = v; - b = p; - } else if (i % 6 === 2) { - r = p; - g = v; - b = t; - } else if (i % 6 === 3) { - r = p; - g = q; - b = v; - } else if (i % 6 === 4) { - r = t; - g = p; - b = v; - } else { - r = v; - g = p; - b = q; - } - return vec3f(r, g, b); - }) - .$name('hsv to rgb'); + let r = f32(0); + let g = f32(0); + let b = f32(0); + if (i % 6 === 0) { + r = v; + g = t; + b = p; + } else if (i % 6 === 1) { + r = q; + g = v; + b = p; + } else if (i % 6 === 2) { + r = p; + g = v; + b = t; + } else if (i % 6 === 3) { + r = p; + g = q; + b = v; + } else if (i % 6 === 4) { + r = t; + g = p; + b = v; + } else { + r = v; + g = p; + b = q; + } + return vec3f(r, g, b); +}); -export const rgbToHsv = tgpu['~unstable'] - .fn([vec3f], vec3f)((rgb) => { - const r = rgb.x; - const g = rgb.y; - const b = rgb.z; +export const rgbToHsv = tgpu.fn([vec3f], vec3f)((rgb) => { + const r = rgb.x; + const g = rgb.y; + const b = rgb.z; - const maxC = max(r, max(g, b)); - const minC = min(r, min(g, b)); - const delta = f32(maxC - minC); - let h = f32(0); - let s = f32(0); - if (maxC === 0) { - s = 0; - } else { - s = delta / maxC; - } - const v = maxC; + const maxC = max(r, max(g, b)); + const minC = min(r, min(g, b)); + const delta = f32(maxC - minC); + let h = f32(0); + let s = f32(0); + if (maxC === 0) { + s = 0; + } else { + s = delta / maxC; + } + const v = maxC; - if (maxC === minC) { - h = 0; - } else if (maxC === r) { - let cond = f32(0); - if (g < b) { - cond = 6; - } else { - cond = 0; - } - h = g - b + delta * cond; - h /= 6 * delta; - } else if (maxC === g) { - h = b - r + delta * 2; - h /= 6 * delta; - } else if (maxC === b) { - h = r - g + delta * 4; - h /= 6 * delta; + if (maxC === minC) { + h = 0; + } else if (maxC === r) { + let cond = f32(0); + if (g < b) { + cond = 6; + } else { + cond = 0; } + h = g - b + delta * cond; + h /= 6 * delta; + } else if (maxC === g) { + h = b - r + delta * 2; + h /= 6 * delta; + } else if (maxC === b) { + h = r - g + delta * 4; + h /= 6 * delta; + } - return vec3f(h, s, v); - }) - .$name('rgb to hsv'); + return vec3f(h, s, v); +}); diff --git a/packages/typegpu-color/src/oklab.ts b/packages/typegpu-color/src/oklab.ts index ab236f134..22ff7789a 100644 --- a/packages/typegpu-color/src/oklab.ts +++ b/packages/typegpu-color/src/oklab.ts @@ -14,12 +14,12 @@ import { } from 'typegpu/std'; import { linearToSrgb, srgbToLinear } from './srgb.ts'; -const cbrt = tgpu['~unstable'].fn([f32], f32)((x) => { +const cbrt = tgpu.fn([f32], f32)((x) => { 'kernel & js'; return sign(x) * pow(abs(x), f32(1) / 3); }); -export const linearRgbToOklab = tgpu['~unstable'].fn([vec3f], vec3f)((rgb) => { +export const linearRgbToOklab = tgpu.fn([vec3f], vec3f)((rgb) => { 'kernel & js'; const l = 0.4122214708 * rgb.x + 0.5363325363 * rgb.y + 0.0514459929 * rgb.z; const m = 0.2119034982 * rgb.x + 0.6806995451 * rgb.y + 0.1073969566 * rgb.z; @@ -36,7 +36,7 @@ export const linearRgbToOklab = tgpu['~unstable'].fn([vec3f], vec3f)((rgb) => { ); }); -export const oklabToLinearRgb = tgpu['~unstable'].fn([vec3f], vec3f)((lab) => { +export const oklabToLinearRgb = tgpu.fn([vec3f], vec3f)((lab) => { 'kernel & js'; const l_ = lab.x + 0.3963377774 * lab.y + 0.2158037573 * lab.z; const m_ = lab.x - 0.1055613458 * lab.y - 0.0638541728 * lab.z; @@ -59,7 +59,7 @@ export const oklabToLinearRgb = tgpu['~unstable'].fn([vec3f], vec3f)((lab) => { * a and b must be normalized so a^2 + b^2 == 1 */ -const computeMaxSaturation = tgpu['~unstable'].fn([f32, f32], f32)((a, b) => { +const computeMaxSaturation = tgpu.fn([f32, f32], f32)((a, b) => { 'kernel & js'; // Max saturation will be when one of r, g or b goes below zero. @@ -151,7 +151,7 @@ const LC = struct({ * Finds L_cusp and C_cusp for a given hue * a and b must be normalized so a^2 + b^2 == 1 */ -const findCusp = tgpu['~unstable'].fn([f32, f32], LC)((a, b) => { +const findCusp = tgpu.fn([f32, f32], LC)((a, b) => { 'kernel & js'; // First, find the maximum saturation (saturation S = C/L) const S_cusp = computeMaxSaturation(a, b); @@ -171,7 +171,7 @@ const findCusp = tgpu['~unstable'].fn([f32, f32], LC)((a, b) => { * a and b must be normalized so a^2 + b^2 == 1 */ -const findGamutIntersection = tgpu['~unstable'].fn( +const findGamutIntersection = tgpu.fn( [f32, f32, f32, f32, f32, LC], f32, )((a, b, L1, C1, L0, cusp) => { @@ -261,7 +261,7 @@ const findGamutIntersection = tgpu['~unstable'].fn( return t; }); -const gamutClipPreserveChroma = tgpu['~unstable'].fn([vec3f], vec3f)((lab) => { +const gamutClipPreserveChroma = tgpu.fn([vec3f], vec3f)((lab) => { 'kernel & js'; const L = lab.x; const eps = 0.00001; @@ -279,7 +279,7 @@ const gamutClipPreserveChroma = tgpu['~unstable'].fn([vec3f], vec3f)((lab) => { export const oklabGamutClipAlphaAccess = tgpu['~unstable'].accessor(f32, 0.2); -const gamutClipAdaptiveL05 = tgpu['~unstable'].fn([vec3f], vec3f)((lab) => { +const gamutClipAdaptiveL05 = tgpu.fn([vec3f], vec3f)((lab) => { 'kernel & js'; const alpha = oklabGamutClipAlphaAccess.value; const L = lab.x; @@ -300,7 +300,7 @@ const gamutClipAdaptiveL05 = tgpu['~unstable'].fn([vec3f], vec3f)((lab) => { return vec3f(L_clipped, C_clipped * a_, C_clipped * b_); }); -const gamutClipAdaptiveL0cusp = tgpu['~unstable'].fn([vec3f], vec3f)((lab) => { +const gamutClipAdaptiveL0cusp = tgpu.fn([vec3f], vec3f)((lab) => { 'kernel & js'; const alpha = oklabGamutClipAlphaAccess.value; const L = lab.x; @@ -331,12 +331,12 @@ export const oklabGamutClip = { adaptiveL0Cusp: gamutClipAdaptiveL0cusp, }; -export const oklabToRgb = tgpu['~unstable'].fn([vec3f], vec3f)((lab) => { +export const oklabToRgb = tgpu.fn([vec3f], vec3f)((lab) => { 'kernel & js'; return linearToSrgb(oklabToLinearRgb(oklabGamutClipSlot.value(lab))); }); -export const rgbToOklab = tgpu['~unstable'].fn([vec3f], vec3f)((rgb) => { +export const rgbToOklab = tgpu.fn([vec3f], vec3f)((rgb) => { 'kernel & js'; return linearRgbToOklab(srgbToLinear(rgb)); }); diff --git a/packages/typegpu-color/src/srgb.ts b/packages/typegpu-color/src/srgb.ts index 9bbbcccb6..b06bfab3e 100644 --- a/packages/typegpu-color/src/srgb.ts +++ b/packages/typegpu-color/src/srgb.ts @@ -2,7 +2,7 @@ import tgpu from 'typegpu'; import { vec3f } from 'typegpu/data'; import { add, gt, mul, pow, select, sub } from 'typegpu/std'; -export const linearToSrgb = tgpu['~unstable'].fn([vec3f], vec3f)((linear) => { +export const linearToSrgb = tgpu.fn([vec3f], vec3f)((linear) => { 'kernel & js'; return select( mul(12.92, linear), @@ -11,7 +11,7 @@ export const linearToSrgb = tgpu['~unstable'].fn([vec3f], vec3f)((linear) => { ); }); -export const srgbToLinear = tgpu['~unstable'].fn([vec3f], vec3f)((rgb) => { +export const srgbToLinear = tgpu.fn([vec3f], vec3f)((rgb) => { 'kernel & js'; return select( mul(1.0 / 12.92, rgb), diff --git a/packages/typegpu-color/src/ycbcr.ts b/packages/typegpu-color/src/ycbcr.ts index 49fda0194..1d8c4562c 100644 --- a/packages/typegpu-color/src/ycbcr.ts +++ b/packages/typegpu-color/src/ycbcr.ts @@ -26,6 +26,6 @@ export const rgbToYcbcrMatrix = tgpu['~unstable'].const( ), ); -export const rgbToYcbcr = tgpu['~unstable'].fn([vec3f], vec3f)((rgb) => { - return mul(rgb, rgbToYcbcrMatrix.value); -}); +export const rgbToYcbcr = tgpu.fn([vec3f], vec3f)((rgb) => + mul(rgb, rgbToYcbcrMatrix.value) +); diff --git a/packages/typegpu-noise/src/generator.ts b/packages/typegpu-noise/src/generator.ts index 18e3d9f2c..05ed140c0 100644 --- a/packages/typegpu-noise/src/generator.ts +++ b/packages/typegpu-noise/src/generator.ts @@ -10,9 +10,7 @@ export interface StatefulGenerator { sample: TgpuFn<() => d.F32>; } -export const randomGeneratorShell: TgpuFnShell<[], d.F32> = tgpu[ - '~unstable' -].fn([], d.f32); +export const randomGeneratorShell: TgpuFnShell<[], d.F32> = tgpu.fn([], d.f32); /** * Incorporated from https://www.cg.tuwien.ac.at/research/publications/2023/PETER-2023-PSW/PETER-2023-PSW-.pdf @@ -22,19 +20,19 @@ export const BPETER: StatefulGenerator = (() => { const seed = tgpu['~unstable'].privateVar(d.vec2f); return { - seed: tgpu['~unstable'].fn([d.f32])((value) => { + seed: tgpu.fn([d.f32])((value) => { seed.value = d.vec2f(value, 0); }), - seed2: tgpu['~unstable'].fn([d.vec2f])((value) => { + seed2: tgpu.fn([d.vec2f])((value) => { seed.value = value; }), - seed3: tgpu['~unstable'].fn([d.vec3f])((value) => { + seed3: tgpu.fn([d.vec3f])((value) => { seed.value = add(value.xy, d.vec2f(value.z)); }), - seed4: tgpu['~unstable'].fn([d.vec4f])((value) => { + seed4: tgpu.fn([d.vec4f])((value) => { seed.value = add(value.xy, value.zw); }), diff --git a/packages/typegpu-noise/src/perlin-2d.ts b/packages/typegpu-noise/src/perlin-2d.ts index a01a271d0..77cd38585 100644 --- a/packages/typegpu-noise/src/perlin-2d.ts +++ b/packages/typegpu-noise/src/perlin-2d.ts @@ -4,26 +4,22 @@ import { add, dot, floor, mix, mul, sub } from 'typegpu/std'; import { randOnUnitCircle, randSeed2 } from './random.ts'; import { smootherStep } from './utils.ts'; -export const computeJunctionGradient = tgpu['~unstable'].fn([d.vec2i], d.vec2f)( - (pos) => { - randSeed2(mul(0.001, d.vec2f(pos))); - return randOnUnitCircle(); - }, -); +export const computeJunctionGradient = tgpu.fn([d.vec2i], d.vec2f)((pos) => { + randSeed2(mul(0.001, d.vec2f(pos))); + return randOnUnitCircle(); +}); export const getJunctionGradientSlot = tgpu['~unstable'].slot( computeJunctionGradient, ); -const dotProdGrid = tgpu['~unstable'].fn([d.vec2f, d.vec2i], d.f32)( - (pos, junction) => { - const relative = sub(pos, d.vec2f(junction)); - const gridVector = getJunctionGradientSlot.value(junction); - return dot(relative, gridVector); - }, -); +const dotProdGrid = tgpu.fn([d.vec2f, d.vec2i], d.f32)((pos, junction) => { + const relative = sub(pos, d.vec2f(junction)); + const gridVector = getJunctionGradientSlot.value(junction); + return dot(relative, gridVector); +}); -export const sample = tgpu['~unstable'].fn([d.vec2f], d.f32)((pos) => { +export const sample = tgpu.fn([d.vec2f], d.f32)((pos) => { const topLeftJunction = d.vec2i(floor(pos)); const topLeft = dotProdGrid(pos, topLeftJunction); diff --git a/packages/typegpu-noise/src/perlin-3d/algorithm.ts b/packages/typegpu-noise/src/perlin-3d/algorithm.ts index 9678e6656..f4766a07f 100644 --- a/packages/typegpu-noise/src/perlin-3d/algorithm.ts +++ b/packages/typegpu-noise/src/perlin-3d/algorithm.ts @@ -4,26 +4,22 @@ import { add, dot, floor, mix, mul, sub } from 'typegpu/std'; import { randOnUnitSphere, randSeed3 } from '../random.ts'; import { smootherStep } from '../utils.ts'; -export const computeJunctionGradient = tgpu['~unstable'].fn([d.vec3i], d.vec3f)( - (pos) => { - randSeed3(mul(0.001, d.vec3f(pos))); - return randOnUnitSphere(); - }, -); +export const computeJunctionGradient = tgpu.fn([d.vec3i], d.vec3f)((pos) => { + randSeed3(mul(0.001, d.vec3f(pos))); + return randOnUnitSphere(); +}); export const getJunctionGradientSlot = tgpu['~unstable'].slot( computeJunctionGradient, ); -const dotProdGrid = tgpu['~unstable'].fn([d.vec3f, d.vec3f], d.f32)( - (pos, junction) => { - const relative = sub(pos, junction); - const gridVector = getJunctionGradientSlot.value(d.vec3i(junction)); - return dot(relative, gridVector); - }, -); +const dotProdGrid = tgpu.fn([d.vec3f, d.vec3f], d.f32)((pos, junction) => { + const relative = sub(pos, junction); + const gridVector = getJunctionGradientSlot.value(d.vec3i(junction)); + return dot(relative, gridVector); +}); -export const sample = tgpu['~unstable'].fn([d.vec3f], d.f32)((pos) => { +export const sample = tgpu.fn([d.vec3f], d.f32)((pos) => { const minJunction = floor(pos); const xyz = dotProdGrid(pos, minJunction); diff --git a/packages/typegpu-noise/src/perlin-3d/dynamic-cache.ts b/packages/typegpu-noise/src/perlin-3d/dynamic-cache.ts index 940a000d0..b1b016a98 100644 --- a/packages/typegpu-noise/src/perlin-3d/dynamic-cache.ts +++ b/packages/typegpu-noise/src/perlin-3d/dynamic-cache.ts @@ -146,17 +146,15 @@ export function dynamicCacheConfig( }; }); - const getJunctionGradient = tgpu['~unstable'].fn([d.vec3i], d.vec3f)( - (pos) => { - const size = d.vec3i(cleanValuesSlot.value.size.xyz); - const x = (pos.x % size.x + size.x) % size.x; - const y = (pos.y % size.y + size.y) % size.y; - const z = (pos.z % size.z + size.z) % size.z; - - return cleanValuesSlot.value - .memory[x + y * size.x + z * size.x * size.y] as d.v3f; - }, - ); + const getJunctionGradient = tgpu.fn([d.vec3i], d.vec3f)((pos) => { + const size = d.vec3i(cleanValuesSlot.value.size.xyz); + const x = (pos.x % size.x + size.x) % size.x; + const y = (pos.y % size.y + size.y) % size.y; + const z = (pos.z % size.z + size.z) % size.z; + + return cleanValuesSlot.value + .memory[x + y * size.x + z * size.x * size.y] as d.v3f; + }); const computeLayout = tgpu.bindGroupLayout({ size: { uniform: d.vec4u }, diff --git a/packages/typegpu-noise/src/perlin-3d/static-cache.ts b/packages/typegpu-noise/src/perlin-3d/static-cache.ts index e3cf51cf8..216674abe 100644 --- a/packages/typegpu-noise/src/perlin-3d/static-cache.ts +++ b/packages/typegpu-noise/src/perlin-3d/static-cache.ts @@ -79,17 +79,15 @@ export function staticCache(options: { computePipeline.dispatchWorkgroups(size.x, size.y, size.z); - const getJunctionGradient = tgpu['~unstable'].fn([d.vec3i], d.vec3f)( - (pos) => { - const size_i = d.vec3i(size); - const x = (pos.x % size_i.x + size_i.x) % size_i.x; - const y = (pos.y % size_i.y + size_i.y) % size_i.y; - const z = (pos.z % size_i.z + size_i.z) % size_i.z; + const getJunctionGradient = tgpu.fn([d.vec3i], d.vec3f)((pos) => { + const size_i = d.vec3i(size); + const x = (pos.x % size_i.x + size_i.x) % size_i.x; + const y = (pos.y % size_i.y + size_i.y) % size_i.y; + const z = (pos.z % size_i.z + size_i.z) % size_i.z; - return memoryReadonly - .value[x + y * size_i.x + z * size_i.x * size_i.y] as d.v3f; - }, - ); + return memoryReadonly + .value[x + y * size_i.x + z * size_i.x * size_i.y] as d.v3f; + }); return { getJunctionGradient, diff --git a/packages/typegpu-noise/src/random.ts b/packages/typegpu-noise/src/random.ts index 1b6b47841..1564fe730 100644 --- a/packages/typegpu-noise/src/random.ts +++ b/packages/typegpu-noise/src/random.ts @@ -5,31 +5,30 @@ import { randomGeneratorSlot } from './generator.ts'; const TWO_PI = Math.PI * 2; -export const randSeed: TgpuFn<(seed: d.F32) => d.Void> = tgpu['~unstable'] +export const randSeed: TgpuFn<(seed: d.F32) => d.Void> = tgpu .fn([d.f32])((seed) => { randomGeneratorSlot.value.seed(seed); }); -export const randSeed2: TgpuFn<(seed: d.Vec2f) => d.Void> = tgpu['~unstable'] +export const randSeed2: TgpuFn<(seed: d.Vec2f) => d.Void> = tgpu .fn([d.vec2f])((seed) => { randomGeneratorSlot.value.seed2(seed); }); -export const randSeed3: TgpuFn<(seed: d.Vec3f) => d.Void> = tgpu['~unstable'] +export const randSeed3: TgpuFn<(seed: d.Vec3f) => d.Void> = tgpu .fn([d.vec3f])((seed) => { randomGeneratorSlot.value.seed3(seed); }); -export const randSeed4: TgpuFn<(seed: d.Vec4f) => d.Void> = tgpu['~unstable'] +export const randSeed4: TgpuFn<(seed: d.Vec4f) => d.Void> = tgpu .fn([d.vec4f])((seed) => { randomGeneratorSlot.value.seed4(seed); }); -export const randFloat01: TgpuFn<() => d.F32> = tgpu['~unstable'].fn([], d.f32)( - () => randomGeneratorSlot.value.sample(), -); +export const randFloat01: TgpuFn<() => d.F32> = tgpu + .fn([], d.f32)(() => randomGeneratorSlot.value.sample()); -export const randInUnitCube: TgpuFn<() => d.Vec3f> = tgpu['~unstable'] +export const randInUnitCube: TgpuFn<() => d.Vec3f> = tgpu .fn([], d.vec3f)(() => d.vec3f( randomGeneratorSlot.value.sample() * 2 - 1, @@ -38,7 +37,7 @@ export const randInUnitCube: TgpuFn<() => d.Vec3f> = tgpu['~unstable'] ) ); -export const randInUnitCircle: TgpuFn<() => d.Vec2f> = tgpu['~unstable'] +export const randInUnitCircle: TgpuFn<() => d.Vec2f> = tgpu .fn([], d.vec2f)(() => { const radius = sqrt(randomGeneratorSlot.value.sample()); const angle = randomGeneratorSlot.value.sample() * TWO_PI; @@ -46,13 +45,13 @@ export const randInUnitCircle: TgpuFn<() => d.Vec2f> = tgpu['~unstable'] return d.vec2f(cos(angle) * radius, sin(angle) * radius); }); -export const randOnUnitCircle: TgpuFn<() => d.Vec2f> = tgpu['~unstable'] +export const randOnUnitCircle: TgpuFn<() => d.Vec2f> = tgpu .fn([], d.vec2f)(() => { const angle = randomGeneratorSlot.value.sample() * TWO_PI; return d.vec2f(cos(angle), sin(angle)); }); -export const randOnUnitSphere: TgpuFn<() => d.Vec3f> = tgpu['~unstable'] +export const randOnUnitSphere: TgpuFn<() => d.Vec3f> = tgpu .fn([], d.vec3f)(() => { const z = 2 * randomGeneratorSlot.value.sample() - 1; const oneMinusZSq = sqrt(1 - z * z); @@ -63,11 +62,10 @@ export const randOnUnitSphere: TgpuFn<() => d.Vec3f> = tgpu['~unstable'] return d.vec3f(x, y, z); }); -export const randOnUnitHemisphere: TgpuFn<(normal: d.Vec3f) => d.Vec3f> = - tgpu['~unstable'] - .fn([d.vec3f], d.vec3f)((normal) => { - const value = randOnUnitSphere(); - const alignment = dot(normal, value); +export const randOnUnitHemisphere: TgpuFn<(normal: d.Vec3f) => d.Vec3f> = tgpu + .fn([d.vec3f], d.vec3f)((normal) => { + const value = randOnUnitSphere(); + const alignment = dot(normal, value); - return mul(sign(alignment), value); - }); + return mul(sign(alignment), value); + }); diff --git a/packages/typegpu-noise/src/utils.ts b/packages/typegpu-noise/src/utils.ts index 3f7af34ce..4f24b98ef 100644 --- a/packages/typegpu-noise/src/utils.ts +++ b/packages/typegpu-noise/src/utils.ts @@ -16,6 +16,6 @@ export type PrefixKeys = { * Works as a replacement for smoothstep, but with a continuous * second derivative, so lighting is continuous. */ -export const smootherStep = tgpu['~unstable'].fn([d.f32], d.f32)((x) => { - return 6 * pow(x, 5) - 15 * pow(x, 4) + 10 * pow(x, 3); -}); +export const smootherStep = tgpu.fn([d.f32], d.f32)((x) => + 6 * pow(x, 5) - 15 * pow(x, 4) + 10 * pow(x, 3) +); diff --git a/packages/typegpu/src/core/function/tgpuFn.ts b/packages/typegpu/src/core/function/tgpuFn.ts index 9b787cc4c..40cde8b45 100644 --- a/packages/typegpu/src/core/function/tgpuFn.ts +++ b/packages/typegpu/src/core/function/tgpuFn.ts @@ -72,19 +72,7 @@ export type TgpuFnShell< & (( strings: TemplateStringsArray, ...values: unknown[] - ) => TgpuFn<(...args: Args) => Return>) - & { - /** - * @deprecated Invoke the shell as a function instead. - */ - does: - & (( - implementation: ( - ...args: InferArgs - ) => Infer, - ) => TgpuFn<(...args: Args) => Return>) - & ((implementation: string) => TgpuFn<(...args: Args) => Return>); - }; + ) => TgpuFn<(...args: Args) => Return>); interface TgpuFnBase extends TgpuNamable { readonly [$internal]: { @@ -140,9 +128,7 @@ export function fn< stripTemplate(arg, ...values), ); - return Object.assign(Object.assign(call, shell), { - does: call, - }) as unknown as TgpuFnShell; + return Object.assign(call, shell) as unknown as TgpuFnShell; } export function isTgpuFn( diff --git a/packages/typegpu/src/index.ts b/packages/typegpu/src/index.ts index df97ed1ae..fd8e4b5da 100644 --- a/packages/typegpu/src/index.ts +++ b/packages/typegpu/src/index.ts @@ -20,6 +20,7 @@ import { vertexLayout } from './core/vertexLayout/vertexLayout.ts'; import { bindGroupLayout } from './tgpuBindGroupLayout.ts'; export const tgpu = { + fn, bindGroupLayout, vertexLayout, @@ -30,6 +31,9 @@ export const tgpu = { resolveWithContext, '~unstable': { + /** + * @deprecated This feature is now stable, use tgpu.fn. + */ fn, fragmentFn, vertexFn, diff --git a/packages/typegpu/tests/accessor.test.ts b/packages/typegpu/tests/accessor.test.ts index 1d6e36f0a..9a0373663 100644 --- a/packages/typegpu/tests/accessor.test.ts +++ b/packages/typegpu/tests/accessor.test.ts @@ -9,20 +9,13 @@ const RED_RESOLVED = 'vec3f(1, 0, 0)'; describe('tgpu.accessor', () => { it('resolves to invocation of provided function', () => { - const colorAccessor = tgpu['~unstable'].accessor(d.vec3f).$name('color'); + const colorAccess = tgpu['~unstable'].accessor(d.vec3f); - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { - return color; - }`) - .$name('getColor') - .$uses({ color: colorAccessor }) - .with( - colorAccessor, - tgpu['~unstable'] - .fn([], d.vec3f)(`() -> vec3f { return ${RED_RESOLVED}; }`) - .$name('red'), - ); + const red = tgpu.fn([], d.vec3f)(`() { return ${RED}; }`).$uses({ RED }); + + const getColor = tgpu.fn([], d.vec3f)`() { return colorAccess; }` + .$uses({ colorAccess }) + .with(colorAccess, red); expect(parseResolved({ getColor })).toBe( parse(/* wgsl */ ` @@ -38,46 +31,38 @@ describe('tgpu.accessor', () => { }); it('resolves to provided buffer usage', ({ root }) => { - const colorAccessor = tgpu['~unstable'].accessor(d.vec3f).$name('color'); + const colorAccess = tgpu['~unstable'].accessor(d.vec3f); - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { - return color; - }`) - .$name('getColor') - .$uses({ color: colorAccessor }) - .with( - colorAccessor, - root - .createBuffer(d.vec3f, RED) - .$usage('uniform') - .$name('red') - .as('uniform'), - ); + const redUniform = root + .createBuffer(d.vec3f, RED) + .$usage('uniform') + .as('uniform'); + + const getColor = tgpu.fn([], d.vec3f)`() { return colorAccess; }` + .$uses({ colorAccess }) + .with(colorAccess, redUniform); expect(parseResolved({ getColor })).toBe( parse(/* wgsl */ ` - @group(0) @binding(0) var red: vec3f; + @group(0) @binding(0) var redUniform: vec3f; fn getColor() -> vec3f { - return red; + return redUniform; } `), ); }); it('resolves to resolved form of provided JS value', () => { - const colorAccessor = tgpu['~unstable'].accessor(d.vec3f); - const multiplierAccessor = tgpu['~unstable'].accessor(d.f32); + const colorAccess = tgpu['~unstable'].accessor(d.vec3f); + const multiplierAccess = tgpu['~unstable'].accessor(d.f32); - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { - return color * multiplier; - }`) - .$name('getColor') - .$uses({ color: colorAccessor, multiplier: multiplierAccessor }) - .with(colorAccessor, RED) - .with(multiplierAccessor, 2); + const getColor = tgpu.fn([], d.vec3f)`() { + return colorAccess * multiplierAccess; + }` + .$uses({ colorAccess, multiplierAccess }) + .with(colorAccess, RED) + .with(multiplierAccess, 2); expect(parseResolved({ getColor })).toBe( parse(/* wgsl */ ` @@ -89,14 +74,10 @@ describe('tgpu.accessor', () => { }); it('resolves to default value if no value provided', () => { - const colorAccessor = tgpu['~unstable'].accessor(d.vec3f, RED); // red by default + const colorAccess = tgpu['~unstable'].accessor(d.vec3f, RED); // red by default - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { - return color; - }`) - .$name('getColor') - .$uses({ color: colorAccessor }); + const getColor = tgpu.fn([], d.vec3f)`() { return colorAccess; }` + .$uses({ colorAccess }); expect(parseResolved({ getColor })).toBe( parse(/* wgsl */ ` @@ -108,23 +89,17 @@ describe('tgpu.accessor', () => { }); it('resolves to provided value rather than default value', () => { - const colorAccessor = tgpu['~unstable'].accessor(d.vec3f, RED); // red by default + const colorAccess = tgpu['~unstable'].accessor(d.vec3f, RED); // red by default - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { - return color; - }`) - .$name('getColor') - .$uses({ color: colorAccessor }); + const getColor = tgpu.fn([], d.vec3f)`() { return colorAccess; }` + .$uses({ colorAccess }); // overriding to green - const getColorWithGreen = getColor.with(colorAccessor, d.vec3f(0, 1, 0)); + const getColorWithGreen = getColor.with(colorAccess, d.vec3f(0, 1, 0)); - const main = tgpu['~unstable'] - .fn([])(`() { + const main = tgpu.fn([])(`() { return getColorWithGreen(); }`) - .$name('main') .$uses({ getColorWithGreen }); expect(parseResolved({ main })).toBe( @@ -141,14 +116,10 @@ describe('tgpu.accessor', () => { }); it('throws error when no default nor value provided', () => { - const colorAccessor = tgpu['~unstable'].accessor(d.vec3f).$name('color'); + const colorAccess = tgpu['~unstable'].accessor(d.vec3f).$name('color'); - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(`() -> vec3f { - return color; - })`) - .$name('getColor') - .$uses({ color: colorAccessor }); + const getColor = tgpu.fn([], d.vec3f)`() { return colorAccess; }` + .$uses({ colorAccess }); expect(() => tgpu.resolve({ externals: { getColor }, names: 'strict' })) .toThrowErrorMatchingInlineSnapshot(` @@ -160,34 +131,26 @@ 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, - root - .createBuffer(d.vec3f, RED) - .$usage('uniform') - .$name('colorUniform') - .as('uniform'), - ); + const redUniform = root + .createBuffer(d.vec3f, RED) + .$usage('uniform') + .as('uniform'); - const colorAccessorFn = tgpu['~unstable'].accessor( - d.vec3f, - tgpu['~unstable'] - .fn([], d.vec3f)(() => RED) - .$name('getColor'), - ); + const colorValueAccess = tgpu['~unstable'].accessor(d.vec3f, RED); + const colorUsageAccess = tgpu['~unstable'].accessor(d.vec3f, redUniform); - const main = tgpu['~unstable'] - .fn([])(() => { - const color = colorAccessorValue.value; - const color2 = colorAccessorUsage.value; - const color3 = colorAccessorFn.value; + const getColor = tgpu.fn([], d.vec3f)(() => RED); + const colorAccessorFn = tgpu['~unstable'].accessor(d.vec3f, getColor); - const colorX = colorAccessorValue.value.x; - const color2X = colorAccessorUsage.value.x; - const color3X = colorAccessorFn.value.x; - }) - .$name('main'); + const main = tgpu.fn([])(() => { + const color = colorValueAccess.value; + const color2 = colorUsageAccess.value; + const color3 = colorAccessorFn.value; + + const colorX = colorValueAccess.value.x; + const color2X = colorUsageAccess.value.x; + const color3X = colorAccessorFn.value.x; + }); const resolved = tgpu.resolve({ externals: { main }, @@ -196,7 +159,7 @@ describe('tgpu.accessor', () => { expect(parse(resolved)).toBe( parse(/* wgsl */ ` - @group(0) @binding(0) var colorUniform: vec3f; + @group(0) @binding(0) var redUniform: vec3f; fn getColor() -> vec3f { return vec3f(1, 0, 0); @@ -204,11 +167,11 @@ describe('tgpu.accessor', () => { fn main() { var color = vec3f(1, 0, 0); - var color2 = colorUniform; + var color2 = redUniform; var color3 = getColor(); var colorX = vec3f(1, 0, 0).x; - var color2X = colorUniform.x; + var color2X = redUniform.x; var color3X = getColor().x; } `), diff --git a/packages/typegpu/tests/array.test.ts b/packages/typegpu/tests/array.test.ts index 249f654d6..0e961ad08 100644 --- a/packages/typegpu/tests/array.test.ts +++ b/packages/typegpu/tests/array.test.ts @@ -100,7 +100,7 @@ describe('array.length', () => { }, }); - const foo = tgpu['~unstable'].fn([])(() => { + const foo = tgpu.fn([])(() => { let acc = d.f32(1); for (let i = d.u32(0); i < layout.bound.values.value.length; i++) { layout.bound.values.value[i] = acc; @@ -131,7 +131,7 @@ describe('array.length', () => { }, }); - const foo = tgpu['~unstable'].fn([])(() => { + const foo = tgpu.fn([])(() => { let acc = d.f32(1); for (let i = 0; i < layout.bound.values.value.length; i++) { layout.bound.values.value[i] = acc; @@ -164,10 +164,7 @@ describe('array.length', () => { }, }); - const testFn = tgpu['~unstable'].fn( - [], - d.i32, - )(() => { + const testFn = tgpu.fn([], d.i32)(() => { return arrayLength(layout.$.values); }); @@ -191,10 +188,7 @@ describe('array.length', () => { }, }); - const testFn = tgpu['~unstable'].fn( - [], - d.u32, - )(() => { + const testFn = tgpu.fn([], d.u32)(() => { return arrayLength(layout.bound.values.value); }); diff --git a/packages/typegpu/tests/bufferUsage.test.ts b/packages/typegpu/tests/bufferUsage.test.ts index 003b5c7ed..0df1108a1 100644 --- a/packages/typegpu/tests/bufferUsage.test.ts +++ b/packages/typegpu/tests/bufferUsage.test.ts @@ -42,7 +42,7 @@ describe('TgpuBufferUniform', () => { const buffer = root.createBuffer(d.f32).$usage('uniform').$name('param'); const uniform = buffer.as('uniform'); - const func = tgpu['~unstable'].fn([])(() => { + const func = tgpu.fn([])(() => { const x = uniform.value; }); @@ -62,17 +62,15 @@ describe('TgpuBufferUniform', () => { }); 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 Boid = d.struct({ + pos: d.vec3f, + vel: d.vec3u, + }); const buffer = root.createBuffer(Boid).$usage('uniform').$name('boid'); const uniform = buffer.as('uniform'); - const func = tgpu['~unstable'].fn([])(() => { + const func = tgpu.fn([])(() => { const pos = uniform.value.pos; const velX = uniform.value.vel.x; }); @@ -134,7 +132,7 @@ describe('TgpuBufferMutable', () => { const buffer = root.createBuffer(d.f32).$usage('storage').$name('param'); const mutable = buffer.as('mutable'); - const func = tgpu['~unstable'].fn([])(() => { + const func = tgpu.fn([])(() => { const x = mutable.value; }); @@ -164,7 +162,7 @@ describe('TgpuBufferMutable', () => { const buffer = root.createBuffer(Boid).$usage('storage').$name('boid'); const mutable = buffer.as('mutable'); - const func = tgpu['~unstable'].fn([])(() => { + const func = tgpu.fn([])(() => { const pos = mutable.value.pos; const velX = mutable.value.vel.x; }); @@ -226,7 +224,7 @@ describe('TgpuBufferReadonly', () => { const buffer = root.createBuffer(d.f32).$usage('storage').$name('param'); const readonly = buffer.as('readonly'); - const func = tgpu['~unstable'].fn([])(() => { + const func = tgpu.fn([])(() => { const x = readonly.value; }); @@ -256,7 +254,7 @@ describe('TgpuBufferReadonly', () => { const buffer = root.createBuffer(Boid).$usage('storage').$name('boid'); const readonly = buffer.as('readonly'); - const func = tgpu['~unstable'].fn([])(() => { + const func = tgpu.fn([])(() => { const pos = readonly.value.pos; const velX = readonly.value.vel.x; }); diff --git a/packages/typegpu/tests/constant.test.ts b/packages/typegpu/tests/constant.test.ts index d6e0d321f..8653c831d 100644 --- a/packages/typegpu/tests/constant.test.ts +++ b/packages/typegpu/tests/constant.test.ts @@ -6,17 +6,12 @@ import { parse, parseResolved } from './utils/parseResolved.ts'; describe('tgpu.const', () => { it('should inject const declaration when used in functions', () => { const x = tgpu['~unstable'].const(d.u32, 2); - const fn1 = tgpu['~unstable'] - .fn([])(`() { - return x; - }`) - .$uses({ x }) - .$name('fn1'); + const fn1 = tgpu.fn([], d.u32)`() { return x; }`.$uses({ x }); expect(parseResolved({ fn1 })).toBe( parse(` const x: u32 = 2; - fn fn1() { + fn fn1() -> u32 { return x; } `), @@ -24,24 +19,20 @@ describe('tgpu.const', () => { }); it('allows accessing constants in tgsl through .value', () => { - const Boid = d - .struct({ - pos: d.vec3f, - vel: d.vec3u, - }) - .$name('Boid'); + const Boid = d.struct({ + pos: d.vec3f, + vel: d.vec3u, + }); - const boidConst = tgpu['~unstable'] - .const(Boid, { - pos: d.vec3f(1, 2, 3), - vel: d.vec3u(4, 5, 6), - }) - .$name('boid'); + const boid = tgpu['~unstable'].const(Boid, { + pos: d.vec3f(1, 2, 3), + vel: d.vec3u(4, 5, 6), + }); - const func = tgpu['~unstable'].fn([])(() => { - const pos = boidConst.value; - const vel = boidConst.value.vel; - const velX = boidConst.value.vel.x; + const func = tgpu.fn([])(() => { + const pos = boid.$; + const vel = boid.$.vel; + const velX = boid.$.vel.x; }); const resolved = tgpu.resolve({ diff --git a/packages/typegpu/tests/declare.test.ts b/packages/typegpu/tests/declare.test.ts index 1c295d0c3..c0023a7d5 100644 --- a/packages/typegpu/tests/declare.test.ts +++ b/packages/typegpu/tests/declare.test.ts @@ -9,14 +9,12 @@ describe('tgpu.declare', () => { '@group(0) @binding(0) var val: f32;', ); - const fn = tgpu['~unstable'] - .fn([])(`() { + const empty = tgpu.fn([])(`() { // do nothing }`) - .$uses({ declaration }) - .$name('empty'); + .$uses({ declaration }); - expect(parseResolved({ fn })).toBe( + expect(parseResolved({ empty })).toBe( parse(` @group(0) @binding(0) var val: f32; @@ -30,15 +28,14 @@ describe('tgpu.declare', () => { '@group(0) @binding(0) var val: f32;', ); - const fn = tgpu['~unstable'] + const empty = tgpu .fn([])(`() { declaration // do nothing }`) - .$uses({ declaration }) - .$name('empty'); + .$uses({ declaration }); - expect(parseResolved({ fn })).toBe( + expect(parseResolved({ empty })).toBe( parse(` @group(0) @binding(0) var val: f32; @@ -48,22 +45,20 @@ describe('tgpu.declare', () => { }); it('should inject all provided declarations', () => { - const fn = tgpu['~unstable'] - .fn([])(`() { + const decl1 = tgpu['~unstable'].declare( + '@group(0) @binding(0) var val: f32;', + ); + const decl2 = tgpu['~unstable'].declare(` + struct Output { + x: u32, + }`); + + const empty = tgpu.fn([])(`() { // do nothing }`) - .$uses({ - extraDeclaration1: tgpu['~unstable'].declare( - '@group(0) @binding(0) var val: f32;', - ), - extraDeclaration2: tgpu['~unstable'].declare(` - struct Output { - x: u32, - }`), - }) - .$name('empty'); - - expect(parseResolved({ fn })).toBe( + .$uses({ decl1, decl2 }); + + expect(parseResolved({ empty })).toBe( parse(` @group(0) @binding(0) var val: f32; @@ -80,21 +75,19 @@ describe('tgpu.declare', () => { const declaration = tgpu['~unstable'] .declare('@group(0) @binding(0) var val: f32;') .$uses({ - nestedDeclaration: tgpu['~unstable'].declare( + nested: tgpu['~unstable'].declare( `struct Output { x: u32, }`, ), }); - const fn = tgpu['~unstable'] - .fn([])(`() { + const empty = tgpu.fn([])(`() { // do nothing }`) - .$uses({ declaration }) - .$name('empty'); + .$uses({ declaration }); - expect(parseResolved({ fn })).toBe( + expect(parseResolved({ empty })).toBe( parse(` struct Output { x: u32, @@ -116,14 +109,12 @@ describe('tgpu.declare', () => { .declare('@group(0) @binding(0) var val: Output;') .$uses({ Output }); - const fn = tgpu['~unstable'] - .fn([])(`() { + const empty = tgpu.fn([])(`() { // do nothing }`) - .$uses({ declaration }) - .$name('empty'); + .$uses({ declaration }); - expect(parseResolved({ fn })).toBe( + expect(parseResolved({ empty })).toBe( parse(` struct Output { x: u32, @@ -136,17 +127,15 @@ describe('tgpu.declare', () => { ); }); - it('works with tgsl functions', () => { + it('works with TGSL functions', () => { const declaration = tgpu['~unstable'].declare( '@group(0) @binding(0) var val: f32;', ); - const main = tgpu['~unstable'] - .fn([], d.f32)(() => { - declaration; - return 2; - }) - .$name('main'); + const main = tgpu.fn([], d.f32)(() => { + declaration; + return 2; + }); expect(parseResolved({ main })).toBe( parse(` diff --git a/packages/typegpu/tests/derived.test.ts b/packages/typegpu/tests/derived.test.ts index bfea855ee..d710fda83 100644 --- a/packages/typegpu/tests/derived.test.ts +++ b/packages/typegpu/tests/derived.test.ts @@ -7,7 +7,7 @@ import { parse, parseResolved } from './utils/parseResolved.ts'; describe('TgpuDerived', () => { it('memoizes results of transitive "derived"', () => { - const foo = tgpu['~unstable'].slot(1).$name('foo'); + const foo = tgpu['~unstable'].slot(1); const computeDouble = vi.fn(() => { return foo.value * 2; }); @@ -15,11 +15,9 @@ describe('TgpuDerived', () => { const a = tgpu['~unstable'].derived(() => double.value + 1); const b = tgpu['~unstable'].derived(() => double.value + 2); - const main = tgpu['~unstable'] - .fn([], d.f32)(() => { - return a.value + b.value; - }) - .$name('main'); + const main = tgpu.fn([], d.f32)(() => { + return a.value + b.value; + }); expect(parseResolved({ main })).toBe( parse(` @@ -36,23 +34,19 @@ describe('TgpuDerived', () => { const foo = tgpu['~unstable'].slot(); const double = tgpu['~unstable'].derived(() => foo.value * 2); - const getDouble = tgpu['~unstable'] - .fn([], d.f32)(() => { - return double.value; - }) - .$name('getDouble'); + const getDouble = tgpu.fn([], d.f32)(() => { + return double.value; + }); const a = getDouble.with(foo, 2); const b = getDouble.with(foo, 2); // the same as `a` const c = getDouble.with(foo, 4); - const main = tgpu['~unstable'] - .fn([])(() => { - a(); - b(); - c(); - }) - .$name('main'); + const main = tgpu.fn([])(() => { + a(); + b(); + c(); + }); expect(parseResolved({ main })).toBe( parse(` @@ -74,16 +68,14 @@ describe('TgpuDerived', () => { }); it('can use slot values from its surrounding context', () => { - const gridSizeSlot = tgpu['~unstable'].slot().$name('gridSize'); + const gridSizeSlot = tgpu['~unstable'].slot(); const fill = tgpu['~unstable'].derived(() => { const gridSize = gridSizeSlot.value; - return tgpu['~unstable'] - .fn([d.arrayOf(d.f32, gridSize)])((arr) => { - // do something - }) - .$name('fill'); + return tgpu.fn([d.arrayOf(d.f32, gridSize)])( + (arr) => {/* do something */}, + ).$name('fill'); }); const fillWith2 = fill.with(gridSizeSlot, 2); @@ -91,14 +83,12 @@ describe('TgpuDerived', () => { const exampleArray: number[] = [1, 2, 3]; - const main = tgpu['~unstable'] - .fn([])(() => { - fill.value(exampleArray); - fillWith2.value(exampleArray); - fillWith3.value(exampleArray); - }) - .with(gridSizeSlot, 1) - .$name('main'); + const main = tgpu.fn([])(() => { + fill.value(exampleArray); + fillWith2.value(exampleArray); + fillWith3.value(exampleArray); + }) + .with(gridSizeSlot, 1); expect(parseResolved({ main })).toBe( parse(/* wgsl */ ` @@ -123,22 +113,20 @@ describe('TgpuDerived', () => { return mul(2, vec); }); - const Boid = d - .struct({ - pos: d.vec3f, - vel: d.vec3u, - }) - .$name('Boid'); + const Boid = d.struct({ + pos: d.vec3f, + vel: d.vec3u, + }); const buffer = root.createBuffer(Boid).$usage('uniform').$name('boid'); const uniform = buffer.as('uniform'); const derivedUniformSlot = tgpu['~unstable'].derived(() => uniform); - const derivedDerivedUniformSlot = tgpu['~unstable'].derived( - () => derivedUniformSlot, + const derivedDerivedUniformSlot = tgpu['~unstable'].derived(() => + derivedUniformSlot ); - const func = tgpu['~unstable'].fn([])(() => { + const func = tgpu.fn([])(() => { const pos = doubledVectorSlot.value; const posX = doubledVectorSlot.value.x; const vel = derivedUniformSlot.value.vel; @@ -178,26 +166,22 @@ describe('TgpuDerived', () => { // 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'); + const valueSlot = tgpu['~unstable'].slot(1); - const derivedFn = utgpu.derived(() => { - return tgpu['~unstable'] - .fn([], d.f32)(() => valueSlot.value) - .with(valueSlot, valueSlot.value) // currently necessary to work :/ + const derivedFn = tgpu['~unstable'].derived(() => { + return tgpu.fn([], d.f32)(() => valueSlot.$) + .with(valueSlot, valueSlot.$) // currently necessary to work :/ .$name('innerFn'); }); const derivedFnWith2 = derivedFn.with(valueSlot, 2); - const mainFn = tgpu['~unstable'] - .fn([])(() => { - derivedFn.value(); - derivedFnWith2.value(); - }) - .$name('main'); + const main = tgpu.fn([])(() => { + derivedFn.$(); + derivedFnWith2.$(); + }); - expect(parseResolved({ mainFn })).toBe( + expect(parseResolved({ main })).toBe( parse(` fn innerFn() -> f32 { return 1; @@ -216,15 +200,13 @@ describe('TgpuDerived', () => { }); it('does not allow defining derived values at resolution', () => { - const slot = tgpu['~unstable'].slot(2).$name('gridSize'); + const gridSizeSlot = tgpu['~unstable'].slot(2); const derived = tgpu['~unstable'].derived(() => - slot.value > 0 - ? tgpu['~unstable'].derived(() => slot.value).value - : tgpu['~unstable'].derived(() => -slot.value).value + gridSizeSlot.$ > 0 + ? tgpu['~unstable'].derived(() => gridSizeSlot.$).$ + : tgpu['~unstable'].derived(() => -gridSizeSlot.$).$ ); - const fn = tgpu['~unstable'].fn([], d.u32)(() => { - return derived.value; - }); + const fn = tgpu.fn([], d.u32)(() => derived.$); expect(() => parseResolved({ fn })).toThrow( 'Cannot create tgpu.derived objects at the resolution stage.', diff --git a/packages/typegpu/tests/function.test.ts b/packages/typegpu/tests/function.test.ts index a221dee77..d7ccbe0bd 100644 --- a/packages/typegpu/tests/function.test.ts +++ b/packages/typegpu/tests/function.test.ts @@ -11,106 +11,64 @@ import { parse, parseResolved } from './utils/parseResolved.ts'; import { Void } from '../src/data/wgslTypes.ts'; import type { Prettify } from '../src/shared/utilityTypes.ts'; -describe('tgpu.fn', () => { - it('should inject function declaration of called function', () => { - const emptyFn = tgpu['~unstable'] - .fn([])(`() { - // do nothing - }`) - .$name('empty'); - - const actual = parseResolved({ emptyFn }); - - const expected = parse('fn empty() {}'); +const empty = tgpu.fn([])`() { + // do nothing +}`; - expect(actual).toBe(expected); +describe('tgpu.fn', () => { + it('should inject function declaration', () => { + expect(parseResolved({ empty })).toBe(parse('fn empty() {}')); }); it('should inject function declaration only once', () => { - const emptyFn = tgpu['~unstable'] - .fn([])(`() { - // do nothing - }`) - .$name('empty'); - - const actual = parseResolved({ - main: tgpu['~unstable'] - .fn([])(` - () { - emptyFn(); - emptyFn(); - }`) - .$uses({ emptyFn }) - .$name('main'), - }); + const main = tgpu.fn([])`() { + empty(); + empty(); + }` + .$uses({ empty }); - const expected = parse(` + expect(parseResolved({ main })).toBe(parse(` fn empty() {} fn main() { empty(); empty(); } - `); - - expect(actual).toBe(expected); + `)); }); it('should inject function declaration only once (calls are nested)', () => { - const emptyFn = tgpu['~unstable'] - .fn([])(`() { - // do nothing - }`) - .$name('empty'); - - const nestedAFn = tgpu['~unstable'] - .fn([])(`() { - emptyFn(); - }`) - .$uses({ emptyFn }) - .$name('nested_a'); - - const nestedBFn = tgpu['~unstable'] - .fn([])(`() { - emptyFn(); - }`) - .$uses({ emptyFn }) - .$name('nested_b'); - - const actual = parseResolved({ - main: tgpu['~unstable'] - .fn([])(`() { - nestedAFn(); - nestedBFn(); - }`) - .$uses({ nestedAFn, nestedBFn }) - .$name('main'), - }); + const nestedA = tgpu.fn([])`() { empty(); }`.$uses({ empty }); + const nestedB = tgpu.fn([])`() { empty(); }`.$uses({ empty }); - const expected = parse(` + const main = tgpu.fn([])`() { + nestedA(); + nestedB(); + }` + .$uses({ nestedA, nestedB }); + + expect(parseResolved({ main })).toBe(parse(` fn empty() {} - fn nested_a() { + fn nestedA() { empty(); } - fn nested_b() { + fn nestedB() { empty(); } fn main() { - nested_a(); - nested_b(); + nestedA(); + nestedB(); } - `); - - expect(actual).toBe(expected); + `)); }); it('creates typed shell from parameters', () => { - const proc = tgpu['~unstable'].fn([]); - const one = tgpu['~unstable'].fn([d.f32]); - const two = tgpu['~unstable'].fn([d.f32, d.u32]); + const proc = tgpu.fn([]); + const one = tgpu.fn([d.f32]); + const two = tgpu.fn([d.f32, d.u32]); expectTypeOf(proc).toEqualTypeOf>(); expectTypeOf>().toEqualTypeOf< @@ -129,9 +87,9 @@ describe('tgpu.fn', () => { }); it('creates typed shell from parameters and return type', () => { - const proc = tgpu['~unstable'].fn([], d.bool); - const one = tgpu['~unstable'].fn([d.f32], d.bool); - const two = tgpu['~unstable'].fn([d.f32, d.u32], d.bool); + const proc = tgpu.fn([], d.bool); + const one = tgpu.fn([d.f32], d.bool); + const two = tgpu.fn([d.f32, d.u32], d.bool); expectTypeOf(proc).toEqualTypeOf>(); expectTypeOf>().toEqualTypeOf< diff --git a/packages/typegpu/tests/functionTagged.test.ts b/packages/typegpu/tests/functionTagged.test.ts index dbd381e30..710cc6871 100644 --- a/packages/typegpu/tests/functionTagged.test.ts +++ b/packages/typegpu/tests/functionTagged.test.ts @@ -6,43 +6,33 @@ import { parse, parseResolved } from './utils/parseResolved.ts'; describe('tagged syntax', () => { describe('function', () => { it('parses template literal without arguments', () => { - const constFn = tgpu['~unstable'].fn([], d.i32)`() -> i32 { - return 3; - }`.$name('const'); + const getConst = tgpu.fn([], d.i32)`() { return 3; }`; - const actual = parseResolved({ constFn }); - - const expected = parse('fn const() -> i32 { return 3; }'); - - expect(actual).toBe(expected); + expect(parseResolved({ getConst })).toBe(parse(` + fn getConst() -> i32 { + return 3; + } + `)); }); it('parses template literal with arguments of different types', () => { - const addFn = tgpu['~unstable'].fn([], d.f32)`() -> f32 { + const add = tgpu.fn([], d.f32)`() { return f32(${10}) + f32(${'20'}) + f32(${30.1}); - }`.$name('add'); - - const actual = parseResolved({ addFn }); + }`; - const expected = parse( + expect(parseResolved({ add })).toBe(parse( 'fn add() -> f32 { return f32(10) + f32(20) + f32(30.1); }', - ); - - expect(actual).toBe(expected); + )); }); it('parses template literal with arguments of different types, object args', () => { - const addFn = tgpu['~unstable'].fn([], d.f32)`() -> f32 { + const add = tgpu.fn([], d.f32)`() { return f32(${10}) + f32(${'20'}) + f32(${30.1}); - }`.$name('add'); - - const actual = parseResolved({ addFn }); + }`; - const expected = parse( + expect(parseResolved({ add })).toBe(parse( 'fn add() -> f32 { return f32(10) + f32(20) + f32(30.1); }', - ); - - expect(actual).toBe(expected); + )); }); }); diff --git a/packages/typegpu/tests/rawFn.test.ts b/packages/typegpu/tests/rawFn.test.ts index f93dfd037..b2d7efcc3 100644 --- a/packages/typegpu/tests/rawFn.test.ts +++ b/packages/typegpu/tests/rawFn.test.ts @@ -6,73 +6,55 @@ import { parse, parseResolved } from './utils/parseResolved.ts'; describe('tgpu.fn with raw string WGSL implementation', () => { it('is namable', () => { - const getX = tgpu['~unstable'] - .fn([], d.f32)(`() -> f32 { - return 3.0f; - }`) - .$name('get_x'); + const getX = tgpu.fn([], d.f32)`() { return 3.0f; }`.$name('get_x'); expect(getName(getX)).toBe('get_x'); }); - it('resolves rawFn to WGSL', () => { - const getY = tgpu['~unstable'] - .fn([], d.f32)(`() -> f32 { - return 3.0f; - }`) - .$name('get_y'); - - const actual = parseResolved({ getY }); + it('resolves to WGSL', () => { + const getY = tgpu.fn([], d.f32)`() { return 3.0f; }`; - const expected = parse(` - fn get_y() -> f32 { + expect(parseResolved({ getY })).toBe(parse(` + fn getY() -> f32 { return 3.0f; } - `); - - expect(actual).toBe(expected); + `)); }); it('resolves externals and replaces their usages in code', () => { - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(`() -> vec3f { - let color = vec3f(); - return color; - }`) - .$name('get_color'); + const getColor = tgpu.fn([], d.vec3f)`() { + let color = vec3f(); + return color; + }`; - const getX = tgpu['~unstable'] - .fn([], d.f32)(`() -> f32 { + const getX = tgpu.fn([], d.f32)`() -> f32 { let color = get_color(); return 3.0f; - }`) - .$name('get_x') + }` .$uses({ get_color: getColor }); - const getY = tgpu['~unstable'] - .fn([], d.f32)(`() -> f32 { + const getY = tgpu.fn([], d.f32)(`() -> f32 { let c = color(); - return getX(); + return get_x(); }`) - .$name('get_y') - .$uses({ getX, color: getColor }); + .$uses({ get_x: getX, color: getColor }); const actual = parseResolved({ getY }); const expected = parse(` - fn get_color() -> vec3f { + fn getColor() -> vec3f { let color = vec3f(); return color; } - fn get_x() -> f32 { - let color = get_color(); + fn getX() -> f32 { + let color = getColor(); return 3.0f; } - fn get_y() -> f32 { - let c = get_color(); - return get_x(); + fn getY() -> f32 { + let c = getColor(); + return getX(); } `); @@ -80,14 +62,9 @@ describe('tgpu.fn with raw string WGSL implementation', () => { }); it('replaces external usage just for exact identifier matches', () => { - const getx = tgpu['~unstable'] - .fn([], d.f32)(`() -> f32 { - return 3.0f; - }`) - .$name('external'); + const getx = tgpu.fn([], d.f32)`() { return 3.0f; }`.$name('external'); - const getY = tgpu['~unstable'] - .fn([], d.f32)(`() -> f32 { + const getY = tgpu.fn([], d.f32)`() { let x = getx(); let y = getx() + getx(); let z = hellogetx(); @@ -95,7 +72,7 @@ describe('tgpu.fn with raw string WGSL implementation', () => { xgetx(); getxx(); return getx(); - }`) + }` .$name('get_y') .$uses({ getx }); @@ -121,12 +98,10 @@ describe('tgpu.fn with raw string WGSL implementation', () => { }); it("doesn't replace property access identifiers when replacing externals", () => { - const HighlightedCircle = d - .struct({ - index: d.u32, - color: d.vec4f, - }) - .$name('HighlightedCircle'); + const HighlightedCircle = d.struct({ + index: d.u32, + color: d.vec4f, + }); const uniformBindGroupLayout = tgpu.bindGroupLayout({ highlightedCircle: { uniform: HighlightedCircle }, @@ -251,15 +226,12 @@ struct fragment_Output { }); it('automatically adds struct definitions of argument types when resolving wgsl-defined functions', () => { - const Point = d - .struct({ - a: d.u32, - b: d.u32, - }) - .$name('Point'); + const Point = d.struct({ + a: d.u32, + b: d.u32, + }); - const func = tgpu['~unstable'] - .fn([d.vec4f, Point])(/* wgsl */ `(a: vec4f, b: Point) { + const func = tgpu.fn([d.vec4f, Point])(/* wgsl */ `(a: vec4f, b: Point) { var newPoint: Point; newPoint = b; }`) @@ -287,14 +259,13 @@ struct fragment_Output { }) .$name('P'); - const func = tgpu['~unstable'] - .fn([d.vec4f, Point], d.vec2f)(/* wgsl */ `( - a: vec4f, - b: PointStruct - ) -> vec2f { - var newPoint: PointStruct; - newPoint = b; - }`) + const func = tgpu.fn([d.vec4f, Point], d.vec2f)(/* wgsl */ `( + a: vec4f, + b: PointStruct + ) -> vec2f { + var newPoint: PointStruct; + newPoint = b; + }`) .$name('newPointF'); expect(parseResolved({ func })).toBe( @@ -319,13 +290,13 @@ struct fragment_Output { }) .$name('P'); - const func = tgpu['~unstable'] - .fn([d.vec4f], Point)(/* wgsl */ `(a: vec4f) -> PointStruct { + const func = tgpu.fn([d.vec4f], Point)( + /* wgsl */ `(a: vec4f) -> PointStruct { var newPoint: PointStruct; newPoint = b; return newPoint; - }`) - .$name('newPointF'); + }`, + ).$name('newPointF'); expect(parseResolved({ func })).toBe( parse(` @@ -346,7 +317,7 @@ struct fragment_Output { // it('adds return type nested struct definitions when resolving wgsl-defined functions', () => { // const Point = d.struct({ a: d.u32 }).$name('P'); - // const func = tgpu['~unstable'] + // const func = tgpu // .fn([d.arrayOf(Point, 4)])(/* wgsl */ `(a: array) { // return; // }`) @@ -365,15 +336,13 @@ struct fragment_Output { // }); it('resolves object externals and replaces their usages in code', () => { - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(`() -> vec3f { + const getColor = tgpu.fn([], d.vec3f)(`() -> vec3f { let color = vec3f(); return color; }`) .$name('get_color'); - const main = tgpu['~unstable'] - .fn([], d.f32)(`() -> f32 { + const main = tgpu.fn([], d.f32)(`() -> f32 { let c = functions.getColor(); return c.x; }`) @@ -398,13 +367,12 @@ struct fragment_Output { it('resolves compound types with structs provided in externals', () => { const Point = d.struct({ a: d.u32 }).$name('P'); - const getColor = tgpu['~unstable'] - .fn([d.arrayOf(Point, 4)], d.u32)( - /* wgsl */ `(a: array) { + const getColor = tgpu.fn([d.arrayOf(Point, 4)], d.u32)( + `(a: array) { var b: MyPoint = a[0]; return b.a; }`, - ) + ) .$name('get_color') .$uses({ MyPoint: Point }); @@ -422,12 +390,12 @@ struct fragment_Output { describe('tgpu.fn with raw wgsl and missing types', () => { it('resolves missing base types', () => { - const getColor = tgpu['~unstable'] - .fn([d.vec3f, d.u32, d.mat2x2f, d.bool, d.vec2b], d.vec4u)( - /* wgsl */ `(a, b: u32, c, d, e) { + const getColor = tgpu.fn( + [d.vec3f, d.u32, d.mat2x2f, d.bool, d.vec2b], + d.vec4u, + )(`(a, b: u32, c, d, e) { return vec4u(); - }`, - ) + }`) .$name('get_color'); expect(parseResolved({ getColor })).toBe( @@ -440,12 +408,9 @@ describe('tgpu.fn with raw wgsl and missing types', () => { }); it('resolves void functions', () => { - const getColor = tgpu['~unstable'] - .fn([])( - `() { + const getColor = tgpu.fn([])(`() { return; - }`, - ) + }`) .$name('get_color'); expect(parseResolved({ getColor })).toBe( @@ -458,12 +423,9 @@ describe('tgpu.fn with raw wgsl and missing types', () => { }); it('resolves compound types', () => { - const getColor = tgpu['~unstable'] - .fn([d.arrayOf(d.u32, 4)], d.u32)( - /* wgsl */ `(a) { + const getColor = tgpu.fn([d.arrayOf(d.u32, 4)], d.u32)(`(a) { return a[0]; - }`, - ) + }`) .$name('get_color'); expect(parseResolved({ getColor })).toBe( @@ -478,13 +440,10 @@ describe('tgpu.fn with raw wgsl and missing types', () => { it('resolves compound types with structs provided in externals', () => { const Point = d.struct({ a: d.u32 }).$name('P'); - const getColor = tgpu['~unstable'] - .fn([d.arrayOf(Point, 4)], d.u32)( - /* wgsl */ `(a) { + const getColor = tgpu.fn([d.arrayOf(Point, 4)], d.u32)(`(a) { var b: MyPoint = a[0]; return b.a; - }`, - ) + }`) .$name('get_color') .$uses({ MyPoint: Point }); @@ -507,10 +466,9 @@ describe('tgpu.fn with raw wgsl and missing types', () => { }) .$name('P'); - const func = tgpu['~unstable'] - .fn([Point, Point], Point)(/* wgsl */ `(a, b: PointStruct) { - return b; - }`) + const func = tgpu.fn([Point, Point], Point)(`(a, b: PointStruct) { + return b; + }`) .$name('newPointF'); expect(parseResolved({ func })).toBe( @@ -527,12 +485,9 @@ describe('tgpu.fn with raw wgsl and missing types', () => { }); it('throws when parameter type mismatch', () => { - const getColor = tgpu['~unstable'] - .fn([d.vec3f])( - /* wgsl */ `(a: vec4f) { + const getColor = tgpu.fn([d.vec3f])(`(a: vec4f) { return; - }`, - ) + }`) .$name('get_color'); expect(() => parseResolved({ getColor })) @@ -546,12 +501,9 @@ describe('tgpu.fn with raw wgsl and missing types', () => { it('throws when compound parameter type mismatch', () => { const Point = d.struct({ a: d.u32 }).$name('P'); - const getColor = tgpu['~unstable'] - .fn([d.arrayOf(Point, 4)])( - /* wgsl */ `(a: arrayOf) { + const getColor = tgpu.fn([d.arrayOf(Point, 4)])(`(a: arrayOf) { return; - }`, - ) + }`) .$name('get_color') .$uses({ MyPoint: Point }); @@ -564,12 +516,9 @@ describe('tgpu.fn with raw wgsl and missing types', () => { }); it('throws when return type mismatch', () => { - const getColor = tgpu['~unstable'] - .fn([], d.vec4f)( - /* wgsl */ `() -> vec2f { + const getColor = tgpu.fn([], d.vec4f)(`() -> vec2f { return; - }`, - ) + }`) .$name('get_color'); expect(() => parseResolved({ getColor })) @@ -581,12 +530,9 @@ describe('tgpu.fn with raw wgsl and missing types', () => { }); it('throws when wrong argument count', () => { - const getColor = tgpu['~unstable'] - .fn([d.vec3f, d.vec4f])( - /* wgsl */ `(a, b, c) { + const getColor = tgpu.fn([d.vec3f, d.vec4f])(`(a, b, c) { return; - }`, - ) + }`) .$name('get_color'); expect(() => parseResolved({ getColor })) @@ -599,12 +545,9 @@ describe('tgpu.fn with raw wgsl and missing types', () => { it('resolves implicitly typed struct without externals', () => { const Point = d.struct({ a: d.i32 }).$name('myStruct'); - const getColor = tgpu['~unstable'] - .fn([Point])( - /* wgsl */ `(a) { + const getColor = tgpu.fn([Point])(`(a) { return; - }`, - ) + }`) .$name('get_color'); console.log(parseResolved({ getColor })); diff --git a/packages/typegpu/tests/resolve.test.ts b/packages/typegpu/tests/resolve.test.ts index f0bbb8a6a..8565043e8 100644 --- a/packages/typegpu/tests/resolve.test.ts +++ b/packages/typegpu/tests/resolve.test.ts @@ -80,10 +80,9 @@ describe('tgpu resolve', () => { health: d.f32, }); - const getPlayerHealth = tgpu['~unstable'] - .fn([PlayerData], d.f32)((pInfo) => { - return pInfo.health; - }) + const getPlayerHealth = tgpu.fn([PlayerData], d.f32)((pInfo) => { + return pInfo.health; + }) .$name('getPlayerHealthTest'); const shaderLogic = ` @@ -131,15 +130,14 @@ describe('tgpu resolve', () => { range: d.vec2f, }); - const random = tgpu['~unstable'] - .fn([], d.f32)(/* wgsl */ `() -> f32 { + const random = tgpu.fn([], d.f32)`() { var r: Random; r.seed = vec2(3.14, 1.59); r.range = vec2(0.0, 1.0); r.seed.x = fract(cos(dot(r.seed, vec2f(23.14077926, 232.61690225))) * 136.8168); r.seed.y = fract(cos(dot(r.seed, vec2f(54.47856553, 345.84153136))) * 534.7645); return clamp(r.seed.y, r.range.x, r.range.y); - }`) + }` .$uses({ Random }); const shaderLogic = ` @@ -283,8 +281,7 @@ describe('tgpu resolve', () => { }); it('should resolve object externals and replace their usages in template', () => { - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(`() -> vec3f { + const getColor = tgpu.fn([], d.vec3f)(`() -> vec3f { let color = vec3f(); return color; }`) @@ -323,15 +320,13 @@ describe('tgpu resolve', () => { }); it('should resolve only used object externals and ignore non-existing', () => { - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(`() -> vec3f { + const getColor = tgpu.fn([], d.vec3f)(`() -> vec3f { let color = vec3f(); return color; }`) .$name('get_color'); - const getIntensity = tgpu['~unstable'] - .fn([], d.vec3f)(`() -> vec3f { + const getIntensity = tgpu.fn([], d.vec3f)(`() -> vec3f { return 1; }`) .$name('get_intensity'); diff --git a/packages/typegpu/tests/slot.test.ts b/packages/typegpu/tests/slot.test.ts index 6d607074a..ccb2ed9c7 100644 --- a/packages/typegpu/tests/slot.test.ts +++ b/packages/typegpu/tests/slot.test.ts @@ -11,17 +11,15 @@ describe('tgpu.slot', () => { it('resolves to default value if no value provided', () => { const colorSlot = tgpu['~unstable'].slot(RED); // red by default - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { + const getColor = tgpu.fn([], d.vec3f)`() { return colorSlot; - }`) - .$name('getColor') + }` .$uses({ colorSlot }); const actual = parseResolved({ getColor }); expect(actual).toBe( - parse(/* wgsl */ ` + parse(` fn getColor() -> vec3f { return ${RED}; } @@ -32,19 +30,16 @@ describe('tgpu.slot', () => { it('resolves to provided value rather than default value', () => { const colorSlot = tgpu['~unstable'].slot(RED); // red by default - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { + const getColor = tgpu.fn([], d.vec3f)`() { return colorSlot; - }`) - .$name('getColor') + }` .$uses({ colorSlot }); // overriding to green const getColorWithGreen = getColor.with(colorSlot, GREEN); - const main = tgpu['~unstable'] - .fn([])(`() { - return getColorWithGreen(); + const main = tgpu.fn([])(`() { + getColorWithGreen(); }`) .$name('main') .$uses({ getColorWithGreen }); @@ -57,7 +52,7 @@ describe('tgpu.slot', () => { } fn main() { - return getColor(); + getColor(); } `), ); @@ -66,21 +61,17 @@ describe('tgpu.slot', () => { it('resolves to provided value', () => { const colorSlot = tgpu['~unstable'].slot(); // no default - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { + const getColor = tgpu.fn([], d.vec3f)`() { return colorSlot; - }`) - .$name('getColor') + }` .$uses({ colorSlot }); // overriding to green const getColorWithGreen = getColor.with(colorSlot, 'vec3f(0., 1., 0.)'); - const main = tgpu['~unstable'] - .fn([])(/* wgsl */ `() { + const main = tgpu.fn([])`() { getColorWithGreen(); - }`) - .$name('main') + }` .$uses({ getColorWithGreen }); const actual = parseResolved({ main }); @@ -102,11 +93,9 @@ describe('tgpu.slot', () => { it('throws error when no default nor value provided', () => { const colorSlot = tgpu['~unstable'].slot().$name('color'); - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(`() -> vec3f { + const getColor = tgpu.fn([], d.vec3f)`() { return colorSlot; - })`) - .$name('getColor') + }` .$uses({ colorSlot }); expect(() => tgpu.resolve({ externals: { getColor }, names: 'strict' })) @@ -121,31 +110,25 @@ describe('tgpu.slot', () => { it('prefers closer scope', () => { const colorSlot = tgpu['~unstable'].slot(); // no default - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { + const getColor = tgpu.fn([], d.vec3f)`() -> vec3f { return colorSlot; - }`) - .$name('getColor') + }` .$uses({ colorSlot }); const getColorWithRed = getColor.with(colorSlot, RED); const getColorWithGreen = getColor.with(colorSlot, GREEN); - const wrapperFn = tgpu['~unstable'] - .fn([])(/* wgsl */ `() { + const wrapper = tgpu.fn([])`() { return getColorWithGreen(); - }`) + }` .$uses({ getColorWithGreen }) - .$name('wrapper') .with(colorSlot, RED); - const main = tgpu['~unstable'] - .fn([])(/* wgsl */ `() { + const main = tgpu.fn([])`() { getColorWithRed(); - wrapperFn(); - }`) - .$uses({ getColorWithRed, wrapperFn }) - .$name('main'); + wrapper(); + }` + .$uses({ getColorWithRed, wrapper }); const actual = parseResolved({ main }); @@ -175,55 +158,43 @@ describe('tgpu.slot', () => { const sizeSlot = tgpu['~unstable'].slot<1 | 100>(); const colorSlot = tgpu['~unstable'].slot(); - const getSize = tgpu['~unstable'] - .fn([], d.f32)(/* wgsl */ `() -> f32 { - return sizeSlot; - }`) - .$uses({ sizeSlot }) - .$name('getSize'); + const getSize = tgpu.fn([], d.f32)`() { return sizeSlot; }` + .$uses({ sizeSlot }); - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(/* wgsl */ `() -> vec3f { - return colorSlot; - }`) - .$uses({ colorSlot }) - .$name('getColor'); + const getColor = tgpu.fn([], d.vec3f)`() -> vec3f { return colorSlot; }` + .$uses({ colorSlot }); - const sizeAndColor = tgpu['~unstable'] - .fn([])(`() { + const sizeAndColor = tgpu.fn([])`() { getSize(); getColor(); - }`) - .$uses({ getSize, getColor }) - .$name('sizeAndColor'); + }` + .$uses({ getSize, getColor }); - const wrapperFn = tgpu['~unstable'] - .fn([])(`() { + const wrapper = tgpu + .fn([])`() { sizeAndColor(); - }`) - .$uses({ sizeAndColor }) - .$name('wrapper'); + }` + .$uses({ sizeAndColor }); - const wrapperWithSmallRed = wrapperFn + const wrapperWithSmallRed = wrapper .with(sizeSlot, 1) .with(colorSlot, RED); - const wrapperWithBigRed = wrapperFn + const wrapperWithBigRed = wrapper .with(sizeSlot, 100) .with(colorSlot, RED); - const wrapperWithSmallGreen = wrapperFn + const wrapperWithSmallGreen = wrapper .with(sizeSlot, 1) .with(colorSlot, GREEN); - const wrapperWithBigGreen = wrapperFn + const wrapperWithBigGreen = wrapper .with(sizeSlot, 100) .with(colorSlot, GREEN); - const main = tgpu['~unstable'] - .fn([])(`() { + const main = tgpu.fn([])`() { wrapperWithSmallRed(); wrapperWithBigRed(); wrapperWithSmallGreen(); wrapperWithBigGreen(); - }`) + }` .$uses({ wrapperWithSmallRed, wrapperWithBigRed, @@ -304,29 +275,19 @@ describe('tgpu.slot', () => { const slotC = tgpu['~unstable'].slot(3); const slotD = tgpu['~unstable'].slot(4); - const fn1 = tgpu['~unstable'] - .fn([])('() { let value = slotA; }') - .$uses({ slotA }) - .$name('fn1'); - const fn2 = tgpu['~unstable'] - .fn([])('() { fn1(); }') + const fn1 = tgpu.fn([])`() { let value = slotA; }` + .$uses({ slotA }); + const fn2 = tgpu.fn([])`() { fn1(); }` .$uses({ fn1 }) - .$name('fn2') .with(slotC, slotD); - const fn3 = tgpu['~unstable'] - .fn([])('() { fn2(); }') + const fn3 = tgpu.fn([])`() { fn2(); }` .$uses({ fn2 }) - .$name('fn3') .with(slotB, slotC); - const fn4 = tgpu['~unstable'] - .fn([])('() { fn3(); }') + const fn4 = tgpu.fn([])`() { fn3(); }` .$uses({ fn3 }) - .$name('fn4') .with(slotA, slotB); - const main = tgpu['~unstable'] - .fn([])('() { fn4(); }') - .$uses({ fn4 }) - .$name('main'); + const main = tgpu.fn([])`() { fn4(); }` + .$uses({ fn4 }); const actual = parseResolved({ main }); const expected = parse(/* wgsl */ ` @@ -354,15 +315,11 @@ 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)(() => d.vec3f(1, 2, 3)) - .$name('getColor'), - ); - const colorAccessorSlot = tgpu['~unstable'].slot(colorAccessorFn); + 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 func = tgpu['~unstable'].fn([])(() => { + const func = tgpu.fn([])(() => { const pos = vectorSlot.value; const posX = vectorSlot.value.x; const vel = uniformSlot.value.vel; @@ -371,7 +328,7 @@ describe('tgpu.slot', () => { const vel_ = uniformSlotSlot.value.vel; const velX_ = uniformSlotSlot.value.vel.x; - const color = colorAccessorSlot.value; + const color = colorAccessSlot.value; }); const resolved = tgpu.resolve({ diff --git a/packages/typegpu/tests/std/matrix/rotate.test.ts b/packages/typegpu/tests/std/matrix/rotate.test.ts index 12575219b..4a9feea39 100644 --- a/packages/typegpu/tests/std/matrix/rotate.test.ts +++ b/packages/typegpu/tests/std/matrix/rotate.test.ts @@ -9,15 +9,14 @@ describe('rotate', () => { it('generates correct WGSL for rotateX4 with custom matrix', () => { const M = mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1); - const rotateFn = tgpu['~unstable'] - .fn([])(() => { - const angle = 4; - const resultExpression = rotateX4(M, angle); - }).$name('rotateX4'); + const rotateFn = tgpu.fn([])(() => { + const angle = 4; + const resultExpression = rotateX4(M, angle); + }); expect(parseResolved({ rotateFn })).toBe( parse( - `fn rotateX4() { + `fn rotateFn() { var angle = 4; var resultExpression = ( mat4x4f(1, 0, 0, 0, 0, cos(angle), sin(angle), 0, 0, -sin(angle), cos(angle), 0, 0, 0, 0, 1) * @@ -31,15 +30,14 @@ describe('rotate', () => { it('generates correct WGSL for rotateY4 with custom matrix', () => { const M = mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1); - const rotateFn = tgpu['~unstable'] - .fn([])(() => { - const angle = 4; - const resultExpression = rotateY4(M, angle); - }).$name('rotateY4'); + const rotateFn = tgpu.fn([])(() => { + const angle = 4; + const resultExpression = rotateY4(M, angle); + }); expect(parseResolved({ rotateFn })).toBe( parse( - `fn rotateY4() { + `fn rotateFn() { var angle = 4; var resultExpression = ( mat4x4f(cos(angle), 0, -sin(angle), 0, 0, 1, 0, 0, sin(angle), 0, cos(angle), 0, 0, 0, 0, 1) * @@ -53,15 +51,14 @@ describe('rotate', () => { it('generates correct WGSL for rotateZ4 with custom matrix', () => { const M = mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1); - const rotateFn = tgpu['~unstable'] - .fn([])(() => { - const angle = 4; - const resultExpression = rotateZ4(M, angle); - }).$name('rotateZ4'); + const rotateFn = tgpu.fn([])(() => { + const angle = 4; + const resultExpression = rotateZ4(M, angle); + }); expect(parseResolved({ rotateFn })).toBe( parse( - `fn rotateZ4() { + `fn rotateFn() { var angle = 4; var resultExpression = ( mat4x4f(cos(angle), sin(angle), 0, 0, -sin(angle), cos(angle), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * @@ -73,33 +70,24 @@ describe('rotate', () => { }); it('rotates around X correctly', () => { - expect( - isCloseTo( - mul(rotateX4(mat4x4f.identity(), Math.PI / 2), vec4f(1, 2, 3, 1)), - vec4f(1, -3, 2, 1), - ), - ) - .toBe(true); + expect(isCloseTo( + mul(rotateX4(mat4x4f.identity(), Math.PI / 2), vec4f(1, 2, 3, 1)), + vec4f(1, -3, 2, 1), + )).toBe(true); }); it('rotates around Y correctly', () => { - expect( - isCloseTo( - mul(rotateY4(mat4x4f.identity(), Math.PI / 2), vec4f(1, 2, 3, 1)), - vec4f(3, 2, -1, 1), - ), - ) - .toBe(true); + expect(isCloseTo( + mul(rotateY4(mat4x4f.identity(), Math.PI / 2), vec4f(1, 2, 3, 1)), + vec4f(3, 2, -1, 1), + )).toBe(true); }); it('rotates around Z correctly', () => { - expect( - isCloseTo( - mul(rotateZ4(mat4x4f.identity(), Math.PI / 2), vec4f(1, 2, 3, 1)), - vec4f(-2, 1, 3, 1), - ), - ) - .toBe(true); + expect(isCloseTo( + mul(rotateZ4(mat4x4f.identity(), Math.PI / 2), vec4f(1, 2, 3, 1)), + vec4f(-2, 1, 3, 1), + )).toBe(true); }); it('applies order correctly', () => { diff --git a/packages/typegpu/tests/std/matrix/scale.test.ts b/packages/typegpu/tests/std/matrix/scale.test.ts index 133ba8902..452b29842 100644 --- a/packages/typegpu/tests/std/matrix/scale.test.ts +++ b/packages/typegpu/tests/std/matrix/scale.test.ts @@ -18,14 +18,13 @@ describe('scale', () => { const M = mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1); const T = vec3f(2, 2, 4); - const scaleFn = tgpu['~unstable'] - .fn([])(() => { - const resultExpression = scale4(M, T); - }).$name('scale4'); + const scaleFn = tgpu.fn([])(() => { + const resultExpression = scale4(M, T); + }); expect(parseResolved({ scaleFn })).toBe( parse( - `fn scale4() { + `fn scaleFn() { var resultExpression = ( mat4x4f(vec3f(2, 2, 4).x, 0, 0, 0, 0, vec3f(2, 2, 4).y, 0, 0, 0, 0, vec3f(2, 2, 4).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1) diff --git a/packages/typegpu/tests/std/matrix/translate.test.ts b/packages/typegpu/tests/std/matrix/translate.test.ts index 8f25b5518..da7b978c3 100644 --- a/packages/typegpu/tests/std/matrix/translate.test.ts +++ b/packages/typegpu/tests/std/matrix/translate.test.ts @@ -18,14 +18,13 @@ describe('translate', () => { const M = mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1); const T = vec3f(2, 2, 4); - const translateFn = tgpu['~unstable'] - .fn([])(() => { - const resultExpression = translate4(M, T); - }).$name('translate4'); + const translateFn = tgpu.fn([])(() => { + const resultExpression = translate4(M, T); + }); expect(parseResolved({ translateFn })).toBe( parse( - `fn translate4() { + `fn translateFn() { var resultExpression = ( mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(2, 2, 4).x, vec3f(2, 2, 4).y, vec3f(2, 2, 4).z, 1) * mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1) diff --git a/packages/typegpu/tests/tgsl/codeGen.test.ts b/packages/typegpu/tests/tgsl/codeGen.test.ts index b7109e962..b1f7898cf 100644 --- a/packages/typegpu/tests/tgsl/codeGen.test.ts +++ b/packages/typegpu/tests/tgsl/codeGen.test.ts @@ -10,7 +10,7 @@ describe('codeGen', () => { describe('vectors', () => { it('handles member access for external vectors', () => { const size = d.vec3f(1, 2, 3); - const main = tgpu['~unstable'].fn([], d.f32)(() => { + const main = tgpu.fn([], d.f32)(() => { return size.x * size.y * size.z; }); @@ -20,7 +20,7 @@ describe('codeGen', () => { }); it('handles member access for local vectors', () => { - const main = tgpu['~unstable'].fn([], d.f32)(() => { + const main = tgpu.fn([], d.f32)(() => { const size = d.vec3f(1, 2, 3); return size.x * size.y * size.z; }); diff --git a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts index 3238a4a42..cb5dcb097 100644 --- a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts +++ b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts @@ -160,10 +160,9 @@ describe('wgslGenerator', () => { const testUsage = testBuffer.as('mutable'); - const testFn = tgpu['~unstable'] - .fn([], d.u32)(() => { - return testUsage.value.a + testUsage.value.b.x; - }); + const testFn = tgpu.fn([], d.u32)(() => { + return testUsage.value.a + testUsage.value.b.x; + }); const astInfo = getMetaData( testFn[$internal].implementation as (...args: unknown[]) => unknown, @@ -225,7 +224,7 @@ describe('wgslGenerator', () => { const testUsage = testBuffer.as('uniform'); - const testFn = tgpu['~unstable'].fn([], d.u32)(() => { + const testFn = tgpu.fn([], d.u32)(() => { return testUsage.value[3] as number; }); @@ -281,15 +280,14 @@ describe('wgslGenerator', () => { const testUsage = testBuffer.as('mutable'); - const testFn = tgpu['~unstable'] - .fn([d.u32], d.vec4f)((idx) => { - // biome-ignore lint/style/noNonNullAssertion: - const value = std.atomicLoad(testUsage.value.b.aa[idx]!.y); - const vec = std.mix(d.vec4f(), testUsage.value.a, value); - // biome-ignore lint/style/noNonNullAssertion: - std.atomicStore(testUsage.value.b.aa[idx]!.x, vec.y); - return vec; - }); + const testFn = tgpu.fn([d.u32], d.vec4f)((idx) => { + // biome-ignore lint/style/noNonNullAssertion: + const value = std.atomicLoad(testUsage.value.b.aa[idx]!.y); + const vec = std.mix(d.vec4f(), testUsage.value.a, value); + // biome-ignore lint/style/noNonNullAssertion: + std.atomicStore(testUsage.value.b.aa[idx]!.x, vec.y); + return vec; + }); const astInfo = getMetaData( testFn[$internal].implementation as (...args: unknown[]) => unknown, @@ -427,11 +425,9 @@ describe('wgslGenerator', () => { }); it('creates correct resources for derived values and slots', () => { - const testFn = tgpu['~unstable'] - .fn([], d.vec4u)(() => { - return derivedV4u.value; - }) - .$name('testFn'); + const testFn = tgpu.fn([], d.vec4u)(() => { + return derivedV4u.value; + }); expect(parseResolved({ testFn })).toBe( parse(` @@ -470,10 +466,9 @@ describe('wgslGenerator', () => { }); it('creates correct resources for indexing into a derived value', () => { - const testFn = tgpu['~unstable'] - .fn([d.u32], d.f32)((idx) => { - return derivedV2f.value[idx] as number; - }); + const testFn = tgpu.fn([d.u32], d.f32)((idx) => { + return derivedV2f.value[idx] as number; + }); const astInfo = getMetaData( testFn[$internal].implementation as (...args: unknown[]) => unknown, @@ -505,7 +500,7 @@ describe('wgslGenerator', () => { }); it('generates correct code for array expressions', () => { - const testFn = tgpu['~unstable'].fn([], d.u32)(() => { + const testFn = tgpu.fn([], d.u32)(() => { const arr = [d.u32(1), 2, 3]; return arr[1] as number; }); @@ -551,7 +546,7 @@ describe('wgslGenerator', () => { }); it('generates correct code for complex array expressions', () => { - const testFn = tgpu['~unstable'].fn([], d.u32)(() => { + const testFn = tgpu.fn([], d.u32)(() => { const arr = [ d.vec2u(1, 2), d.vec2u(3, 4), @@ -570,15 +565,13 @@ describe('wgslGenerator', () => { }); it('generates correct code for array expressions with struct elements', () => { - const testStruct = d - .struct({ - x: d.u32, - y: d.f32, - }) - .$name('TestStruct'); - - const testFn = tgpu['~unstable'].fn([], d.f32)(() => { - const arr = [testStruct({ x: 1, y: 2 }), testStruct({ x: 3, y: 4 })]; + const TestStruct = d.struct({ + x: d.u32, + y: d.f32, + }); + + const testFn = tgpu.fn([], d.f32)(() => { + const arr = [TestStruct({ x: 1, y: 2 }), TestStruct({ x: 3, y: 4 })]; return (arr[1] as { x: number; y: number }).y; }); @@ -604,7 +597,7 @@ describe('wgslGenerator', () => { } expect(JSON.stringify(astInfo.ast?.body)).toMatchInlineSnapshot( - `"[0,[[13,"arr",[100,[[6,"testStruct",[[104,{"x":[5,"1"],"y":[5,"2"]}]]],[6,"testStruct",[[104,{"x":[5,"3"],"y":[5,"4"]}]]]]]],[10,[7,[8,"arr",[5,"1"]],"y"]]]]"`, + `"[0,[[13,"arr",[100,[[6,"TestStruct",[[104,{"x":[5,"1"],"y":[5,"2"]}]]],[6,"TestStruct",[[104,{"x":[5,"3"],"y":[5,"4"]}]]]]]],[10,[7,[8,"arr",[5,"1"]],"y"]]]]"`, ); ctx[$internal].itemStateStack.pushFunctionScope( @@ -614,26 +607,21 @@ describe('wgslGenerator', () => { astInfo.externals ?? {}, ); - // Check for: const arr = [testStruct({ x: 1, y: 2 }), testStruct({ x: 3, y: 4 })]; + // Check for: const arr = [TestStruct({ x: 1, y: 2 }), TestStruct({ x: 3, y: 4 })]; // ^ this should be an array const res = wgslGenerator.generateExpression( ctx, (astInfo.ast?.body[1][0] as tinyest.Const)[2] as tinyest.Expression, ); - expect(res.dataType).toStrictEqual(d.arrayOf(testStruct, 2)); + expect(res.dataType).toStrictEqual(d.arrayOf(TestStruct, 2)); }); it('generates correct code for array expressions with derived elements', () => { - const testFn = tgpu['~unstable'] - .fn([], d.f32)(() => { - const arr = [ - derivedV2f.value, - std.mul(derivedV2f.value, d.vec2f(2, 2)), - ]; - return (arr[1] as { x: number; y: number }).y; - }) - .$name('testFn'); + const testFn = tgpu.fn([], d.f32)(() => { + const arr = [derivedV2f.$, std.mul(derivedV2f.$, d.vec2f(2, 2))]; + return (arr[1] as { x: number; y: number }).y; + }); expect(parseResolved({ testFn })).toBe( parse(` @@ -652,7 +640,7 @@ describe('wgslGenerator', () => { } expect(JSON.stringify(astInfo.ast?.body)).toMatchInlineSnapshot( - `"[0,[[13,"arr",[100,[[7,"derivedV2f","value"],[6,[7,"std","mul"],[[7,"derivedV2f","value"],[6,[7,"d","vec2f"],[[5,"2"],[5,"2"]]]]]]]],[10,[7,[8,"arr",[5,"1"]],"y"]]]]"`, + `"[0,[[13,"arr",[100,[[7,"derivedV2f","$"],[6,[7,"std","mul"],[[7,"derivedV2f","$"],[6,[7,"d","vec2f"],[[5,"2"],[5,"2"]]]]]]]],[10,[7,[8,"arr",[5,"1"]],"y"]]]]"`, ); }); @@ -662,11 +650,11 @@ describe('wgslGenerator', () => { y: d.vec3f, }); - const fnOne = tgpu['~unstable'].fn([], TestStruct).does(() => { + const fnOne = tgpu.fn([], TestStruct)(() => { return TestStruct({ x: 1, y: d.vec3f(1, 2, 3) }); }); - const fnTwo = tgpu['~unstable'].fn([], d.f32).does(() => { + const fnTwo = tgpu.fn([], d.f32)(() => { return fnOne().y.x; }); @@ -724,11 +712,10 @@ describe('wgslGenerator', () => { const testUsage = testBuffer.as('mutable'); const testSlot = tgpu['~unstable'].slot(testUsage); - const testFn = tgpu['~unstable'] - .fn([], d.f32)(() => { - const value = testSlot.value.value; - return value.x + value.y + value.z; - }); + const testFn = tgpu.fn([], d.f32)(() => { + const value = testSlot.value.value; + return value.x + value.y + value.z; + }); const astInfo = getMetaData( testFn[$internal].implementation as (...args: unknown[]) => unknown, @@ -892,18 +879,14 @@ describe('wgslGenerator', () => { }); it('throws error when incorrectly initializing function', () => { - const internalTestFn = tgpu['~unstable'] - .fn([d.vec2f], d.mat4x4f)(() => { - return d.mat4x4f(); - }) - .$name('internalTestFn'); - - const testFn = tgpu['~unstable'] - .fn([])(() => { - // @ts-expect-error - return internalTestFn([1, 23, 3]); - }) - .$name('testFn'); + const internalTestFn = tgpu.fn([d.vec2f], d.mat4x4f)(() => { + return d.mat4x4f(); + }); + + const testFn = tgpu.fn([])(() => { + // @ts-expect-error + return internalTestFn([1, 23, 3]); + }); expect(() => parseResolved({ cleantestFn: testFn })) .toThrowErrorMatchingInlineSnapshot(` @@ -916,34 +899,30 @@ describe('wgslGenerator', () => { }); it('throws error when initializing translate4 function', () => { - const testFn = tgpu['~unstable'] - .fn([], d.mat4x4f)(() => { - // @ts-expect-error - return std.translate4(); - }) - .$name('testTranslateError'); + const testFn = tgpu.fn([], d.mat4x4f)(() => { + // @ts-expect-error + return std.translate4(); + }); expect(() => parseResolved({ testFn })).toThrowErrorMatchingInlineSnapshot(` [Error: Resolution of the following tree failed: - -- fn:testTranslateError +- fn:testFn - translate4: Cannot read properties of undefined (reading 'value')] `); }); it('throws error when initializing vec4f with an array', () => { - const testFn = tgpu['~unstable'] - .fn([], d.mat4x4f)(() => { - // @ts-expect-error - const x = d.vec4f([1, 2, 3, 4]); - return d.mat4x4f(); - }) - .$name('testVec4fError'); + const testFn = tgpu.fn([], d.mat4x4f)(() => { + // @ts-expect-error + const x = d.vec4f([1, 2, 3, 4]); + return d.mat4x4f(); + }); expect(() => parseResolved({ testFn })).toThrowErrorMatchingInlineSnapshot(` [Error: Resolution of the following tree failed: - -- fn:testVec4fError +- fn:testFn - vec4f: Resolution of the following tree failed: - vec4f: Cannot convert argument of type 'array' to 'f32' for function vec4f] `); diff --git a/packages/typegpu/tests/tgslFn.test.ts b/packages/typegpu/tests/tgslFn.test.ts index 38b056d20..78f2dedb9 100644 --- a/packages/typegpu/tests/tgslFn.test.ts +++ b/packages/typegpu/tests/tgslFn.test.ts @@ -8,76 +8,57 @@ import { attest } from '@ark/attest'; describe('TGSL tgpu.fn function', () => { it('is namable', () => { - const getX = tgpu['~unstable'] - .fn([], d.f32)(() => { - return 3; - }) - .$name('get_x'); + const getX = tgpu.fn([], d.f32)(() => 3).$name('get_x'); expect(getName(getX)).toBe('get_x'); }); - it('resolves fn to WGSL', () => { - const getY = tgpu['~unstable'] - .fn([], d.f32)(() => { - return 3; - }) - .$name('getY'); + it('resolves to WGSL', () => { + const getY = tgpu.fn([], d.f32)(() => 3); - const actual = parseResolved({ getY }); - - const expected = parse(` + expect(parseResolved({ getY })).toBe(parse(` fn getY() -> f32 { return 3; - }`); - - expect(actual).toBe(expected); + }`)); }); it('resolves externals', () => { - const v = d.vec3f; // necessary workaround until we finish implementation of member access in the generator - const getColor = tgpu['~unstable'] - .fn([], d.vec3f)(() => { - const color = v(); - const color2 = v(1, 2, 3); - return color; - }) - .$uses({ v: d.vec3f }) - .$name('get_color'); - - const getX = tgpu['~unstable'] - .fn([], d.f32)(() => { - const color = getColor(); - return 3; - }) - .$name('get_x') + const getColor = tgpu.fn([], d.vec3f)(() => { + const color = d.vec3f(); + const color2 = d.vec3f(1, 2, 3); + return color; + }) + .$uses({ v: d.vec3f }); + + const getX = tgpu.fn([], d.f32)(() => { + const color = getColor(); + return 3; + }) .$uses({ getColor }); - const getY = tgpu['~unstable'] - .fn([], d.f32)(() => { - const c = getColor(); - return getX(); - }) - .$name('getY') + const getY = tgpu.fn([], d.f32)(() => { + const c = getColor(); + return getX(); + }) .$uses({ getX, getColor }); const actual = parseResolved({ getY }); const expected = parse(` - fn get_color() -> vec3f { + fn getColor() -> vec3f { var color = vec3f(); var color2 = vec3f(1, 2, 3); return color; } - fn get_x() -> f32 { - var color = get_color(); + fn getX() -> f32 { + var color = getColor(); return 3; } fn getY() -> f32 { - var c = get_color(); - return get_x(); + var c = getColor(); + return getX(); } `); @@ -90,11 +71,9 @@ describe('TGSL tgpu.fn function', () => { to: d.vec3f, }); - const createGradient = tgpu['~unstable'] - .fn([], Gradient)(() => { - return Gradient({ to: d.vec3f(1, 2, 3), from: d.vec3f(4, 5, 6) }); - }) - .$name('create_gradient'); + const createGradient = tgpu.fn([], Gradient)(() => { + return Gradient({ to: d.vec3f(1, 2, 3), from: d.vec3f(4, 5, 6) }); + }); const actual = parseResolved({ createGradient }); @@ -104,7 +83,7 @@ describe('TGSL tgpu.fn function', () => { to: vec3f, } - fn create_gradient() -> Gradient { + fn createGradient() -> Gradient { return Gradient(vec3f(4, 5, 6), vec3f(1, 2, 3)); } `); @@ -113,31 +92,23 @@ describe('TGSL tgpu.fn function', () => { }); it('resolves deeply nested structs', () => { - const A = d - .struct({ - b: d.f32, - }) - .$name('A'); + const A = d.struct({ + b: d.f32, + }); - const B = d - .struct({ - a: A, - c: d.f32, - }) - .$name('B'); + const B = d.struct({ + a: A, + c: d.f32, + }); - const C = d - .struct({ - b: B, - a: A, - }) - .$name('C'); + const C = d.struct({ + b: B, + a: A, + }); - const pureConfusion = tgpu['~unstable'] - .fn([], A)(() => { - return C({ a: A({ b: 3 }), b: B({ a: A({ b: 4 }), c: 5 }) }).a; - }) - .$name('pure_confusion'); + const pureConfusion = tgpu.fn([], A)(() => { + return C({ a: A({ b: 3 }), b: B({ a: A({ b: 4 }), c: 5 }) }).a; + }); const actual = parseResolved({ pureConfusion }); @@ -156,7 +127,7 @@ describe('TGSL tgpu.fn function', () => { a: A, } - fn pure_confusion() -> A { + fn pureConfusion() -> A { return C(B(A(4), 5), A(3)).a; } `); @@ -235,11 +206,10 @@ describe('TGSL tgpu.fn function', () => { it('throws when vertexFn with empty out', () => { expect(() => - tgpu['~unstable'] - .vertexFn({ - in: { vi: builtin.vertexIndex }, - out: {}, - }) + tgpu['~unstable'].vertexFn({ + in: { vi: builtin.vertexIndex }, + out: {}, + }) ).toThrowErrorMatchingInlineSnapshot( `[Error: A vertexFn output cannot be empty since it must include the 'position' builtin.]`, ); @@ -391,28 +361,27 @@ describe('TGSL tgpu.fn function', () => { } return out; - }) - .$name('fragment_fn'); + }); const actual = parseResolved({ fragmentFn }); const expected = parse(` - struct fragment_fn_Input { + struct fragmentFn_Input { @builtin(position) pos: vec4f, @location(0) uv: vec2f, @builtin(sample_mask) sampleMask: u32, } - struct fragment_fn_Output { + struct fragmentFn_Output { @builtin(sample_mask) sampleMask: u32, @builtin(frag_depth) fragDepth: f32, @location(0) out: vec4f, } @fragment - fn fragment_fn(input: fragment_fn_Input) -> fragment_fn_Output { + fn fragmentFn(input: fragmentFn_Input) -> fragmentFn_Output { var pos = input.pos; - var out = fragment_fn_Output(0, 1, vec4f(0, 0, 0, 0)); + var out = fragmentFn_Output(0, 1, vec4f(0, 0, 0, 0)); if (((input.sampleMask > 0) && (pos.x > 0))) { out.sampleMask = 1; } @@ -448,27 +417,26 @@ describe('TGSL tgpu.fn function', () => { } return out; - }) - .$name('fragment_fn'); + }); const actual = parseResolved({ fragmentFn }); const expected = parse(` - struct fragment_fn_Input { + struct fragmentFn_Input { @builtin(position) pos: vec4f, @location(0) uv: vec2f, @builtin(sample_mask) sampleMask: u32, } - struct fragment_fn_Output { + struct fragmentFn_Output { @builtin(sample_mask) sampleMask: u32, @builtin(frag_depth) fragDepth: f32, @location(0) out: vec4f, } @fragment - fn fragment_fn(_arg_0: fragment_fn_Input) -> fragment_fn_Output { - var out = fragment_fn_Output(0, 1, vec4f(0, 0, 0, 0)); + fn fragmentFn(_arg_0: fragmentFn_Input) -> fragmentFn_Output { + var out = fragmentFn_Output(0, 1, vec4f(0, 0, 0, 0)); if (((_arg_0.sampleMask > 0) && (_arg_0.pos.x > 0))) { out.sampleMask = 1; } @@ -484,18 +452,17 @@ describe('TGSL tgpu.fn function', () => { const fragmentFn = tgpu['~unstable'] .fragmentFn({ in: { pos: builtin.position }, out: d.vec4f })((input) => { return input.pos; - }) - .$name('fragment_fn'); + }); const actual = parseResolved({ fragmentFn }); const expected = parse(` - struct fragment_fn_Input { + struct fragmentFn_Input { @builtin(position) pos: vec4f, } @fragment - fn fragment_fn(input: fragment_fn_Input) -> @location(0) vec4f { + fn fragmentFn(input: fragmentFn_Input) -> @location(0) vec4f { return input.pos; } `); @@ -509,20 +476,17 @@ describe('TGSL tgpu.fn function', () => { a: d.f32, b: d.f32, c: d.vec2f, - }) - .$name('TestStruct'); + }); - const fn = tgpu['~unstable'] - .fn([], TestStruct)(() => { - return { - a: 1, - b: 2, - c: d.vec2f(3, 4), - }; - }) - .$name('test_struct'); + const getTestStruct = tgpu.fn([], TestStruct)(() => { + return { + a: 1, + b: 2, + c: d.vec2f(3, 4), + }; + }); - const actual = parseResolved({ fn }); + const actual = parseResolved({ getTestStruct }); const expected = parse(` struct TestStruct { @@ -531,7 +495,7 @@ describe('TGSL tgpu.fn function', () => { c: vec2f, } - fn test_struct() -> TestStruct { + fn getTestStruct() -> TestStruct { return TestStruct(1, 2, vec2f(3, 4)); } `); @@ -545,25 +509,22 @@ describe('TGSL tgpu.fn function', () => { a: d.f32, b: d.f32, c: d.vec2f, - }) - .$name('TestStruct'); + }); - const fn = tgpu['~unstable'] - .fn([], TestStruct)(() => { - return { - a: 1, - b: 2, - c: d.vec2f(3, 4), - }; - }) - .$name('test_struct'); + const getTestStruct = tgpu.fn([], TestStruct)(() => { + return { + a: 1, + b: 2, + c: d.vec2f(3, 4), + }; + }); const fn2 = tgpu['~unstable'] .computeFn({ in: { gid: builtin.globalInvocationId }, workgroupSize: [24], })((input) => { - const testStruct = fn(); + const testStruct = getTestStruct(); }) .$name('compute_fn'); @@ -580,13 +541,13 @@ describe('TGSL tgpu.fn function', () => { c: vec2f, } - fn test_struct() -> TestStruct { + fn getTestStruct() -> TestStruct { return TestStruct(1, 2, vec2f(3, 4)); } @compute @workgroup_size(24) fn compute_fn(input: compute_fn_Input) { - var testStruct = test_struct(); + var testStruct = getTestStruct(); } `); @@ -594,7 +555,7 @@ describe('TGSL tgpu.fn function', () => { }); it('resolves its header based on the shell, not AST, allowing passing function accepting a subset of arguments', () => { - const foo = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)((a) => a); + const foo = tgpu.fn([d.u32, d.u32], d.u32)((a) => a); expect(parseResolved({ foo })).toBe( parse(`fn foo(a: u32, _arg_1: u32) -> u32 { @@ -604,7 +565,7 @@ describe('TGSL tgpu.fn function', () => { }); it('resolves its header based on the shell, not AST, allowing passing function with no arguments', () => { - const foo = tgpu['~unstable'].fn([d.u32, d.u32], d.u32)(() => 2); + const foo = tgpu.fn([d.u32, d.u32], d.u32)(() => 2); expect(parseResolved({ foo })).toBe( parse(`fn foo(_arg_0: u32, _arg_1: u32) -> u32 { @@ -620,7 +581,7 @@ describe('TGSL tgpu.fn function', () => { return x + y; }; - const add = tgpu['~unstable'].fn([d.u32, d.u32])(addKernel); + const add = tgpu.fn([d.u32, d.u32])(addKernel); expect(() => addKernel(2, 3)).toThrow( 'The function "addKernel" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.', @@ -641,7 +602,7 @@ describe('TGSL tgpu.fn function', () => { return x + y; }; - const add = tgpu['~unstable'].fn([d.u32, d.u32])(addKernelJs); + const add = tgpu.fn([d.u32, d.u32])(addKernelJs); expect(addKernelJs(2, 3)).toBe(5); expect(add(2, 3)).toBe(5); @@ -653,7 +614,7 @@ describe('TGSL tgpu.fn function', () => { }); it('cannot be invoked for inline function with "kernel" directive', () => { - const add = tgpu['~unstable'].fn([d.u32, d.u32])((x, y) => { + const add = tgpu.fn([d.u32, d.u32])((x, y) => { 'kernel'; return x + y; }); @@ -667,7 +628,7 @@ describe('TGSL tgpu.fn function', () => { }); it('cannot be invoked for inline function with no directive', () => { - const add = tgpu['~unstable'].fn([d.u32, d.u32])( + const add = tgpu.fn([d.u32, d.u32])( (x, y) => x + y, ); @@ -680,7 +641,7 @@ describe('TGSL tgpu.fn function', () => { }); it('can be invoked for inline function with "kernel & js" directive', () => { - const add = tgpu['~unstable'].fn([d.u32, d.u32])((x, y) => { + const add = tgpu.fn([d.u32, d.u32])((x, y) => { 'kernel & js'; return x + y; }); @@ -695,13 +656,11 @@ describe('TGSL tgpu.fn function', () => { }); it('resolves a function with a pointer parameter', () => { - const addOnes = tgpu['~unstable'].fn([d.ptrStorage(d.vec3f, 'read-write')])( - (ptr) => { - ptr.x += 1; - ptr.y += 1; - ptr.z += 1; - }, - ); + const addOnes = tgpu.fn([d.ptrStorage(d.vec3f, 'read-write')])((ptr) => { + ptr.x += 1; + ptr.y += 1; + ptr.z += 1; + }); const actual = parseResolved({ addOnes }); @@ -715,7 +674,7 @@ describe('TGSL tgpu.fn function', () => { expect(actual).toEqual(expected); - const callAddOnes = tgpu['~unstable'].fn([])(() => { + const callAddOnes = tgpu.fn([])(() => { const someVec = d.vec3f(1, 2, 3); addOnes(someVec); }); @@ -743,10 +702,9 @@ describe('TGSL tgpu.fn function', () => { value: d.i32, }).$name('Input'); - const fun = tgpu['~unstable'] - .fn([Input])(({ value }) => { - const vector = d.vec2u(value); - }); + const fun = tgpu.fn([Input])(({ value }) => { + const vector = d.vec2u(value); + }); const actual = parseResolved({ fun }); @@ -766,12 +724,11 @@ describe('TGSL tgpu.fn function', () => { it('correctly coerces type of input arguments', () => { const Input = d.struct({ value: d.i32, - }).$name('Input'); + }); - const fun = tgpu['~unstable'] - .fn([Input])((input) => { - const vector = d.vec2u(input.value); - }); + const fun = tgpu.fn([Input])((input) => { + const vector = d.vec2u(input.value); + }); const actual = parseResolved({ fun }); @@ -791,12 +748,11 @@ describe('TGSL tgpu.fn function', () => { it('correctly coerces type of destructured aliased input arguments', () => { const Input = d.struct({ value: d.i32, - }).$name('Input'); + }); - const fun = tgpu['~unstable'] - .fn([Input])(({ value: v }) => { - const vector = d.vec2u(v); - }); + const fun = tgpu.fn([Input])(({ value: v }) => { + const vector = d.vec2u(v); + }); const actual = parseResolved({ fun }); @@ -816,12 +772,11 @@ describe('TGSL tgpu.fn function', () => { it('allows destructuring any struct argument', () => { const Input = d.struct({ value: d.i32, - }).$name('Input'); + }); - const fun = tgpu['~unstable'] - .fn([Input, d.i32, Input])(({ value: v }, x, { value }) => { - const vector = d.vec3u(v, x, value); - }); + const fun = tgpu.fn([Input, d.i32, Input])(({ value: v }, x, { value }) => { + const vector = d.vec3u(v, x, value); + }); const actual = parseResolved({ fun }); @@ -839,7 +794,7 @@ describe('TGSL tgpu.fn function', () => { }); it('maintains argument names in the type', () => { - const fun = tgpu['~unstable'].fn([d.f32, d.f32], d.f32)((x, y) => { + const fun = tgpu.fn([d.f32, d.f32], d.f32)((x, y) => { return x + y; }); @@ -847,7 +802,7 @@ describe('TGSL tgpu.fn function', () => { }); it('falls back to args_N naming when not every argument is used in the implementation', () => { - const fun = tgpu['~unstable'].fn([d.f32, d.f32], d.f32)((x) => { + const fun = tgpu.fn([d.f32, d.f32], d.f32)((x) => { return x * 2; }); diff --git a/packages/typegpu/tests/unplugin/autoname.test.ts b/packages/typegpu/tests/unplugin/autoname.test.ts index d2bdd2196..a57d7c766 100644 --- a/packages/typegpu/tests/unplugin/autoname.test.ts +++ b/packages/typegpu/tests/unplugin/autoname.test.ts @@ -79,7 +79,7 @@ describe('autonaming', () => { .$usage('storage') .$addFlags(GPUBufferUsage.STORAGE); const Item = d.struct({ a: d.u32 }); - const myFn = tgpu['~unstable'].fn( + const myFn = tgpu.fn( [Item], Item, ) /* wgsl */`(item: Item) -> Item { return item; }` @@ -95,14 +95,14 @@ describe('autonaming', () => { it('does not rename already named resources', () => { const myStruct = d.struct({ a: d.u32 }).$name('IntStruct'); - const myFunction = tgpu['~unstable'].fn([])(() => 0).$name('ConstFunction'); + const myFunction = tgpu.fn([])(() => 0).$name('ConstFunction'); expect(getName(myStruct)).toBe('IntStruct'); expect(getName(myFunction)).toBe('ConstFunction'); }); it('names TGPU functions', () => { - const myFunction = tgpu['~unstable'].fn([])(() => 0); + const myFunction = tgpu.fn([])(() => 0); const myComputeFn = tgpu['~unstable'].computeFn({ workgroupSize: [1] })( () => {}, ); @@ -129,7 +129,7 @@ describe('autonaming', () => { // return 0; // }; - // const myGpuFun = tgpu['~unstable'].fn([], d.u32)(myFun); + // const myGpuFun = tgpu.fn([], d.u32)(myFun); // expect(getName(myFun)).toBe('myFun'); // expect(getName(myGpuFun)).toBe('myGpuFun'); @@ -142,7 +142,7 @@ describe('autonaming', () => { // return 0; // }; - // const myGpuFun = tgpu['~unstable'].fn([], d.u32)(myFun); + // const myGpuFun = tgpu.fn([], d.u32)(myFun); // expect(getName(myFun)).toBe('myFun'); // expect(getName(myGpuFun)).toBe('myGpuFun'); @@ -155,7 +155,7 @@ describe('autonaming', () => { // return 0; // } - // const myGpuFun = tgpu['~unstable'].fn([], d.u32)(myFun); + // const myGpuFun = tgpu.fn([], d.u32)(myFun); // expect(getName(myFun)).toBe('myFun'); // expect(getName(myGpuFun)).toBe('myGpuFun'); diff --git a/packages/typegpu/tests/variable.test.ts b/packages/typegpu/tests/variable.test.ts index ed456d1eb..bcdb43c36 100644 --- a/packages/typegpu/tests/variable.test.ts +++ b/packages/typegpu/tests/variable.test.ts @@ -10,13 +10,11 @@ import { parse, parseResolved } from './utils/parseResolved.ts'; describe('var', () => { it('should inject variable declaration when used in functions', () => { const x = tgpu['~unstable'].privateVar(d.u32, 2); - const fn1 = tgpu['~unstable'] - .fn([])(`() { + const fn1 = tgpu.fn([])`() { let y = x; return x; - }`) - .$uses({ x }) - .$name('fn1'); + }` + .$uses({ x }); expect(parseResolved({ fn1 })).toBe( parse(` @@ -101,25 +99,21 @@ describe('var', () => { ); }); - 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([])(() => { - const pos = boidVariable.value; - const vel = boidVariable.value.vel; - const velX = boidVariable.value.vel.x; + it('allows accessing variables in TGSL through .value', () => { + const Boid = d.struct({ + pos: d.vec3f, + vel: d.vec3u, + }); + + const boid = tgpu['~unstable'].privateVar(Boid, { + pos: d.vec3f(1, 2, 3), + vel: d.vec3u(4, 5, 6), + }); + + const func = tgpu.fn([])(() => { + const pos = boid.value; + const vel = boid.value.vel; + const velX = boid.value.vel.x; }); const resolved = tgpu.resolve({ diff --git a/packages/typegpu/tests/vector.test.ts b/packages/typegpu/tests/vector.test.ts index e44a3f7fa..aa328da94 100644 --- a/packages/typegpu/tests/vector.test.ts +++ b/packages/typegpu/tests/vector.test.ts @@ -790,15 +790,13 @@ describe('v3f', () => { it('works in TGSL', () => { const planarPos = d.vec2f(1, 2); - const main = tgpu['~unstable'] - .fn([])(() => { - const planarPosLocal = d.vec2f(1, 2); + const main = tgpu.fn([])(() => { + const planarPosLocal = d.vec2f(1, 2); - const one = d.vec3f(planarPos, 12); // external - const two = d.vec3f(planarPosLocal, 12); // local variable - const three = d.vec3f(d.vec2f(1, 2), 12); // literal - }) - .$name('main'); + const one = d.vec3f(planarPos, 12); // external + const two = d.vec3f(planarPosLocal, 12); // local variable + const three = d.vec3f(d.vec2f(1, 2), 12); // literal + }); expect(parseResolved({ main })).toBe( parse(` @@ -842,15 +840,13 @@ describe('v4f', () => { it('works in TGSL', () => { const red = d.vec3f(0.9, 0.2, 0.1); - const main = tgpu['~unstable'] - .fn([])(() => { - const green = d.vec3f(0, 1, 0); + const main = tgpu.fn([])(() => { + const green = d.vec3f(0, 1, 0); - const one = d.vec4f(red, 1); // external - const two = d.vec4f(green, 1); // local variable - const three = d.vec4f(d.vec3f(0, 0, 1), 1); // literal - }) - .$name('main'); + const one = d.vec4f(red, 1); // external + const two = d.vec4f(green, 1); // local variable + const three = d.vec4f(d.vec3f(0, 0, 1), 1); // literal + }); expect(parseResolved({ main })).toBe( parse(` @@ -876,15 +872,13 @@ describe('v4f', () => { it('works in TGSL', () => { const foo = d.vec3f(0.2, 0.3, 0.4); - const main = tgpu['~unstable'] - .fn([])(() => { - const fooLocal = d.vec3f(0.2, 0.3, 0.4); + const main = tgpu.fn([])(() => { + const fooLocal = d.vec3f(0.2, 0.3, 0.4); - const one = d.vec4f(0.1, foo); // external - const two = d.vec4f(0.1, fooLocal); // local variable - const three = d.vec4f(0.1, d.vec3f(0.2, 0.3, 0.4)); // literal - }) - .$name('main'); + const one = d.vec4f(0.1, foo); // external + const two = d.vec4f(0.1, fooLocal); // local variable + const three = d.vec4f(0.1, d.vec3f(0.2, 0.3, 0.4)); // literal + }); expect(parseResolved({ main })).toBe( parse(` @@ -919,16 +913,13 @@ describe('v4b', () => { it('works in TGSL', () => { const vecExternal = d.vec3b(true, false, true); - const main = tgpu['~unstable'] - .fn([]) - .does(() => { - const vecLocal = d.vec3b(true, true, true); + const main = tgpu.fn([])(() => { + const vecLocal = d.vec3b(true, true, true); - const one = d.vec4b(vecExternal, true); // external - const two = d.vec4b(vecLocal, false); // local variable - const three = d.vec4b(d.vec3b(false, false, true), true); // literal - }) - .$name('main'); + const one = d.vec4b(vecExternal, true); // external + const two = d.vec4b(vecLocal, false); // local variable + const three = d.vec4b(d.vec3b(false, false, true), true); // literal + }); expect(parseResolved({ main })).toBe( parse(` diff --git a/packages/unplugin-typegpu/src/common.ts b/packages/unplugin-typegpu/src/common.ts index cacd784a0..88beb75ba 100644 --- a/packages/unplugin-typegpu/src/common.ts +++ b/packages/unplugin-typegpu/src/common.ts @@ -41,6 +41,16 @@ function isTgpu(ctx: Context, node: babel.Node | acorn.AnyNode): boolean { let tail = node; while (true) { if (tail.type === 'MemberExpression') { + if ( + (tail.property.type === 'Literal' || + tail.property.type === 'StringLiteral') && + tail.property.value === '~unstable' + ) { + // Bypassing the '~unstable' property. + tail = tail.object; + continue; + } + if (tail.property.type !== 'Identifier') { // Not handling computed expressions. break; @@ -89,21 +99,11 @@ export function isShellImplementationCall( ctx: Context, ) { return ( - (node.callee.type === 'CallExpression' && - node.callee.callee.type === 'MemberExpression' && - node.callee.callee.property.type === 'Identifier' && - fnShellFunctionNames.includes(node.callee.callee.property.name) && - node.arguments.length === 1 && - (node.callee.callee.object.type === 'MemberExpression' - ? isTgpu(ctx, node.callee.callee.object.object) - : isTgpu(ctx, node.callee.callee.object))) || // TODO: remove along with the deprecated 'does' method - (node.callee.type === 'MemberExpression' && - node.arguments.length === 1 && - node.callee.property.type === 'Identifier' && - // Assuming that every call to `.does` is related to TypeGPU - // because shells can be created separately from calls to `tgpu`, - // making it hard to detect. - node.callee.property.name === 'does') + node.callee.type === 'CallExpression' && + node.callee.callee.type === 'MemberExpression' && + node.callee.callee.property.type === 'Identifier' && + fnShellFunctionNames.includes(node.callee.callee.property.name) && + node.arguments.length === 1 && isTgpu(ctx, node.callee.callee.object) ); } diff --git a/packages/unplugin-typegpu/test/aliasing.test.ts b/packages/unplugin-typegpu/test/aliasing.test.ts index f9dc109fc..c06faba6c 100644 --- a/packages/unplugin-typegpu/test/aliasing.test.ts +++ b/packages/unplugin-typegpu/test/aliasing.test.ts @@ -6,15 +6,14 @@ describe('[BABEL] tgpu alias gathering', () => { const code = `\ import hello from 'typegpu'; - const increment = hello['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = hello.fn([])(() => { + const x = 2+2; + }); `; expect(babelTransform(code)).toMatchInlineSnapshot(` "import hello from 'typegpu'; - const increment = hello['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { + const increment = hello.fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { v: 1, @@ -28,15 +27,14 @@ describe('[BABEL] tgpu alias gathering', () => { const code = `\ import { tgpu as t } from 'typegpu'; - const increment = t['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = t.fn([])(() => { + const x = 2+2; + }); `; expect(babelTransform(code)).toMatchInlineSnapshot(` "import { tgpu as t } from 'typegpu'; - const increment = t['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { + const increment = t.fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { v: 1, @@ -50,15 +48,14 @@ describe('[BABEL] tgpu alias gathering', () => { const code = `\ import * as t from 'typegpu'; - const increment = t.tgpu['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = t.tgpu.fn([])(() => { + const x = 2+2; + }); `; expect(babelTransform(code)).toMatchInlineSnapshot(` "import * as t from 'typegpu'; - const increment = t.tgpu['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { + const increment = t.tgpu.fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { v: 1, @@ -74,17 +71,15 @@ describe('[ROLLUP] tgpu alias gathering', () => { const code = `\ import hello from 'typegpu'; - const increment = hello['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = hello.fn([])(() => { + const x = 2+2; + }); `; expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import hello from 'typegpu'; - hello['~unstable'] - .fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { + hello.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); }), { v: 1, @@ -99,18 +94,16 @@ describe('[ROLLUP] tgpu alias gathering', () => { const code = `\ import { tgpu as t } from 'typegpu'; - const increment = t['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = t.fn([])(() => { + const x = 2+2; + }); `; // aliasing removed by rollup, but technically it works expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import { tgpu } from 'typegpu'; - tgpu['~unstable'] - .fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { + tgpu.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); }), { v: 1, @@ -122,20 +115,19 @@ describe('[ROLLUP] tgpu alias gathering', () => { }); it('works with namespace import', async () => { + // TODO: Oh ohh, this breaks for some reason :( const code = `\ import * as t from 'typegpu'; - const increment = t.tgpu['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = t.tgpu.fn([])(() => { + const x = 2+2; + }); `; expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import * as t from 'typegpu'; - t.tgpu['~unstable'] - .fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { + t.tgpu.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); }), { v: 1, diff --git a/packages/unplugin-typegpu/test/auto-naming.test.ts b/packages/unplugin-typegpu/test/auto-naming.test.ts index a5dba5f72..a330182de 100644 --- a/packages/unplugin-typegpu/test/auto-naming.test.ts +++ b/packages/unplugin-typegpu/test/auto-naming.test.ts @@ -9,6 +9,8 @@ describe('[BABEL] auto naming', () => { const bindGroupLayout = tgpu.bindGroupLayout({}); const vertexLayout = tgpu.vertexLayout((n) => d.arrayOf(d.u32, n)); + var fn = tgpu.fn([])(() => {}); + let shell = tgpu.fn([]); console.log(bindGroupLayout, vertexLayout); `; @@ -19,6 +21,14 @@ describe('[BABEL] auto naming', () => { import * as d from 'typegpu/data'; const bindGroupLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.bindGroupLayout({}), "bindGroupLayout"); const vertexLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexLayout(n => d.arrayOf(d.u32, n)), "vertexLayout"); + var fn = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { + throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); + }, { + v: 1, + ast: {"params":[],"body":[0,[]],"externalNames":[]}, + externals: {}, + }) && $.f)({})), "fn"); + let shell = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([]), "shell"); console.log(bindGroupLayout, vertexLayout);" `); }); @@ -29,8 +39,6 @@ describe('[BABEL] auto naming', () => { import * as d from 'typegpu/data'; let nothing, accessor = tgpu['~unstable'].accessor(d.u32); - let shell = tgpu['~unstable'].fn([]); - var fn = tgpu['~unstable'].fn([])(() => {}); const cst = tgpu['~unstable'].const(d.u32, 1); console.log(accessor, shell, fn, cst); @@ -42,14 +50,6 @@ describe('[BABEL] auto naming', () => { import * as d from 'typegpu/data'; let nothing, accessor = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].accessor(d.u32), "accessor"); - let shell = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([]), "shell"); - var fn = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { - throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); - }, { - v: 1, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: {}, - }) && $.f)({})), "fn"); const cst = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].const(d.u32, 1), "cst"); console.log(accessor, shell, fn, cst);" `); @@ -106,7 +106,7 @@ describe('[BABEL] auto naming', () => { import tgpu from 'typegpu'; import * as d from 'typegpu/data'; - const myFunction = tgpu['~unstable'].fn([])(() => 0); + const myFunction = tgpu.fn([])(() => 0); const myComputeFn = tgpu['~unstable'].computeFn({ workgroupSize: [1] })( () => {}, ); @@ -125,7 +125,7 @@ describe('[BABEL] auto naming', () => { .toMatchInlineSnapshot(` "import tgpu from 'typegpu'; import * as d from 'typegpu/data'; - const myFunction = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { + const myFunction = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { v: 1, @@ -173,7 +173,7 @@ describe('[BABEL] auto naming', () => { .$usage('storage') .$addFlags(GPUBufferUsage.STORAGE); const Item = d.struct({ a: d.u32 }); - const myFn = tgpu['~unstable'].fn( + const myFn = tgpu.fn( [Item], Item, ) /* wgsl */\`(item: Item) -> Item { return item; }\` @@ -189,7 +189,7 @@ describe('[BABEL] auto naming', () => { const Item = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(d.struct({ a: d.u32 }), "Item"); - const myFn = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([Item], Item) /* wgsl */\`(item: Item) -> Item { return item; }\`.$uses({ + const myFn = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([Item], Item) /* wgsl */\`(item: Item) -> Item { return item; }\`.$uses({ Item }), "myFn"); const myLayout = (globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.bindGroupLayout({ @@ -256,6 +256,8 @@ describe('[ROLLUP] auto naming', () => { const bindGroupLayout = tgpu.bindGroupLayout({}); const vertexLayout = tgpu.vertexLayout((n) => d.arrayOf(d.u32, n)); + let shell = tgpu.fn([]); + var fn = tgpu.fn([])(() => {}); console.log(bindGroupLayout, vertexLayout); `; @@ -267,6 +269,14 @@ describe('[ROLLUP] auto naming', () => { const bindGroupLayout = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.bindGroupLayout({}), "bindGroupLayout")); const vertexLayout = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.vertexLayout((n) => d.arrayOf(d.u32, n)), "vertexLayout")); + ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([]), "shell")); + ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { + throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); + }), { + v: 1, + ast: {"params":[],"body":[0,[]],"externalNames":[]}, + externals: {}, + }) && $.f)({}))), "fn")); console.log(bindGroupLayout, vertexLayout); " @@ -279,8 +289,6 @@ describe('[ROLLUP] auto naming', () => { import * as d from 'typegpu/data'; let nothing, accessor = tgpu['~unstable'].accessor(d.u32); - let shell = tgpu['~unstable'].fn([]); - var fn = tgpu['~unstable'].fn([])(() => {}); const cst = tgpu['~unstable'].const(d.u32, 1); console.log(accessor, shell, fn, cst); @@ -292,14 +300,6 @@ describe('[ROLLUP] auto naming', () => { import * as d from 'typegpu/data'; let accessor = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].accessor(d.u32), "accessor")); - let shell = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([]), "shell")); - var fn = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { - throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); - }), { - v: 1, - ast: {"params":[],"body":[0,[]],"externalNames":[]}, - externals: {}, - }) && $.f)({}))), "fn")); const cst = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].const(d.u32, 1), "cst")); console.log(accessor, shell, fn, cst); @@ -360,7 +360,7 @@ describe('[ROLLUP] auto naming', () => { import tgpu from 'typegpu'; import * as d from 'typegpu/data'; - const myFunction = tgpu['~unstable'].fn([])(() => 0); + const myFunction = tgpu.fn([])(() => 0); const myComputeFn = tgpu['~unstable'].computeFn({ workgroupSize: [1] })( () => {}, ); @@ -380,7 +380,7 @@ describe('[ROLLUP] auto naming', () => { "import tgpu from 'typegpu'; import * as d from 'typegpu/data'; - ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { + ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); }), { v: 1, @@ -427,7 +427,7 @@ describe('[ROLLUP] auto naming', () => { .$usage('storage') .$addFlags(GPUBufferUsage.STORAGE); const Item = d.struct({ a: d.u32 }); - const myFn = tgpu['~unstable'].fn( + const myFn = tgpu.fn( [Item], Item, ) /* wgsl */\`(item: Item) -> Item { return item; }\` @@ -443,7 +443,7 @@ describe('[ROLLUP] auto naming', () => { .$usage('storage') .$addFlags(GPUBufferUsage.STORAGE), "myBuffer")); const Item = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(d.struct({ a: d.u32 }), "Item")); - ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu['~unstable'].fn( + ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.fn( [Item], Item, ) /* wgsl */\`(item: Item) -> Item { return item; }\` diff --git a/packages/unplugin-typegpu/test/kernel-and-js-directive.test.ts b/packages/unplugin-typegpu/test/kernel-and-js-directive.test.ts index f728e9916..ce410600f 100644 --- a/packages/unplugin-typegpu/test/kernel-and-js-directive.test.ts +++ b/packages/unplugin-typegpu/test/kernel-and-js-directive.test.ts @@ -37,7 +37,7 @@ describe('[BABEL] "kernel & js" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((a, b) => { 'kernel & js'; @@ -51,7 +51,7 @@ describe('[BABEL] "kernel & js" directive', () => { expect(babelTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (a, b) => { 'kernel & js'; @@ -71,7 +71,7 @@ describe('[BABEL] "kernel & js" directive', () => { const code = `\ import tgpu from 'typegpu'; - tgpu['~unstable'].fn([])((a, b) => { + tgpu.fn([])((a, b) => { 'kernel & js'; return a + b; }) @@ -79,7 +79,7 @@ describe('[BABEL] "kernel & js" directive', () => { expect(babelTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - tgpu['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (a, b) => { + tgpu.fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (a, b) => { 'kernel & js'; return a + b; @@ -95,7 +95,7 @@ describe('[BABEL] "kernel & js" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(function(a, b){ 'kernel & js'; @@ -109,7 +109,7 @@ describe('[BABEL] "kernel & js" directive', () => { expect(babelTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = function (a, b) { 'kernel & js'; @@ -129,7 +129,7 @@ describe('[BABEL] "kernel & js" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(function addGPU(a, b){ 'kernel & js'; @@ -143,7 +143,7 @@ describe('[BABEL] "kernel & js" directive', () => { expect(babelTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = function addGPU(a, b) { 'kernel & js'; @@ -284,7 +284,7 @@ describe('[ROLLUP] "kernel & js" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((a, b) => { 'kernel & js'; @@ -299,7 +299,7 @@ describe('[ROLLUP] "kernel & js" directive', () => { expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((a, b) => { 'kernel & js'; @@ -321,7 +321,7 @@ describe('[ROLLUP] "kernel & js" directive', () => { const code = `\ import tgpu from 'typegpu'; - tgpu['~unstable'].fn([])((a, b) => { + tgpu.fn([])((a, b) => { 'kernel & js'; return a + b; }) @@ -330,7 +330,7 @@ describe('[ROLLUP] "kernel & js" directive', () => { expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((a, b) => { + tgpu.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((a, b) => { 'kernel & js'; return a + b; }), { @@ -346,7 +346,7 @@ describe('[ROLLUP] "kernel & js" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(function(a, b){ 'kernel & js'; @@ -360,7 +360,7 @@ describe('[ROLLUP] "kernel & js" directive', () => { expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function(a, b){ 'kernel & js'; @@ -382,7 +382,7 @@ describe('[ROLLUP] "kernel & js" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(function addGPU(a, b){ 'kernel & js'; @@ -397,7 +397,7 @@ describe('[ROLLUP] "kernel & js" directive', () => { expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function addGPU(a, b){ 'kernel & js'; diff --git a/packages/unplugin-typegpu/test/kernel-directive.test.ts b/packages/unplugin-typegpu/test/kernel-directive.test.ts index 304666733..9ec3c935c 100644 --- a/packages/unplugin-typegpu/test/kernel-directive.test.ts +++ b/packages/unplugin-typegpu/test/kernel-directive.test.ts @@ -35,7 +35,7 @@ describe('[BABEL] "kernel" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((a, b) => { 'kernel'; @@ -49,7 +49,7 @@ describe('[BABEL] "kernel" directive', () => { expect(babelTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { @@ -67,7 +67,7 @@ describe('[BABEL] "kernel" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(function(a, b){ 'kernel'; @@ -81,7 +81,7 @@ describe('[BABEL] "kernel" directive', () => { expect(babelTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { @@ -99,7 +99,7 @@ describe('[BABEL] "kernel" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(function addGPU(a, b){ 'kernel'; @@ -113,7 +113,7 @@ describe('[BABEL] "kernel" directive', () => { expect(babelTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"addGPU\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { @@ -202,7 +202,7 @@ describe('[ROLLUP] "kernel" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((a, b) => { 'kernel'; @@ -217,7 +217,7 @@ describe('[ROLLUP] "kernel" directive', () => { expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); @@ -238,7 +238,7 @@ describe('[ROLLUP] "kernel" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(function(a, b){ 'kernel'; @@ -252,7 +252,7 @@ describe('[ROLLUP] "kernel" directive', () => { expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); @@ -273,7 +273,7 @@ describe('[ROLLUP] "kernel" directive', () => { const code = `\ import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell(function addGPU(a, b){ 'kernel'; @@ -288,7 +288,7 @@ describe('[ROLLUP] "kernel" directive', () => { expect(await rollupTransform(code)).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const shell = tgpu['~unstable'].fn([]); + const shell = tgpu.fn([]); shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "addGPU" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); diff --git a/packages/unplugin-typegpu/test/parser-options.test.ts b/packages/unplugin-typegpu/test/parser-options.test.ts index 1a15482aa..de4592162 100644 --- a/packages/unplugin-typegpu/test/parser-options.test.ts +++ b/packages/unplugin-typegpu/test/parser-options.test.ts @@ -6,17 +6,16 @@ describe('[BABEL] parser options', () => { const codeWithImport = `\ import tgpu from 'typegpu'; - const increment = tgpu['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = tgpu.fn([])(() => { + const x = 2+2; + }); `; expect( babelTransform(codeWithImport, { include: [/virtual:/] }), ).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const increment = tgpu['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { + const increment = tgpu.fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { v: 1, @@ -26,16 +25,15 @@ describe('[BABEL] parser options', () => { `); const codeWithoutImport = `\ - const increment = tgpu['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = tgpu.fn([])(() => { + const x = 2+2; + }); `; expect( babelTransform(codeWithoutImport, { include: [/virtual:/] }), ).toMatchInlineSnapshot(` - "const increment = tgpu['~unstable'].fn([])(() => { + "const increment = tgpu.fn([])(() => { const x = 2 + 2; });" `); @@ -47,10 +45,9 @@ describe('[ROLLUP] tgpu alias gathering', async () => { const codeWithImport = `\ import tgpu from 'typegpu'; - const increment = tgpu['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = tgpu.fn([])(() => { + const x = 2+2; + }); console.log(increment); `; @@ -60,8 +57,7 @@ describe('[ROLLUP] tgpu alias gathering', async () => { ).toMatchInlineSnapshot(` "import tgpu from 'typegpu'; - const increment = tgpu['~unstable'] - .fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { + const increment = tgpu.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); }), { v: 1, @@ -74,10 +70,9 @@ describe('[ROLLUP] tgpu alias gathering', async () => { `); const codeWithoutImport = `\ - const increment = tgpu['~unstable'] - .fn([])(() => { - const x = 2+2; - }); + const increment = tgpu.fn([])(() => { + const x = 2+2; + }); console.log(increment); `; @@ -85,9 +80,8 @@ describe('[ROLLUP] tgpu alias gathering', async () => { expect( await rollupTransform(codeWithoutImport, { include: [/virtual:/] }), ).toMatchInlineSnapshot(` - "const increment = tgpu['~unstable'] - .fn([])(() => { - }); + "const increment = tgpu.fn([])(() => { + }); console.log(increment); " diff --git a/packages/unplugin-typegpu/test/tgsl-transpiling-with-does.test.ts b/packages/unplugin-typegpu/test/tgsl-transpiling-with-does.test.ts deleted file mode 100644 index e38e7b748..000000000 --- a/packages/unplugin-typegpu/test/tgsl-transpiling-with-does.test.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { babelTransform, rollupTransform } from './transform.ts'; - -// TODO: remove along with the deprecated 'does' method. - -describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { - it('wraps argument passed to does with globalThis set call', () => { - const code = `\ - import tgpu from 'typegpu'; - import * as d from 'typegpu/data'; - - const counterBuffer = root - .createBuffer(d.vec3f, d.vec3f(0, 1, 0)) - .$usage('storage'); - const counter = counterBuffer.as('mutable'); - - const increment = tgpu['~unstable'] - .computeFn({ in: { num: d.builtin.numWorkgroups }, workgroupSize: [1] }) - .does((input) => { - const tmp = counter.value.x; - counter.value.x = counter.value.y; - counter.value.y += tmp; - counter.value.z += d.f32(input.num.x); - }); - `; - - expect(babelTransform(code)).toMatchInlineSnapshot(` - "import tgpu from 'typegpu'; - import * as d from 'typegpu/data'; - const counterBuffer = root.createBuffer(d.vec3f, d.vec3f(0, 1, 0)).$usage('storage'); - const counter = counterBuffer.as('mutable'); - const increment = tgpu['~unstable'].computeFn({ - in: { - num: d.builtin.numWorkgroups - }, - workgroupSize: [1] - }).does(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { - throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); - }, { - v: 1, - ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"tmp",[7,[7,"counter","value"],"x"]],[2,[7,[7,"counter","value"],"x"],"=",[7,[7,"counter","value"],"y"]],[2,[7,[7,"counter","value"],"y"],"+=","tmp"],[2,[7,[7,"counter","value"],"z"],"+=",[6,[7,"d","f32"],[[7,[7,"input","num"],"x"]]]]]],"externalNames":["counter","d"]}, - externals: {counter, d}, - }) && $.f)({}));" - `); - }); - - it('works for multiple functions, skips wgsl-implemented', () => { - const code = `\ - import tgpu from 'typegpu'; - - const a = tgpu['~unstable'].computeFn({ workgroupSize: [1] }).does((input) => { - const x = true; - }); - - const b = tgpu['~unstable'].fn([]).does(() => { - const y = 2 + 2; - }); - - const cx = 2; - const c = tgpu['~unstable'].fn([]).does(() => cx); - - const d = tgpu['~unstable'].fn([]).does('() {}'); - `; - - expect(babelTransform(code)).toMatchInlineSnapshot(` - "import tgpu from 'typegpu'; - const a = tgpu['~unstable'].computeFn({ - workgroupSize: [1] - }).does(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { - throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); - }, { - v: 1, - ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":[]}, - externals: {}, - }) && $.f)({})); - const b = tgpu['~unstable'].fn([]).does(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { - throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); - }, { - v: 1, - ast: {"params":[],"body":[0,[[13,"y",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]}, - externals: {}, - }) && $.f)({})); - const cx = 2; - const c = tgpu['~unstable'].fn([]).does(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { - throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); - }, { - v: 1, - ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":["cx"]}, - externals: {cx}, - }) && $.f)({})); - const d = tgpu['~unstable'].fn([]).does('() {}');" - `); - }); -}); - -describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { - it('wraps argument passed to does with globalThis set call', async () => { - const code = `\ - import tgpu from 'typegpu'; - import * as d from 'typegpu/data'; - - const counterBuffer = root - .createBuffer(d.vec3f, d.vec3f(0, 1, 0)) - .$usage('storage'); - const counter = counterBuffer.as('mutable'); - - const increment = tgpu['~unstable'] - .computeFn({ in: { num: d.builtin.numWorkgroups }, workgroupSize: [1] }) - .does((input) => { - const tmp = counter.value.x; - counter.value.x = counter.value.y; - counter.value.y += tmp; - counter.value.z += d.f32(input.num.x); - }); - `; - - expect(await rollupTransform(code)).toMatchInlineSnapshot(` - "import tgpu from 'typegpu'; - import * as d from 'typegpu/data'; - - const counterBuffer = root - .createBuffer(d.vec3f, d.vec3f(0, 1, 0)) - .$usage('storage'); - const counter = counterBuffer.as('mutable'); - - tgpu['~unstable'] - .computeFn({ in: { num: d.builtin.numWorkgroups }, workgroupSize: [1] }) - .does((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { - throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); - }), { - v: 1, - ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"tmp",[7,[7,"counter","value"],"x"]],[2,[7,[7,"counter","value"],"x"],"=",[7,[7,"counter","value"],"y"]],[2,[7,[7,"counter","value"],"y"],"+=","tmp"],[2,[7,[7,"counter","value"],"z"],"+=",[6,[7,"d","f32"],[[7,[7,"input","num"],"x"]]]]]],"externalNames":["counter","d"]}, - externals: {counter, d}, - }) && $.f)({}))); - " - `); - }); - - it('works for multiple functions, skips wgsl-implemented', async () => { - const code = `\ - import tgpu from 'typegpu'; - - const a = tgpu['~unstable'].computeFn({ workgroupSize: [1] }).does((input) => { - const x = true; - }); - - const b = tgpu['~unstable'].fn([]).does(() => { - const y = 2 + 2; - }); - - const cx = 2; - const c = tgpu['~unstable'].fn([]).does(() => cx); - - const d = tgpu['~unstable'].fn([]).does('() {}'); - `; - - expect(await rollupTransform(code)).toMatchInlineSnapshot(` - "import tgpu from 'typegpu'; - - tgpu['~unstable'].computeFn({ workgroupSize: [1] }).does((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { - throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); - }), { - v: 1, - ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":[]}, - externals: {}, - }) && $.f)({}))); - - tgpu['~unstable'].fn([]).does((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { - throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); - }), { - v: 1, - ast: {"params":[],"body":[0,[[13,"y",[1,[5,"2"],"+",[5,"2"]]]]],"externalNames":[]}, - externals: {}, - }) && $.f)({}))); - - const cx = 2; - tgpu['~unstable'].fn([]).does((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { - throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); - }), { - v: 1, - ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":["cx"]}, - externals: {cx}, - }) && $.f)({}))); - - tgpu['~unstable'].fn([]).does('() {}'); - " - `); - }); -}); diff --git a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts index d3079f329..fb7d7e91e 100644 --- a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts +++ b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts @@ -49,14 +49,14 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { const x = true; }); - const b = tgpu['~unstable'].fn([])(() => { + const b = tgpu.fn([])(() => { const y = 2 + 2; }); const cx = 2; - const c = tgpu['~unstable'].fn([])(() => cx); + const c = tgpu.fn([])(() => cx); - const d = tgpu['~unstable'].fn([])('() {}'); + const d = tgpu.fn([])('() {}'); `; expect(babelTransform(code)).toMatchInlineSnapshot(` @@ -70,7 +70,7 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { ast: {"params":[{"type":"i","name":"input"}],"body":[0,[[13,"x",true]]],"externalNames":[]}, externals: {}, }) && $.f)({})); - const b = tgpu['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { + const b = tgpu.fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { v: 1, @@ -78,14 +78,14 @@ describe('[BABEL] plugin for transpiling tgsl functions to tinyest', () => { externals: {}, }) && $.f)({})); const cx = 2; - const c = tgpu['~unstable'].fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { + const c = tgpu.fn([])(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = () => { throw new Error("The function \\"\\" is invokable only on the GPU. If you want to use it on the CPU, mark it with the \\"kernel & js\\" directive."); }, { v: 1, ast: {"params":[],"body":[0,[[10,"cx"]]],"externalNames":["cx"]}, externals: {cx}, }) && $.f)({})); - const d = tgpu['~unstable'].fn([])('() {}');" + const d = tgpu.fn([])('() {}');" `); }); @@ -203,14 +203,14 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { const x = true; }); - const b = tgpu['~unstable'].fn([])(() => { + const b = tgpu.fn([])(() => { const y = 2 + 2; }); const cx = 2; - const c = tgpu['~unstable'].fn([])(() => cx); + const c = tgpu.fn([])(() => cx); - const d = tgpu['~unstable'].fn([])('() {}'); + const d = tgpu.fn([])('() {}'); `; expect(await rollupTransform(code)).toMatchInlineSnapshot(` @@ -224,7 +224,7 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { externals: {}, }) && $.f)({}))); - tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { + tgpu.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); }), { v: 1, @@ -233,7 +233,7 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { }) && $.f)({}))); const cx = 2; - tgpu['~unstable'].fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { + tgpu.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { throw new Error(\`The function "" is invokable only on the GPU. If you want to use it on the CPU, mark it with the "kernel & js" directive.\`); }), { v: 1, @@ -241,7 +241,7 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { externals: {cx}, }) && $.f)({}))); - tgpu['~unstable'].fn([])('() {}'); + tgpu.fn([])('() {}'); " `); });