diff --git a/apps/typegpu-docs/astro.config.mjs b/apps/typegpu-docs/astro.config.mjs index 934f3ab512..df1081224f 100644 --- a/apps/typegpu-docs/astro.config.mjs +++ b/apps/typegpu-docs/astro.config.mjs @@ -274,6 +274,10 @@ export default defineConfig({ label: 'Naming Convention', slug: 'reference/naming-convention', }, + DEV && { + label: 'Shader Generation', + slug: 'reference/shader-generation', + }, typeDocSidebarGroup, ]), }, diff --git a/apps/typegpu-docs/src/content/docs/reference/shader-generation.mdx b/apps/typegpu-docs/src/content/docs/reference/shader-generation.mdx new file mode 100644 index 0000000000..c9f62e76fd --- /dev/null +++ b/apps/typegpu-docs/src/content/docs/reference/shader-generation.mdx @@ -0,0 +1,199 @@ +--- +title: Shader Generation +draft: true +--- + +TypeGPU houses a very powerful shader generator, capable of generating efficient WGSL code that closely matches the input +JavaScript code. + +## The phases of code generation + +The whole end-to-end process of turning JS into WGSL can be split into two phases: +- Parse the JS code into an AST. +- Collapse each AST node into a [snippet](#snippets) depth first, gradually building up the final WGSL code. + +We found that we don't always have enough information to do both phases as a build step (before the code reaches the browser). +For example, the type of a struct could only be known at runtime, or could be imported from another file, which complicates static analysis: + +```ts twoslash +import * as d from 'typegpu/data'; + +declare const getUserSettings: () => Promise<{ halfPrecision: boolean }>; +// ---cut--- +const half = (await getUserSettings()).halfPrecision; + +// Determining the precision based on a runtime parameter +const vec3 = half ? d.vec3h : d.vec3f; + +const Boid = d.struct({ + pos: vec3, + vel: vec3, +}); + +const createBoid = () => { + 'use gpu'; + return Boid({ pos: vec3(), vel: vec3(0, 1, 0) }); +}; + +const boid = createBoid(); +// ^? +``` + +:::caution +We could do everything at runtime, transforming the code a bit so that the TypeGPU shader generator can have access to +a function's JS source code, along with references to values referenced from the outer scope. + +The code shipped to the browser could look like so: +```js +const createBoid = () => { + 'use gpu'; + return Boid({ pos: vec3(), vel: vec3(0, 1, 0) }); +}; + +// Associate metadata with the function, which the TypeGPU generator can later use +(globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(createBoid, { + v: 1, + name: 'createBoid', + code: `() => { + 'use gpu'; + return Boid({ pos: vec3(), vel: vec3(0, 1, 0) }); + }`, + get externals() { return { Boid, vec3 }; }, +}); +``` + +However, parsing code at runtime requires both shipping the parser to the end user, and having to spend time parsing the code, +sacrificing load times and performance. +::: + +In order to avoid parsing at runtime while keeping the desired flexibility, we parse the AST at build time and compress it into +our custom format called [tinyest](https://npmjs.com/package/tinyest). It retains only information required for WGSL code +generation. + +The code shipped to the browser looks more like this: +```js +const createBoid = () => { + 'use gpu'; + return Boid({ pos: vec3(), vel: vec3(0, 1, 0) }); +}; + +(globalThis.__TYPEGPU_META__ ??= new WeakMap()).set(createBoid, { + v: 1, + name: 'createBoid', + // NOTE: Not meant to be read by humans + ast: {params:[],body:[0,[[10,[6,"Boid",[[104,{pos:[6,"vec3",[]],vel:[6,"vec3",[[5,"0"],[5,"1"],[5,"0"]]]}]]]]]],externalNames:["Boid","vec3"]}, + get externals() { return { Boid, vec3 }; }, +}); +``` + +## Snippets + +Snippets are the basis for TypeGPU shader code generation. They are immutable objects that hold three values: +- *value*: A piece of WGSL code, or something "resolvable" to a piece of WGSL code +- *dataType*: The inferred WGSL type of `value` [(more here)](#data-types) +- *origin*: An enumerable of where the value came from (if it's a reference to an existing value, or ephemeral) +[(more here)](#origins) + +```ts +// A simple snippet of a piece of WGSL code +const foo = snip( + /* value */ 'vec3f(1, 2, 3)', + /* dataType */ d.vec3f, + /* origin */ 'constant' +); // => Snippet + +// A simple snippet of something resolvable to a piece of WGSL code +const bar = snip( + /* value */ d.vec3f(1, 2, 3), + /* dataType */ d.vec3f, + /* origin */ 'constant' +); // => Snippet +``` + +If a snippet contains a value that isn't yet resolved WGSL, we defer that resolution as late as possible, so that we can +perform optimizations as we generate. For example, if we're evaluating the given expression `3 * 4`, we first interpret +both operands as snippets `snip(3, abstractInt, 'constant')` and `snip(4, abstractInt, 'constant')` respectively. +Since both are not yet resolved (or in other words, known at compile time), we can perform the multiplication at compile time, +resulting in a new snippet `snip(12, abstractInt, 'constant')`. + +:::note +If we were instead resolving eagerly, the resulting snippet would be `snip('3 * 4', abstractInt, 'constant')`. +::: + +### Data Types + +The data types that accompany snippets are just [TypeGPU Data Schemas](/TypeGPU/fundamentals/data-schemas). This information +can be used by parent expressions to generate different code. + +:::note +Data type inference is the basis for generating signatures for functions just from the arguments passed to them. +::: + +### Origins + +Origins are enumerable values that describe where a value came from (or didn't come from). Used mainly for: +- Determining if we're using a value that refers to something else (to create an implicit pointer). This mimics the behavior we + expect in JS, and doesn't perform unwanted copies on data. Example: + ```ts + const foo = () => { + 'use gpu'; + // The type of both expressions is `Boid`, yet one is a + // reference to an existing value, and the other is a + // value-type (ephemeral) and would disappear if we didn't + // assign it to a variable or use it. + const firstBoid = layout.$.boids[0]; + const newBoid = Boid(); + + const boidPos = newBoid.pos; + }; + ``` + Generates: + ```wgsl + fn foo() { + let firstBoid = (&boids[0]); // typed as ptr + var newBoid = Boid(); // typed as Boid + + let boidPos = (&newBoid.pos); // typed as ptr + } + ``` +- Detecting illegal uses of our APIs. One example is mutating a value that was passed in as an argument. Since we want the developer to have control over + passing something as value or as reference (pointer), we have to limit the dev's ability to mutate values that were passed in as arguments if they didn't + use refs (pointer instances). Otherwise, the generated WGSL won't act as we expect. + ```ts + const advance = (pos: d.v3f) => { + 'use gpu'; + // `pos` has the origin 'argument'. Property accesses on arguments + // return snippets that also have the origin 'argument'. + // + // If we try to mutate a snippet that has the origin 'argument', + // we'll get a resolution error. + pos.x += 1; + }; + + const main = () => { + 'use gpu'; + const pos = d.vec3f(0, 0, 0); + advance(pos); + // pos.x === 1 in JS + }; + ``` + Generates: + ```wgsl + fn advance(pos: vec3f) { + pos.x += 1; + } + + fn main() { + let pos = vec3f(0, 0, 0); + advance(pos); + // pos.x === 0 in WGSL + } + ``` + +There are essentially three types of origins: +- **Ephemeral Origins**: These origins represent values that are created or derived from other values. They are typically used for creating new instances or + performing operations that produce new values. Examples include creating a new `Boid` instance or calculating a new position based on an existing one. These + include `'runtime'` and `'constant'`. +- **Referential Origins**: These origins represent references to existing values. They are typically used for accessing or modifying existing data. Examples + include accessing the position of an existing `Boid` instance or modifying the position of an existing `Boid` instance. These include `'uniform'`, `'mutable'`, `'readonly'`, `'workgroup'`, `'private'`, `'function'`, `'handle'` and `'constant-ref'`. +- **Argument Origins**: This group is dedicated to exactly one origin: 'argument'. It represents values that are passed as arguments to functions. diff --git a/apps/typegpu-docs/src/examples/image-processing/blur/index.ts b/apps/typegpu-docs/src/examples/image-processing/blur/index.ts index ab1ae41579..2023960861 100644 --- a/apps/typegpu-docs/src/examples/image-processing/blur/index.ts +++ b/apps/typegpu-docs/src/examples/image-processing/blur/index.ts @@ -66,9 +66,7 @@ const ioLayout = tgpu.bindGroupLayout({ outTexture: { storageTexture: d.textureStorage2d('rgba8unorm') }, }); -const tileData = tgpu.workgroupVar( - d.arrayOf(d.arrayOf(d.vec3f, 128), 4), -); +const tileData = tgpu.workgroupVar(d.arrayOf(d.arrayOf(d.vec3f, 128), 4)); const computeFn = tgpu['~unstable'].computeFn({ in: { diff --git a/apps/typegpu-docs/src/examples/rendering/3d-fish/compute.ts b/apps/typegpu-docs/src/examples/rendering/3d-fish/compute.ts index 6ca45e6e58..9f6144b07a 100644 --- a/apps/typegpu-docs/src/examples/rendering/3d-fish/compute.ts +++ b/apps/typegpu-docs/src/examples/rendering/3d-fish/compute.ts @@ -1,22 +1,12 @@ import * as d from 'typegpu/data'; import * as std from 'typegpu/std'; import * as p from './params.ts'; -import { computeBindGroupLayout as layout, ModelData } from './schemas.ts'; +import { computeBindGroupLayout as layout } from './schemas.ts'; import { projectPointOnLine } from './tgsl-helpers.ts'; export const simulate = (fishIndex: number) => { 'use gpu'; - // TODO: replace it with struct copy when Chromium is fixed - const fishData = ModelData({ - position: layout.$.currentFishData[fishIndex].position, - direction: layout.$.currentFishData[fishIndex].direction, - scale: layout.$.currentFishData[fishIndex].scale, - variant: layout.$.currentFishData[fishIndex].variant, - applySeaDesaturation: - layout.$.currentFishData[fishIndex].applySeaDesaturation, - applySeaFog: layout.$.currentFishData[fishIndex].applySeaFog, - applySinWave: layout.$.currentFishData[fishIndex].applySinWave, - }); + const fishData = layout.$.currentFishData[fishIndex]; let separation = d.vec3f(); let alignment = d.vec3f(); let alignmentCount = 0; @@ -30,34 +20,22 @@ export const simulate = (fishIndex: number) => { continue; } - // TODO: replace it with struct copy when Chromium is fixed - const other = ModelData({ - position: layout.$.currentFishData[i].position, - direction: layout.$.currentFishData[i].direction, - scale: layout.$.currentFishData[i].scale, - variant: layout.$.currentFishData[i].variant, - applySeaDesaturation: layout.$.currentFishData[i].applySeaDesaturation, - applySeaFog: layout.$.currentFishData[i].applySeaFog, - applySinWave: layout.$.currentFishData[i].applySinWave, - }); - const dist = std.length(std.sub(fishData.position, other.position)); + const other = layout.$.currentFishData[i]; + const dist = std.length(fishData.position.sub(other.position)); if (dist < layout.$.fishBehavior.separationDist) { - separation = std.add( - separation, - std.sub(fishData.position, other.position), - ); + separation = separation.add(fishData.position.sub(other.position)); } if (dist < layout.$.fishBehavior.alignmentDist) { - alignment = std.add(alignment, other.direction); + alignment = alignment.add(other.direction); alignmentCount = alignmentCount + 1; } if (dist < layout.$.fishBehavior.cohesionDist) { - cohesion = std.add(cohesion, other.position); + cohesion = cohesion.add(other.position); cohesionCount = cohesionCount + 1; } } if (alignmentCount > 0) { - alignment = std.mul(1 / d.f32(alignmentCount), alignment); + alignment = alignment.mul(1 / d.f32(alignmentCount)); } if (cohesionCount > 0) { cohesion = std.sub( @@ -75,12 +53,12 @@ export const simulate = (fishIndex: number) => { if (axisPosition > axisAquariumSize - distance) { const str = axisPosition - (axisAquariumSize - distance); - wallRepulsion = std.sub(wallRepulsion, std.mul(str, repulsion)); + wallRepulsion = wallRepulsion.sub(repulsion.mul(str)); } if (axisPosition < -axisAquariumSize + distance) { const str = -axisAquariumSize + distance - axisPosition; - wallRepulsion = std.add(wallRepulsion, std.mul(str, repulsion)); + wallRepulsion = wallRepulsion.add(repulsion.mul(str)); } } @@ -89,42 +67,38 @@ export const simulate = (fishIndex: number) => { fishData.position, layout.$.mouseRay.line, ); - const diff = std.sub(fishData.position, proj); + const diff = fishData.position.sub(proj); const limit = p.fishMouseRayRepulsionDistance; const str = std.pow(2, std.clamp(limit - std.length(diff), 0, limit)) - 1; - rayRepulsion = std.mul(str, std.normalize(diff)); + rayRepulsion = std.normalize(diff).mul(str); } - fishData.direction = std.add( - fishData.direction, - std.mul(layout.$.fishBehavior.separationStr, separation), + let direction = d.vec3f(fishData.direction); + + direction = direction.add( + separation.mul(layout.$.fishBehavior.separationStr), ); - fishData.direction = std.add( - fishData.direction, - std.mul(layout.$.fishBehavior.alignmentStr, alignment), + direction = direction.add( + alignment.mul(layout.$.fishBehavior.alignmentStr), ); - fishData.direction = std.add( - fishData.direction, - std.mul(layout.$.fishBehavior.cohesionStr, cohesion), + direction = direction.add( + cohesion.mul(layout.$.fishBehavior.cohesionStr), ); - fishData.direction = std.add( - fishData.direction, - std.mul(p.fishWallRepulsionStrength, wallRepulsion), + direction = direction.add( + wallRepulsion.mul(p.fishWallRepulsionStrength), ); - fishData.direction = std.add( - fishData.direction, - std.mul(p.fishMouseRayRepulsionStrength, rayRepulsion), + direction = direction.add( + rayRepulsion.mul(p.fishMouseRayRepulsionStrength), ); - - fishData.direction = std.mul( - std.clamp(std.length(fishData.direction), 0.0, 0.01), - std.normalize(fishData.direction), + direction = std.normalize(direction).mul( + std.clamp(std.length(fishData.direction), 0, 0.01), ); - const translation = std.mul( + const translation = direction.mul( d.f32(std.min(999, layout.$.timePassed)) / 8, - fishData.direction, ); - fishData.position = std.add(fishData.position, translation); - layout.$.nextFishData[fishIndex] = fishData; + + const nextFishData = layout.$.nextFishData[fishIndex]; + nextFishData.position = fishData.position.add(translation); + nextFishData.direction = d.vec3f(direction); }; diff --git a/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts b/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts index 57aa4afb1f..a260836c6b 100644 --- a/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts +++ b/apps/typegpu-docs/src/examples/rendering/3d-fish/index.ts @@ -98,30 +98,31 @@ function enqueuePresetChanges() { const buffer0mutable = fishDataBuffers[0].as('mutable'); const buffer1mutable = fishDataBuffers[1].as('mutable'); const seedUniform = root.createUniform(d.f32); -const randomizeFishPositionsPipeline = root['~unstable'] - .createGuardedComputePipeline((x) => { - 'use gpu'; - randf.seed2(d.vec2f(d.f32(x), seedUniform.$)); - const data = ModelData({ - position: d.vec3f( - randf.sample() * p.aquariumSize.x - p.aquariumSize.x / 2, - randf.sample() * p.aquariumSize.y - p.aquariumSize.y / 2, - randf.sample() * p.aquariumSize.z - p.aquariumSize.z / 2, - ), - direction: d.vec3f( - randf.sample() * 0.1 - 0.05, - randf.sample() * 0.1 - 0.05, - randf.sample() * 0.1 - 0.05, - ), - scale: p.fishModelScale * (1 + (randf.sample() - 0.5) * 0.8), - variant: randf.sample(), - applySinWave: 1, - applySeaFog: 1, - applySeaDesaturation: 1, - }); - buffer0mutable.$[x] = data; - buffer1mutable.$[x] = data; +const randomizeFishPositionsPipeline = root[ + '~unstable' +].createGuardedComputePipeline((x) => { + 'use gpu'; + randf.seed2(d.vec2f(d.f32(x), seedUniform.$)); + const data = ModelData({ + position: d.vec3f( + randf.sample() * p.aquariumSize.x - p.aquariumSize.x / 2, + randf.sample() * p.aquariumSize.y - p.aquariumSize.y / 2, + randf.sample() * p.aquariumSize.z - p.aquariumSize.z / 2, + ), + direction: d.vec3f( + randf.sample() * 0.1 - 0.05, + randf.sample() * 0.1 - 0.05, + randf.sample() * 0.1 - 0.05, + ), + scale: p.fishModelScale * (1 + (randf.sample() - 0.5) * 0.8), + variant: randf.sample(), + applySinWave: 1, + applySeaFog: 1, + applySeaDesaturation: 1, }); + buffer0mutable.$[x] = ModelData(data); + buffer1mutable.$[x] = ModelData(data); +}); const randomizeFishPositions = () => { seedUniform.write((performance.now() % 10000) / 10000); @@ -199,8 +200,9 @@ let depthTexture = root.device.createTexture({ usage: GPUTextureUsage.RENDER_ATTACHMENT, }); -const simulatePipeline = root['~unstable'] - .createGuardedComputePipeline(simulate); +const simulatePipeline = root['~unstable'].createGuardedComputePipeline( + simulate, +); // bind groups @@ -387,9 +389,7 @@ async function updateMouseRay(cx: number, cy: number) { activated: 1, line: Line3({ origin: camera.position.xyz, - dir: std.normalize( - std.sub(worldPosNonUniform, camera.position.xyz), - ), + dir: std.normalize(std.sub(worldPosNonUniform, camera.position.xyz)), }), }); } @@ -453,15 +453,19 @@ window.addEventListener('mousemove', mouseMoveEventListener); // Touch controls -canvas.addEventListener('touchstart', async (event) => { - event.preventDefault(); - if (event.touches.length === 1) { - previousMouseX = event.touches[0].clientX; - previousMouseY = event.touches[0].clientY; - updateMouseRay(event.touches[0].clientX, event.touches[0].clientY); - controlsPopup.style.opacity = '0'; - } -}, { passive: false }); +canvas.addEventListener( + 'touchstart', + async (event) => { + event.preventDefault(); + if (event.touches.length === 1) { + previousMouseX = event.touches[0].clientX; + previousMouseY = event.touches[0].clientY; + updateMouseRay(event.touches[0].clientX, event.touches[0].clientY); + controlsPopup.style.opacity = '0'; + } + }, + { passive: false }, +); const touchMoveEventListener = (event: TouchEvent) => { if (event.touches.length === 1) { diff --git a/apps/typegpu-docs/src/examples/rendering/3d-fish/render.ts b/apps/typegpu-docs/src/examples/rendering/3d-fish/render.ts index bb810faa21..cc94f65c75 100644 --- a/apps/typegpu-docs/src/examples/rendering/3d-fish/render.ts +++ b/apps/typegpu-docs/src/examples/rendering/3d-fish/render.ts @@ -135,7 +135,7 @@ export const fragmentShader = tgpu['~unstable'].fragmentFn({ std.sub(layout.$.camera.position.xyz, input.worldPosition), ); - let desaturatedColor = lightedColor; + let desaturatedColor = d.vec3f(lightedColor); if (input.applySeaDesaturation === 1) { const desaturationFactor = -std.atan2((distanceFromCamera - 5) / 10, 1) / 3; @@ -147,7 +147,7 @@ export const fragmentShader = tgpu['~unstable'].fragmentFn({ desaturatedColor = hsvToRgb(hsv); } - let foggedColor = desaturatedColor; + let foggedColor = d.vec3f(desaturatedColor); if (input.applySeaFog === 1) { const fogParameter = std.max(0, (distanceFromCamera - 1.5) * 0.2); const fogFactor = fogParameter / (1 + fogParameter); diff --git a/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/icosphere.ts b/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/icosphere.ts index a192c30be2..92b576fc27 100644 --- a/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/icosphere.ts +++ b/apps/typegpu-docs/src/examples/rendering/cubemap-reflection/icosphere.ts @@ -133,7 +133,7 @@ export class IcosphereGenerator { in: { gid: d.builtin.globalInvocationId }, workgroupSize: [WORKGROUP_SIZE, 1, 1], })((input) => { - const triangleCount = d.u32(std.arrayLength(prevVertices.value) / 3); + const triangleCount = d.u32(prevVertices.$.length / 3); const triangleIndex = input.gid.x + input.gid.y * MAX_DISPATCH; if (triangleIndex >= triangleCount) { return; @@ -142,13 +142,13 @@ export class IcosphereGenerator { const baseIndexPrev = triangleIndex * 3; const v1 = unpackVec2u( - prevVertices.value[baseIndexPrev].position, + prevVertices.$[baseIndexPrev].position, ); const v2 = unpackVec2u( - prevVertices.value[baseIndexPrev + 1].position, + prevVertices.$[baseIndexPrev + 1].position, ); const v3 = unpackVec2u( - prevVertices.value[baseIndexPrev + 2].position, + prevVertices.$[baseIndexPrev + 2].position, ); const v12 = d.vec4f( @@ -188,8 +188,8 @@ export class IcosphereGenerator { const reprojectedVertex = newVertices[i]; const triBase = i - (i % 3); - let normal = reprojectedVertex; - if (smoothFlag.value === 0) { + let normal = d.vec4f(reprojectedVertex); + if (smoothFlag.$ === 0) { normal = getAverageNormal( newVertices[triBase], newVertices[triBase + 1], @@ -198,12 +198,9 @@ export class IcosphereGenerator { } const outIndex = baseIndexNext + i; - const nextVertex = nextVertices.value[outIndex]; - + const nextVertex = nextVertices.$[outIndex]; nextVertex.position = packVec2u(reprojectedVertex); nextVertex.normal = packVec2u(normal); - - nextVertices.value[outIndex] = nextVertex; } }); diff --git a/apps/typegpu-docs/src/examples/rendering/disco/shaders/fragment.ts b/apps/typegpu-docs/src/examples/rendering/disco/shaders/fragment.ts index 5631c45c65..59a12c7e1e 100644 --- a/apps/typegpu-docs/src/examples/rendering/disco/shaders/fragment.ts +++ b/apps/typegpu-docs/src/examples/rendering/disco/shaders/fragment.ts @@ -4,13 +4,17 @@ import * as std from 'typegpu/std'; import { palette } from '../utils.ts'; import { resolutionAccess, timeAccess } from '../consts.ts'; -const aspectCorrected = tgpu.fn([d.vec2f], d.vec2f)((uv) => { - const v = uv.xy.sub(0.5).mul(2.0); +const aspectCorrected = (uv: d.v2f): d.v2f => { + 'use gpu'; + const v = uv.sub(0.5).mul(2); const aspect = resolutionAccess.$.x / resolutionAccess.$.y; - if (aspect > 1) v.x *= aspect; - else v.y /= aspect; + if (aspect > 1) { + v.x *= aspect; + } else { + v.y /= aspect; + } return v; -}); +}; const accumulate = tgpu.fn( [d.vec3f, d.vec3f, d.f32], @@ -22,21 +26,20 @@ export const mainFragment = tgpu['~unstable'].fragmentFn({ out: d.vec4f, })(({ uv }) => { { - let aspectUv = aspectCorrected(uv); - const originalUv = aspectUv; + const originalUv = aspectCorrected(uv); + + let aspectUv = d.vec2f(originalUv); let accumulatedColor = d.vec3f(); for (let iteration = 0.0; iteration < 5.0; iteration++) { - aspectUv = std.sub( - std.fract(std.mul(aspectUv, 1.3 * std.sin(timeAccess.$))), - 0.5, - ); + aspectUv = std.fract(aspectUv.mul(1.3 * std.sin(timeAccess.$))).sub(0.5); let radialLength = std.length(aspectUv) * std.exp(-std.length(originalUv) * 2); - const paletteColor = palette(std.length(originalUv) + timeAccess.$ * 0.9); radialLength = std.sin(radialLength * 8 + timeAccess.$) / 8; radialLength = std.abs(radialLength); radialLength = std.smoothstep(0.0, 0.1, radialLength); radialLength = 0.06 / radialLength; + + const paletteColor = palette(std.length(originalUv) + timeAccess.$ * 0.9); accumulatedColor = accumulate( accumulatedColor, paletteColor, @@ -53,8 +56,9 @@ export const mainFragment2 = tgpu['~unstable'].fragmentFn({ out: d.vec4f, })(({ uv }) => { { - let aspectUv = aspectCorrected(uv); - const originalUv = aspectUv; + const originalUv = aspectCorrected(uv); + let aspectUv = d.vec2f(originalUv); + let accumulatedColor = d.vec3f(); for (let iteration = 0.0; iteration < 3.0; iteration++) { aspectUv = std.fract(aspectUv.mul(-0.9)).sub(0.5); @@ -80,8 +84,9 @@ export const mainFragment3 = tgpu['~unstable'].fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f, })(({ uv }) => { - let aspectUv = aspectCorrected(uv); - const originalUv = aspectUv; + const originalUv = aspectCorrected(uv); + let aspectUv = d.vec2f(originalUv); + let accumulatedColor = d.vec3f(); const baseAngle = timeAccess.$ * 0.3; const cosBaseAngle = std.cos(baseAngle); @@ -129,10 +134,11 @@ export const mainFragment4 = tgpu['~unstable'].fragmentFn({ std.abs(std.fract(aspectUv.x * 1.2) - 0.5), std.abs(std.fract(aspectUv.y * 1.2) - 0.5), ).mul(2).sub(1); - aspectUv = mirroredUv; - const originalUv = aspectUv; + aspectUv = d.vec2f(mirroredUv); + const originalUv = d.vec2f(aspectUv); let accumulatedColor = d.vec3f(0, 0, 0); const time = timeAccess.$; + for (let iteration = 0; iteration < 4; iteration++) { const iterationF32 = d.f32(iteration); // rotation + scale @@ -159,6 +165,7 @@ export const mainFragment4 = tgpu['~unstable'].fragmentFn({ ); accumulatedColor = accumulate(accumulatedColor, paletteColor, radialLength); } + return d.vec4f(accumulatedColor, 1.0); }); @@ -167,9 +174,10 @@ export const mainFragment5 = tgpu['~unstable'].fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f, })(({ uv }) => { - let aspectUv = aspectCorrected(uv); - const originalUv = aspectUv; + const originalUv = aspectCorrected(uv); + let aspectUv = d.vec2f(originalUv); let accumulatedColor = d.vec3f(); + for (let iteration = 0; iteration < 3; iteration++) { const iterationF32 = d.f32(iteration); // swirl distortion @@ -203,7 +211,8 @@ export const mainFragment6 = tgpu['~unstable'].fragmentFn({ out: d.vec4f, })(({ uv }) => { let aspectUv = aspectCorrected(uv); - const originalUv = aspectUv; + const originalUv = d.vec2f(aspectUv); + let accumulatedColor = d.vec3f(0, 0, 0); const time = timeAccess.$; for (let iteration = 0; iteration < 5; iteration++) { @@ -245,7 +254,7 @@ export const mainFragment7 = tgpu['~unstable'].fragmentFn({ std.abs(std.fract(aspectUv.x * 1.5) - 0.5), std.abs(std.fract(aspectUv.y * 1.5) - 0.5), ).mul(2); - const originalUv = aspectUv; + const originalUv = d.vec2f(aspectUv); let accumulatedColor = d.vec3f(0, 0, 0); const time = timeAccess.$; for (let iteration = 0; iteration < 4; iteration++) { diff --git a/apps/typegpu-docs/src/examples/rendering/ray-marching/index.ts b/apps/typegpu-docs/src/examples/rendering/ray-marching/index.ts index 798e0579d9..990f10203c 100644 --- a/apps/typegpu-docs/src/examples/rendering/ray-marching/index.ts +++ b/apps/typegpu-docs/src/examples/rendering/ray-marching/index.ts @@ -127,13 +127,13 @@ const rayMarch = (ro: d.v3f, rd: d.v3f): Shape => { }); for (let i = 0; i < MAX_STEPS; i++) { - const p = std.add(ro, std.mul(rd, dO)); + const p = ro.add(rd.mul(dO)); const scene = getSceneDist(p); dO += scene.dist; if (dO > MAX_DIST || scene.dist < SURF_DIST) { result.dist = dO; - result.color = scene.color; + result.color = d.vec3f(scene.color); break; } } @@ -154,7 +154,7 @@ const softShadow = ( for (let i = 0; i < 100; i++) { if (t >= maxT) break; - const h = getSceneDist(std.add(ro, std.mul(rd, t))).dist; + const h = getSceneDist(ro.add(rd.mul(t))).dist; if (h < 0.001) return 0; res = std.min(res, k * h / t); t += std.max(h, 0.001); @@ -169,9 +169,9 @@ const getNormal = (p: d.v3f): d.v3f => { const e = 0.01; const n = d.vec3f( - getSceneDist(std.add(p, d.vec3f(e, 0, 0))).dist - dist, - getSceneDist(std.add(p, d.vec3f(0, e, 0))).dist - dist, - getSceneDist(std.add(p, d.vec3f(0, 0, e))).dist - dist, + getSceneDist(p.add(d.vec3f(e, 0, 0))).dist - dist, + getSceneDist(p.add(d.vec3f(0, e, 0))).dist - dist, + getSceneDist(p.add(d.vec3f(0, 0, e))).dist - dist, ); return std.normalize(n); @@ -223,17 +223,17 @@ const fragmentMain = tgpu['~unstable'].fragmentFn({ // Lighting with orbiting light const lightPos = getOrbitingLightPos(time.$); - const l = std.normalize(std.sub(lightPos, p)); + const l = std.normalize(lightPos.sub(p)); const diff = std.max(std.dot(n, l), 0); // Soft shadows const shadowRo = p; const shadowRd = l; - const shadowDist = std.length(std.sub(lightPos, p)); + const shadowDist = std.length(lightPos.sub(p)); const shadow = softShadow(shadowRo, shadowRd, 0.1, shadowDist, d.f32(16)); // Combine lighting with shadows and color - const litColor = std.mul(march.color, diff); + const litColor = march.color.mul(diff); const finalColor = std.mix( std.mul(litColor, 0.5), // Shadow color litColor, // Lit color diff --git a/apps/typegpu-docs/src/examples/rendering/xor-dev-runner/index.ts b/apps/typegpu-docs/src/examples/rendering/xor-dev-runner/index.ts index 4ec15685ee..11494df9a1 100644 --- a/apps/typegpu-docs/src/examples/rendering/xor-dev-runner/index.ts +++ b/apps/typegpu-docs/src/examples/rendering/xor-dev-runner/index.ts @@ -78,7 +78,7 @@ const fragmentMain = tgpu['~unstable'].fragmentFn({ const p = sub(mul(z, dir), scale.$); p.x -= time.$ + 3; p.z -= time.$ + 3; - let q = p; + let q = d.vec3f(p); let prox = p.y; for (let i = 40.1; i > 0.01; i *= 0.2) { q = sub(i * 0.9, abs(sub(mod(q, i + i), i))); diff --git a/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts b/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts index 183712439a..171923a170 100644 --- a/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts +++ b/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts @@ -91,7 +91,7 @@ const rayMarch = tgpu.fn( if (scene.dist < c.SURF_DIST) { result.dist = distOrigin; - result.color = scene.color; + result.color = d.vec3f(scene.color); break; } } diff --git a/apps/typegpu-docs/src/examples/simulation/boids-next/index.ts b/apps/typegpu-docs/src/examples/simulation/boids-next/index.ts index 2d2fde0a5e..7b9b358b04 100644 --- a/apps/typegpu-docs/src/examples/simulation/boids-next/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/boids-next/index.ts @@ -179,7 +179,7 @@ const { currentTrianglePos, nextTrianglePos } = computeBindGroupLayout.bound; const simulate = (index: number) => { 'use gpu'; - const instanceInfo = currentTrianglePos.value[index]; + const instanceInfo = TriangleData(currentTrianglePos.value[index]); let separation = d.vec2f(); let alignment = d.vec2f(); let cohesion = d.vec2f(); @@ -246,7 +246,7 @@ const simulate = (index: number) => { instanceInfo.position = std.add(instanceInfo.position, instanceInfo.velocity); - nextTrianglePos.value[index] = instanceInfo; + nextTrianglePos.value[index] = TriangleData(instanceInfo); }; const simulatePipeline = root['~unstable'] diff --git a/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts b/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts index 47be228566..fb47dade46 100644 --- a/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/fluid-double-buffering/index.ts @@ -43,13 +43,13 @@ const coordsToIndex = (x: number, y: number) => { const getCell = (x: number, y: number): d.v4f => { 'use gpu'; - return inputGridSlot.$[coordsToIndex(x, y)]; + return d.vec4f(inputGridSlot.$[coordsToIndex(x, y)]); }; const setCell = (x: number, y: number, value: d.v4f) => { 'use gpu'; const index = coordsToIndex(x, y); - outputGridSlot.$[index] = value; + outputGridSlot.$[index] = d.vec4f(value); }; const setVelocity = (x: number, y: number, velocity: d.v2f) => { @@ -179,7 +179,7 @@ const computeVelocity = (x: number, y: number): d.v2f => { const leastCostDir = dirChoices[d.u32(randf.sample() * d.f32(dirChoiceCount))]; - return leastCostDir; + return d.vec2f(leastCostDir); }; const moveObstacles = () => { @@ -340,7 +340,7 @@ const simulate = (xu: number, yu: number) => { const minInflow = getMinimumInFlow(x, y); next.z = std.max(minInflow, next.z); - outputGridSlot.$[index] = next; + outputGridSlot.$[index] = d.vec4f(next); }; const OBSTACLE_BOX = 0; @@ -474,7 +474,7 @@ function makePipelines( } } - outputGridSlot.$[index] = value; + outputGridSlot.$[index] = d.vec4f(value); }); const simulatePipeline = root['~unstable'] diff --git a/apps/typegpu-docs/src/examples/simulation/gravity/compute.ts b/apps/typegpu-docs/src/examples/simulation/gravity/compute.ts index 54042282d1..eb79d0fa32 100644 --- a/apps/typegpu-docs/src/examples/simulation/gravity/compute.ts +++ b/apps/typegpu-docs/src/examples/simulation/gravity/compute.ts @@ -9,18 +9,17 @@ const { none, bounce, merge } = collisionBehaviors; // tiebreaker function for merges and bounces const isSmaller = tgpu.fn([d.u32, d.u32], d.bool)((currentId, otherId) => { - if ( - computeLayout.$.inState[currentId].mass < - computeLayout.$.inState[otherId].mass - ) { + const current = computeLayout.$.inState[currentId]; + const other = computeLayout.$.inState[otherId]; + + if (current.mass < other.mass) { return true; } - if ( - computeLayout.$.inState[currentId].mass === - computeLayout.$.inState[otherId].mass - ) { + + if (current.mass === other.mass) { return currentId < otherId; } + return false; }); @@ -29,42 +28,24 @@ export const computeCollisionsShader = tgpu['~unstable'].computeFn({ workgroupSize: [1], })((input) => { const currentId = input.gid.x; - // TODO: replace it with struct copy when Chromium is fixed - const current = CelestialBody({ - position: computeLayout.$.inState[currentId].position, - velocity: computeLayout.$.inState[currentId].velocity, - mass: computeLayout.$.inState[currentId].mass, - collisionBehavior: computeLayout.$.inState[currentId].collisionBehavior, - textureIndex: computeLayout.$.inState[currentId].textureIndex, - radiusMultiplier: computeLayout.$.inState[currentId].radiusMultiplier, - ambientLightFactor: computeLayout.$.inState[currentId].ambientLightFactor, - destroyed: computeLayout.$.inState[currentId].destroyed, - }); - - const updatedCurrent = current; + const current = CelestialBody(computeLayout.$.inState[currentId]); + if (current.destroyed === 0) { - for (let i = 0; i < computeLayout.$.celestialBodiesCount; i++) { - const otherId = d.u32(i); - // TODO: replace it with struct copy when Chromium is fixed - const other = CelestialBody({ - position: computeLayout.$.inState[otherId].position, - velocity: computeLayout.$.inState[otherId].velocity, - mass: computeLayout.$.inState[otherId].mass, - collisionBehavior: computeLayout.$.inState[otherId].collisionBehavior, - textureIndex: computeLayout.$.inState[otherId].textureIndex, - radiusMultiplier: computeLayout.$.inState[otherId].radiusMultiplier, - ambientLightFactor: computeLayout.$.inState[otherId].ambientLightFactor, - destroyed: computeLayout.$.inState[otherId].destroyed, - }); - // no collision occurs... + for ( + let otherId = d.u32(0); + otherId < d.u32(computeLayout.$.celestialBodiesCount); + otherId++ + ) { + const other = computeLayout.$.inState[otherId]; if ( - d.u32(i) === input.gid.x || // ...with itself + otherId === currentId || // ...with itself other.destroyed === 1 || // ...when other is destroyed current.collisionBehavior === none || // ...when current behavior is none other.collisionBehavior === none || // ...when other behavior is none std.distance(current.position, other.position) >= radiusOf(current) + radiusOf(other) // ...when other is too far away ) { + // no collision occurs... continue; } @@ -76,30 +57,21 @@ export const computeCollisionsShader = tgpu['~unstable'].computeFn({ // bounce occurs // push the smaller object outside if (isSmaller(currentId, otherId)) { - updatedCurrent.position = std.add( - other.position, - std.mul( - radiusOf(current) + radiusOf(other), - std.normalize(std.sub(current.position, other.position)), - ), + const dir = std.normalize(current.position.sub(other.position)); + current.position = other.position.add( + dir.mul(radiusOf(current) + radiusOf(other)), ); } + // bounce with tiny damping - updatedCurrent.velocity = std.mul( - 0.99, - std.sub( - updatedCurrent.velocity, - std.mul( - (((2 * other.mass) / (current.mass + other.mass)) * - std.dot( - std.sub(current.velocity, other.velocity), - std.sub(current.position, other.position), - )) / - std.pow(std.distance(current.position, other.position), 2), - std.sub(current.position, other.position), - ), - ), - ); + const posDiff = current.position.sub(other.position); + const velDiff = current.velocity.sub(other.velocity); + const posDiffFactor = + (((2 * other.mass) / (current.mass + other.mass)) * + std.dot(velDiff, posDiff)) / std.dot(posDiff, posDiff); + + current.velocity = current.velocity + .sub(posDiff.mul(posDiffFactor)).mul(0.99); } else { // merge occurs const isCurrentAbsorbed = current.collisionBehavior === bounce || @@ -107,57 +79,41 @@ export const computeCollisionsShader = tgpu['~unstable'].computeFn({ isSmaller(currentId, otherId)); if (isCurrentAbsorbed) { // absorbed by the other - updatedCurrent.destroyed = 1; + current.destroyed = 1; } else { // absorbs the other - const m1 = updatedCurrent.mass; + const m1 = current.mass; const m2 = other.mass; - updatedCurrent.velocity = std.add( - std.mul(m1 / (m1 + m2), updatedCurrent.velocity), - std.mul(m2 / (m1 + m2), other.velocity), + current.velocity = std.add( + current.velocity.mul(m1 / (m1 + m2)), + other.velocity.mul(m2 / (m1 + m2)), ); - updatedCurrent.mass = m1 + m2; + current.mass = m1 + m2; } } } } - computeLayout.$.outState[input.gid.x] = updatedCurrent; + computeLayout.$.outState[currentId] = CelestialBody(current); }); export const computeGravityShader = tgpu['~unstable'].computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: [1], })((input) => { - // TODO: replace it with struct copy when Chromium is fixed - const current = CelestialBody({ - position: computeLayout.$.inState[input.gid.x].position, - velocity: computeLayout.$.inState[input.gid.x].velocity, - mass: computeLayout.$.inState[input.gid.x].mass, - collisionBehavior: computeLayout.$.inState[input.gid.x].collisionBehavior, - textureIndex: computeLayout.$.inState[input.gid.x].textureIndex, - radiusMultiplier: computeLayout.$.inState[input.gid.x].radiusMultiplier, - ambientLightFactor: computeLayout.$.inState[input.gid.x].ambientLightFactor, - destroyed: computeLayout.$.inState[input.gid.x].destroyed, - }); const dt = timeAccess.$.passed * timeAccess.$.multiplier; + const currentId = input.gid.x; + const current = CelestialBody(computeLayout.$.inState[currentId]); - const updatedCurrent = current; if (current.destroyed === 0) { - for (let i = 0; i < computeLayout.$.celestialBodiesCount; i++) { - // TODO: replace it with struct copy when Chromium is fixed - const other = CelestialBody({ - position: computeLayout.$.inState[i].position, - velocity: computeLayout.$.inState[i].velocity, - mass: computeLayout.$.inState[i].mass, - collisionBehavior: computeLayout.$.inState[i].collisionBehavior, - textureIndex: computeLayout.$.inState[i].textureIndex, - radiusMultiplier: computeLayout.$.inState[i].radiusMultiplier, - ambientLightFactor: computeLayout.$.inState[i].ambientLightFactor, - destroyed: computeLayout.$.inState[i].destroyed, - }); - - if (d.u32(i) === input.gid.x || other.destroyed === 1) { + for ( + let otherId = d.u32(0); + otherId < d.u32(computeLayout.$.celestialBodiesCount); + otherId++ + ) { + const other = computeLayout.$.inState[otherId]; + + if (otherId === currentId || other.destroyed === 1) { continue; } @@ -167,20 +123,14 @@ export const computeGravityShader = tgpu['~unstable'].computeFn({ ); const gravityForce = (current.mass * other.mass) / dist / dist; - const direction = std.normalize( - std.sub(other.position, current.position), - ); - updatedCurrent.velocity = std.add( - updatedCurrent.velocity, - std.mul((gravityForce / current.mass) * dt, direction), + const direction = std.normalize(other.position.sub(current.position)); + current.velocity = current.velocity.add( + direction.mul((gravityForce / current.mass) * dt), ); } - updatedCurrent.position = std.add( - updatedCurrent.position, - std.mul(dt, updatedCurrent.velocity), - ); + current.position = current.position.add(current.velocity.mul(dt)); } - computeLayout.$.outState[input.gid.x] = updatedCurrent; + computeLayout.$.outState[currentId] = CelestialBody(current); }); diff --git a/apps/typegpu-docs/src/examples/simulation/gravity/helpers.ts b/apps/typegpu-docs/src/examples/simulation/gravity/helpers.ts index e6528ab728..58171f9b75 100644 --- a/apps/typegpu-docs/src/examples/simulation/gravity/helpers.ts +++ b/apps/typegpu-docs/src/examples/simulation/gravity/helpers.ts @@ -1,10 +1,13 @@ import { load } from '@loaders.gl/core'; import { OBJLoader } from '@loaders.gl/obj'; -import { tgpu, type TgpuRoot } from 'typegpu'; +import type { TgpuRoot } from 'typegpu'; import * as d from 'typegpu/data'; -import * as std from 'typegpu/std'; import { sphereTextureNames } from './enums.ts'; -import { CelestialBody, renderVertexLayout, SkyBoxVertex } from './schemas.ts'; +import { + type CelestialBody, + renderVertexLayout, + SkyBoxVertex, +} from './schemas.ts'; function vert( position: [number, number, number], @@ -163,6 +166,7 @@ export async function loadSphereTextures(root: TgpuRoot) { return texture; } -export const radiusOf = tgpu.fn([CelestialBody], d.f32)((body) => - std.pow((body.mass * 0.75) / Math.PI, 0.333) * body.radiusMultiplier -); +export const radiusOf = (body: CelestialBody): number => { + 'use gpu'; + return (((body.mass * 0.75) / Math.PI) ** 0.333) * body.radiusMultiplier; +}; diff --git a/apps/typegpu-docs/src/examples/simulation/gravity/render.ts b/apps/typegpu-docs/src/examples/simulation/gravity/render.ts index 3ead80a8a4..b88d7e1238 100644 --- a/apps/typegpu-docs/src/examples/simulation/gravity/render.ts +++ b/apps/typegpu-docs/src/examples/simulation/gravity/render.ts @@ -4,7 +4,6 @@ import * as std from 'typegpu/std'; import { radiusOf } from './helpers.ts'; import { cameraAccess, - CelestialBody, filteringSamplerSlot, lightSourceAccess, renderBindGroupLayout as renderLayout, @@ -54,32 +53,17 @@ export const mainVertex = tgpu['~unstable'].vertexFn({ }, out: VertexOutput, })((input) => { - // TODO: replace it with struct copy when Chromium is fixed - const currentBody = CelestialBody({ - position: renderLayout.$.celestialBodies[input.instanceIndex].position, - velocity: renderLayout.$.celestialBodies[input.instanceIndex].velocity, - mass: renderLayout.$.celestialBodies[input.instanceIndex].mass, - collisionBehavior: - renderLayout.$.celestialBodies[input.instanceIndex].collisionBehavior, - textureIndex: - renderLayout.$.celestialBodies[input.instanceIndex].textureIndex, - radiusMultiplier: - renderLayout.$.celestialBodies[input.instanceIndex].radiusMultiplier, - ambientLightFactor: - renderLayout.$.celestialBodies[input.instanceIndex].ambientLightFactor, - destroyed: renderLayout.$.celestialBodies[input.instanceIndex].destroyed, - }); + const currentBody = renderLayout.$.celestialBodies[input.instanceIndex]; - const worldPosition = std.add( - std.mul(radiusOf(currentBody), input.position.xyz), - currentBody.position, + const worldPosition = currentBody.position.add( + input.position.xyz.mul(radiusOf(currentBody)), ); const camera = cameraAccess.$; - const positionOnCanvas = std.mul( - camera.projection, - std.mul(camera.view, d.vec4f(worldPosition, 1)), - ); + const positionOnCanvas = camera.projection + .mul(camera.view) + .mul(d.vec4f(worldPosition, 1)); + return { position: positionOnCanvas, uv: input.uv, @@ -107,22 +91,16 @@ export const mainFragment = tgpu['~unstable'].fragmentFn({ input.sphereTextureIndex, ).xyz; - const ambient = std.mul( - input.ambientLightFactor, - std.mul(textureColor, lightColor), - ); + const ambient = textureColor.mul(lightColor).mul(input.ambientLightFactor); const normal = input.normals; const lightDirection = std.normalize( - std.sub(lightSourceAccess.$, input.worldPosition), + lightSourceAccess.$.sub(input.worldPosition), ); const cosTheta = std.dot(normal, lightDirection); - const diffuse = std.mul( - std.max(0, cosTheta), - std.mul(textureColor, lightColor), - ); + const diffuse = textureColor.mul(lightColor).mul(std.max(0, cosTheta)); - const litColor = std.add(ambient, diffuse); + const litColor = ambient.add(diffuse); return d.vec4f(litColor.xyz, 1); }); diff --git a/apps/typegpu-docs/src/examples/simulation/gravity/schemas.ts b/apps/typegpu-docs/src/examples/simulation/gravity/schemas.ts index 61f64bbcd0..3b3c444a88 100644 --- a/apps/typegpu-docs/src/examples/simulation/gravity/schemas.ts +++ b/apps/typegpu-docs/src/examples/simulation/gravity/schemas.ts @@ -2,6 +2,7 @@ import tgpu, { type TgpuSampler } from 'typegpu'; import * as d from 'typegpu/data'; import { Camera } from './setup-orbit-camera.ts'; +export type CelestialBody = d.Infer; export const CelestialBody = d.struct({ destroyed: d.u32, // boolean position: d.vec3f, diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts index ab68c910ee..70af959fe5 100644 --- a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts @@ -289,8 +289,6 @@ function frame(now: number) { .with(renderBindGroups[1 - currentTexture]) .draw(3); - root['~unstable'].flush(); - currentTexture = 1 - currentTexture; requestAnimationFrame(frame); diff --git a/apps/typegpu-docs/src/examples/simulation/stable-fluid/simulation.ts b/apps/typegpu-docs/src/examples/simulation/stable-fluid/simulation.ts index 18e5ede654..574ddafd7b 100644 --- a/apps/typegpu-docs/src/examples/simulation/stable-fluid/simulation.ts +++ b/apps/typegpu-docs/src/examples/simulation/stable-fluid/simulation.ts @@ -111,10 +111,10 @@ export const advectFn = tgpu['~unstable'].computeFn({ const clampedPos = std.clamp( prevPos, d.vec2f(-0.5), - d.vec2f(std.sub(d.vec2f(texSize.xy), d.vec2f(0.5))), + d.vec2f(texSize.xy).sub(0.5), ); const normalizedPos = std.div( - std.add(clampedPos, d.vec2f(0.5)), + clampedPos.add(0.5), d.vec2f(texSize.xy), ); diff --git a/apps/typegpu-docs/src/examples/tests/tgsl-parsing-test/pointers.ts b/apps/typegpu-docs/src/examples/tests/tgsl-parsing-test/pointers.ts index a4209071c0..9c6b1fdbc0 100644 --- a/apps/typegpu-docs/src/examples/tests/tgsl-parsing-test/pointers.ts +++ b/apps/typegpu-docs/src/examples/tests/tgsl-parsing-test/pointers.ts @@ -5,30 +5,25 @@ import * as std from 'typegpu/std'; const SimpleStruct = d.struct({ vec: d.vec2f }); const modifyNumFn = tgpu.fn([d.ptrFn(d.u32)])((ptr) => { - ptr += 1; + ptr.$ += 1; }); const modifyVecFn = tgpu.fn([d.ptrFn(d.vec2f)])((ptr) => { - ptr.x += 1; + ptr.$.x += 1; }); const modifyStructFn = tgpu.fn([d.ptrFn(SimpleStruct)])((ptr) => { - ptr.vec.x += 1; -}); - -const privateNum = tgpu.privateVar(d.u32); -const modifyNumPrivate = tgpu.fn([d.ptrPrivate(d.u32)])((ptr) => { - ptr += 1; + ptr.$.vec.x += 1; }); const privateVec = tgpu.privateVar(d.vec2f); const modifyVecPrivate = tgpu.fn([d.ptrPrivate(d.vec2f)])((ptr) => { - ptr.x += 1; + ptr.$.x += 1; }); const privateStruct = tgpu.privateVar(SimpleStruct); const modifyStructPrivate = tgpu.fn([d.ptrPrivate(SimpleStruct)])((ptr) => { - ptr.vec.x += 1; + ptr.$.vec.x += 1; }); // TODO: replace `s = s &&` with `s &&=` when implemented @@ -36,26 +31,23 @@ export const pointersTest = tgpu.fn([], d.bool)(() => { let s = true; // function pointers - const num = d.u32(); + const num = d.ref(d.u32()); modifyNumFn(num); - s = s && (num === 1); + s = s && (num.$ === 1); - const vec = d.vec2f(); + const vec = d.ref(d.vec2f()); modifyVecFn(vec); - s = s && std.allEq(vec, d.vec2f(1, 0)); + s = s && std.allEq(vec.$, d.vec2f(1, 0)); - const myStruct = SimpleStruct(); + const myStruct = d.ref(SimpleStruct()); modifyStructFn(myStruct); - s = s && std.allEq(myStruct.vec, d.vec2f(1, 0)); + s = s && std.allEq(myStruct.$.vec, d.vec2f(1, 0)); // private pointers - modifyNumPrivate(privateNum.$); - s = s && (privateNum.$ === 1); - - modifyVecPrivate(privateVec.$); + modifyVecPrivate(d.ref(privateVec.$)); s = s && std.allEq(privateVec.$, d.vec2f(1, 0)); - modifyStructPrivate(privateStruct.$); + modifyStructPrivate(d.ref(privateStruct.$)); s = s && std.allEq(privateStruct.$.vec, d.vec2f(1, 0)); return s; diff --git a/apps/typegpu-docs/src/examples/tests/wgsl-resolution/index.ts b/apps/typegpu-docs/src/examples/tests/wgsl-resolution/index.ts index b9a69e70a6..7f2e51cecc 100644 --- a/apps/typegpu-docs/src/examples/tests/wgsl-resolution/index.ts +++ b/apps/typegpu-docs/src/examples/tests/wgsl-resolution/index.ts @@ -165,7 +165,7 @@ const mainCompute = tgpu['~unstable'].computeFn({ instanceInfo.position = std.add(instanceInfo.position, instanceInfo.velocity); - nextTrianglePos.value[index] = instanceInfo; + nextTrianglePos.value[index] = TriangleData(instanceInfo); }).$name('compute shader'); // WGSL resolution diff --git a/packages/tinyest-for-wgsl/src/parsers.ts b/packages/tinyest-for-wgsl/src/parsers.ts index 644ad99a50..ff619b204f 100644 --- a/packages/tinyest-for-wgsl/src/parsers.ts +++ b/packages/tinyest-for-wgsl/src/parsers.ts @@ -24,73 +24,6 @@ function isDeclared(ctx: Context, name: string) { return ctx.stack.some((scope) => scope.declaredNames.includes(name)); } -const BINARY_OP_MAP = { - '==': '==', - '!=': '!=', - '===': '==', - '!==': '!=', - '<': '<', - '<=': '<=', - '>': '>', - '>=': '>=', - '<<': '<<', - '>>': '>>', - get '>>>'(): never { - throw new Error('The `>>>` operator is unsupported in TGSL.'); - }, - '+': '+', - '-': '-', - '*': '*', - '/': '/', - '%': '%', - '|': '|', - '^': '^', - '&': '&', - get in(): never { - throw new Error('The `in` operator is unsupported in TGSL.'); - }, - get instanceof(): never { - throw new Error('The `instanceof` operator is unsupported in TGSL.'); - }, - '**': '**', - get '|>'(): never { - throw new Error('The `|>` operator is unsupported in TGSL.'); - }, -} as const; - -const LOGICAL_OP_MAP = { - '||': '||', - '&&': '&&', - get '??'(): never { - throw new Error('The `??` operator is unsupported in TGSL.'); - }, -} as const; - -const ASSIGNMENT_OP_MAP = { - '=': '=', - '+=': '+=', - '-=': '-=', - '*=': '*=', - '/=': '/=', - '%=': '%=', - '<<=': '<<=', - '>>=': '>>=', - get '>>>='(): never { - throw new Error('The `>>>=` operator is unsupported in TGSL.'); - }, - '|=': '|=', - '^=': '^=', - '&=': '&=', - get '**='(): never { - throw new Error('The `**=` operator is unsupported in TGSL.'); - }, - '||=': '||=', - '&&=': '&&=', - get '??='(): never { - throw new Error('The `??=` operator is unsupported in TGSL.'); - }, -} as const; - const Transpilers: Partial< { [Type in JsNode['type']]: ( @@ -144,24 +77,36 @@ const Transpilers: Partial< }, BinaryExpression(ctx, node) { - const wgslOp = BINARY_OP_MAP[node.operator]; const left = transpile(ctx, node.left) as tinyest.Expression; const right = transpile(ctx, node.right) as tinyest.Expression; - return [NODE.binaryExpr, left, wgslOp, right]; + return [ + NODE.binaryExpr, + left, + node.operator as tinyest.BinaryOperator, + right, + ]; }, LogicalExpression(ctx, node) { - const wgslOp = LOGICAL_OP_MAP[node.operator]; const left = transpile(ctx, node.left) as tinyest.Expression; const right = transpile(ctx, node.right) as tinyest.Expression; - return [NODE.logicalExpr, left, wgslOp, right]; + return [ + NODE.logicalExpr, + left, + node.operator as tinyest.LogicalOperator, + right, + ]; }, AssignmentExpression(ctx, node) { - const wgslOp = ASSIGNMENT_OP_MAP[node.operator as acorn.AssignmentOperator]; const left = transpile(ctx, node.left) as tinyest.Expression; const right = transpile(ctx, node.right) as tinyest.Expression; - return [NODE.assignmentExpr, left, wgslOp, right]; + return [ + NODE.assignmentExpr, + left, + node.operator as tinyest.AssignmentOperator, + right, + ]; }, UnaryExpression(ctx, node) { diff --git a/packages/tinyest/src/nodes.ts b/packages/tinyest/src/nodes.ts index 6f073ff321..40bb78efab 100644 --- a/packages/tinyest/src/nodes.ts +++ b/packages/tinyest/src/nodes.ts @@ -118,12 +118,15 @@ export type Statement = export type BinaryOperator = | '==' | '!=' + | '===' + | '!==' | '<' | '<=' | '>' | '>=' | '<<' | '>>' + | '>>>' | '+' | '-' | '*' @@ -132,6 +135,8 @@ export type BinaryOperator = | '|' | '^' | '&' + | 'in' + | 'instanceof' | '**'; export type BinaryExpression = readonly [ @@ -155,7 +160,9 @@ export type AssignmentOperator = | '&=' | '**=' | '||=' - | '&&='; + | '&&=' + | '>>>=' + | '??='; export type AssignmentExpression = readonly [ type: NodeTypeCatalog['assignmentExpr'], @@ -164,7 +171,7 @@ export type AssignmentExpression = readonly [ rhs: Expression, ]; -export type LogicalOperator = '&&' | '||'; +export type LogicalOperator = '&&' | '||' | '??'; export type LogicalExpression = readonly [ type: NodeTypeCatalog['logicalExpr'], diff --git a/packages/typegpu-noise/src/generator.ts b/packages/typegpu-noise/src/generator.ts index c05c0f2035..67774b1a55 100644 --- a/packages/typegpu-noise/src/generator.ts +++ b/packages/typegpu-noise/src/generator.ts @@ -25,7 +25,7 @@ export const BPETER: StatefulGenerator = (() => { }), seed2: tgpu.fn([d.vec2f])((value) => { - seed.value = value; + seed.value = d.vec2f(value); }), seed3: tgpu.fn([d.vec3f])((value) => { diff --git a/packages/typegpu-sdf/src/2d.ts b/packages/typegpu-sdf/src/2d.ts index 8b0c7a54fc..88ff0bf2a2 100644 --- a/packages/typegpu-sdf/src/2d.ts +++ b/packages/typegpu-sdf/src/2d.ts @@ -153,7 +153,7 @@ export const sdBezierApprox = tgpu.fn( }); export const sdPie = tgpu.fn([vec2f, vec2f, f32], f32)((p, c, r) => { - const p_w = p; + const p_w = vec2f(p); p_w.x = abs(p.x); const l = length(p_w) - r; const m = length(p_w.sub(c.mul(clamp(dot(p_w, c), 0, r)))); diff --git a/packages/typegpu/src/core/buffer/bufferUsage.ts b/packages/typegpu/src/core/buffer/bufferUsage.ts index 4c3578a500..b83e880aae 100644 --- a/packages/typegpu/src/core/buffer/bufferUsage.ts +++ b/packages/typegpu/src/core/buffer/bufferUsage.ts @@ -1,7 +1,11 @@ import type { AnyData } from '../../data/dataTypes.ts'; import { schemaCallWrapper } from '../../data/schemaCallWrapper.ts'; import { type ResolvedSnippet, snip } from '../../data/snippet.ts'; -import type { AnyWgslData, BaseData } from '../../data/wgslTypes.ts'; +import { + type AnyWgslData, + type BaseData, + isNaturallyEphemeral, +} from '../../data/wgslTypes.ts'; import { IllegalBufferAccessError } from '../../errors.ts'; import { getExecMode, inCodegenMode, isInsideTgpuFn } from '../../execMode.ts'; import { isUsableAsStorage, type StorageFlag } from '../../extension.ts'; @@ -126,7 +130,11 @@ class TgpuFixedBufferImpl< };`, ); - return snip(id, dataType); + return snip( + id, + dataType, + isNaturallyEphemeral(dataType) ? 'runtime' : this.usage, + ); } toString(): string { @@ -135,11 +143,16 @@ class TgpuFixedBufferImpl< get [$gpuValueOf](): InferGPU { const dataType = this.buffer.dataType; + const usage = this.usage; return new Proxy({ [$internal]: true, get [$ownSnippet]() { - return snip(this, dataType); + return snip( + this, + dataType, + isNaturallyEphemeral(dataType) ? 'runtime' : usage, + ); }, [$resolve]: (ctx) => ctx.resolve(this), toString: () => `${this.usage}:${getName(this) ?? ''}.$`, @@ -246,7 +259,11 @@ export class TgpuLaidOutBufferImpl< };`, ); - return snip(id, dataType); + return snip( + id, + dataType, + isNaturallyEphemeral(dataType) ? 'runtime' : this.usage, + ); } toString(): string { @@ -254,12 +271,17 @@ export class TgpuLaidOutBufferImpl< } get [$gpuValueOf](): InferGPU { - const schema = this.dataType as unknown as AnyData; + const schema = this.dataType as AnyData; + const usage = this.usage; return new Proxy({ [$internal]: true, get [$ownSnippet]() { - return snip(this, schema); + return snip( + this, + schema, + isNaturallyEphemeral(schema) ? 'runtime' : usage, + ); }, [$resolve]: (ctx) => ctx.resolve(this), toString: () => `${this.usage}:${getName(this) ?? ''}.$`, diff --git a/packages/typegpu/src/core/constant/tgpuConstant.ts b/packages/typegpu/src/core/constant/tgpuConstant.ts index 475b3afa08..5399be2b54 100644 --- a/packages/typegpu/src/core/constant/tgpuConstant.ts +++ b/packages/typegpu/src/core/constant/tgpuConstant.ts @@ -1,5 +1,8 @@ import { type ResolvedSnippet, snip } from '../../data/snippet.ts'; -import type { AnyWgslData } from '../../data/wgslTypes.ts'; +import { + type AnyWgslData, + isNaturallyEphemeral, +} from '../../data/wgslTypes.ts'; import { inCodegenMode } from '../../execMode.ts'; import type { TgpuNamable } from '../../shared/meta.ts'; import { getName, setName } from '../../shared/meta.ts'; @@ -17,11 +20,17 @@ import { valueProxyHandler } from '../valueProxyUtils.ts'; // Public API // ---------- +type DeepReadonly = T extends { [$internal]: unknown } ? T + : T extends unknown[] ? ReadonlyArray> + : T extends Record + ? { readonly [K in keyof T]: DeepReadonly } + : T; + export interface TgpuConst extends TgpuNamable { - readonly [$gpuValueOf]: InferGPU; - readonly value: InferGPU; - readonly $: InferGPU; + readonly [$gpuValueOf]: DeepReadonly>; + readonly value: DeepReadonly>; + readonly $: DeepReadonly>; readonly [$internal]: { /** Makes it differentiable on the type level. Does not exist at runtime. */ @@ -43,16 +52,35 @@ export function constant( // Implementation // -------------- +function deepFreeze(object: T): T { + // Retrieve the property names defined on object + const propNames = Reflect.ownKeys(object); + + // Freeze properties before freezing self + for (const name of propNames) { + // biome-ignore lint/suspicious/noExplicitAny: chill TypeScript + const value = (object as any)[name]; + + if ((value && typeof value === 'object') || typeof value === 'function') { + deepFreeze(value); + } + } + + return Object.freeze(object); +} + class TgpuConstImpl implements TgpuConst, SelfResolvable { readonly [$internal] = {}; - readonly #value: InferGPU; + readonly #value: DeepReadonly>; constructor( public readonly dataType: TDataType, value: InferGPU, ) { - this.#value = value; + this.#value = value && typeof value === 'object' + ? deepFreeze(value) as DeepReadonly> + : value as DeepReadonly>; } $name(label: string) { @@ -67,27 +95,38 @@ class TgpuConstImpl ctx.addDeclaration(`const ${id}: ${resolvedDataType} = ${resolvedValue};`); - return snip(id, this.dataType); + // Why not a ref? + // 1. On the WGSL side, we cannot take pointers to constants. + // 2. On the JS side, we copy the constant each time we access it, so we're safe. + return snip( + id, + this.dataType, + isNaturallyEphemeral(this.dataType) ? 'constant' : 'constant-ref', + ); } toString() { return `const:${getName(this) ?? ''}`; } - get [$gpuValueOf](): InferGPU { + get [$gpuValueOf](): DeepReadonly> { const dataType = this.dataType; return new Proxy({ [$internal]: true, get [$ownSnippet]() { - return snip(this, dataType); + return snip( + this, + dataType, + isNaturallyEphemeral(dataType) ? 'constant' : 'constant-ref', + ); }, [$resolve]: (ctx) => ctx.resolve(this), toString: () => `const:${getName(this) ?? ''}.$`, - }, valueProxyHandler) as InferGPU; + }, valueProxyHandler) as DeepReadonly>; } - get value(): InferGPU { + get $(): DeepReadonly> { if (inCodegenMode()) { return this[$gpuValueOf]; } @@ -95,7 +134,7 @@ class TgpuConstImpl return this.#value; } - get $(): InferGPU { - return this.value; + get value(): DeepReadonly> { + return this.$; } } diff --git a/packages/typegpu/src/core/declare/tgpuDeclare.ts b/packages/typegpu/src/core/declare/tgpuDeclare.ts index f36d707e81..aaa40c6f0e 100644 --- a/packages/typegpu/src/core/declare/tgpuDeclare.ts +++ b/packages/typegpu/src/core/declare/tgpuDeclare.ts @@ -60,7 +60,7 @@ class TgpuDeclareImpl implements TgpuDeclare, SelfResolvable { ); ctx.addDeclaration(replacedDeclaration); - return snip('', Void); + return snip('', Void, /* ref */ 'constant'); } toString() { diff --git a/packages/typegpu/src/core/function/dualImpl.ts b/packages/typegpu/src/core/function/dualImpl.ts index efd4dc6fa1..2c24f91cb1 100644 --- a/packages/typegpu/src/core/function/dualImpl.ts +++ b/packages/typegpu/src/core/function/dualImpl.ts @@ -5,16 +5,12 @@ import { type Snippet, } from '../../data/snippet.ts'; import { inCodegenMode } from '../../execMode.ts'; -import { type FnArgsConversionHint, getOwnSnippet } from '../../types.ts'; +import { type FnArgsConversionHint, isKnownAtComptime } from '../../types.ts'; import { setName } from '../../shared/meta.ts'; import { $internal } from '../../shared/symbols.ts'; import { tryConvertSnippet } from '../../tgsl/conversion.ts'; import type { AnyData } from '../../data/dataTypes.ts'; -function isKnownAtComptime(value: unknown): boolean { - return typeof value !== 'string' && getOwnSnippet(value) === undefined; -} - export function createDualImpl unknown>( jsImpl: T, gpuImpl: (...args: MapValueToSnippet>) => Snippet, @@ -52,6 +48,12 @@ interface DualImplOptions unknown> { | (( ...inArgTypes: MapValueToDataType> ) => { argTypes: AnyData[]; returnType: AnyData }); + /** + * Whether the function should skip trying to execute the "normal" implementation if + * all arguments are known at compile time. + * @default false + */ + readonly noComptime?: boolean | undefined; readonly ignoreImplicitCastWarning?: boolean | undefined; } @@ -68,27 +70,36 @@ export function dualImpl unknown>( const gpuImpl = (...args: MapValueToSnippet>) => { const { argTypes, returnType } = typeof options.signature === 'function' ? options.signature( - ...args.map((s) => s.dataType) as MapValueToDataType>, + ...args.map((s) => { + // Dereference implicit pointers + if (s.dataType.type === 'ptr' && s.dataType.implicit) { + return s.dataType.inner; + } + return s.dataType; + }) as MapValueToDataType>, ) : options.signature; const argSnippets = args as MapValueToSnippet>; - const converted = argSnippets.map((s, idx) => - tryConvertSnippet( - s, - argTypes[idx] as AnyData, - !options.ignoreImplicitCastWarning, - ) - ) as MapValueToSnippet>; + const converted = argSnippets.map((s, idx) => { + const argType = argTypes[idx] as AnyData | undefined; + if (!argType) { + throw new Error('Function called with invalid arguments'); + } + return tryConvertSnippet(s, argType, !options.ignoreImplicitCastWarning); + }) as MapValueToSnippet>; if ( - converted.every((s) => isKnownAtComptime(s.value)) && + !options.noComptime && + converted.every((s) => isKnownAtComptime(s)) && typeof options.normalImpl === 'function' ) { try { return snip( options.normalImpl(...converted.map((s) => s.value) as never[]), returnType, + // Functions give up ownership of their return value + /* ref */ 'constant', ); } catch (e) { // cpuImpl may in some cases be present but implemented only partially. @@ -100,7 +111,12 @@ export function dualImpl unknown>( } } - return snip(options.codegenImpl(...converted), returnType); + return snip( + options.codegenImpl(...converted), + returnType, + // Functions give up ownership of their return value + /* ref */ 'runtime', + ); }; const impl = ((...args: Parameters) => { @@ -119,6 +135,9 @@ export function dualImpl unknown>( value: { jsImpl: options.normalImpl, gpuImpl, + strictSignature: typeof options.signature !== 'function' + ? options.signature + : undefined, argConversionHint: 'keep', }, }); diff --git a/packages/typegpu/src/core/function/fnCore.ts b/packages/typegpu/src/core/function/fnCore.ts index 7cc3e652f0..6bbd2ff736 100644 --- a/packages/typegpu/src/core/function/fnCore.ts +++ b/packages/typegpu/src/core/function/fnCore.ts @@ -7,6 +7,8 @@ import { type Snippet, } from '../../data/snippet.ts'; import { + isNaturallyEphemeral, + isPtr, isWgslData, isWgslStruct, Void, @@ -135,7 +137,7 @@ export function createFnCore( } ctx.addDeclaration(`${fnAttribute}fn ${id}${header}${body}`); - return snip(id, returnType); + return snip(id, returnType, /* ref */ 'runtime'); } // get data generated by the plugin @@ -188,11 +190,25 @@ export function createFnCore( for (const [i, argType] of argTypes.entries()) { const astParam = ast.params[i]; + // We know if arguments are passed by reference or by value, because we + // enforce that based on the whether the argument is a pointer or not. + // + // It still applies for shell-less functions, since we determine the type + // of the argument based on the argument's referentiality. + // In other words, if we pass a reference to a function, it's typed as a pointer, + // otherwise it's typed as a value. + const origin = isPtr(argType) + ? argType.addressSpace === 'storage' + ? argType.access === 'read' ? 'readonly' : 'mutable' + : argType.addressSpace + : isNaturallyEphemeral(argType) + ? 'runtime' + : 'argument'; switch (astParam?.type) { case FuncParameterType.identifier: { const rawName = astParam.name; - const snippet = snip(ctx.makeNameValid(rawName), argType); + const snippet = snip(ctx.makeNameValid(rawName), argType, origin); args.push(snippet); if (snippet.value !== rawName) { argAliases.push([rawName, snippet]); @@ -200,21 +216,30 @@ export function createFnCore( break; } case FuncParameterType.destructuredObject: { - args.push(snip(`_arg_${i}`, argType)); - argAliases.push(...astParam.props.map(({ name, alias }) => - [ + args.push(snip(`_arg_${i}`, argType, origin)); + argAliases.push(...astParam.props.map(({ name, alias }) => { + // Undecorating, as the struct type can contain builtins + const destrType = undecorate( + (argTypes[i] as WgslStruct).propTypes[name], + ); + + const destrOrigin = isPtr(destrType) + ? destrType.addressSpace === 'storage' + ? destrType.access === 'read' ? 'readonly' : 'mutable' + : destrType.addressSpace + : isNaturallyEphemeral(destrType) + ? 'runtime' + : 'argument'; + + return [ alias, - snip( - `_arg_${i}.${name}`, - (argTypes[i] as WgslStruct) - .propTypes[name], - ), - ] as [string, Snippet] - )); + snip(`_arg_${i}.${name}`, destrType, destrOrigin), + ] as [string, Snippet]; + })); break; } case undefined: - args.push(snip(`_arg_${i}`, argType)); + args.push(snip(`_arg_${i}`, argType, origin)); } } @@ -232,7 +257,7 @@ export function createFnCore( }`, ); - return snip(id, actualReturnType); + return snip(id, actualReturnType, /* ref */ 'runtime'); }, }; diff --git a/packages/typegpu/src/core/function/shelllessImpl.ts b/packages/typegpu/src/core/function/shelllessImpl.ts index 02b249f602..7a824af85b 100644 --- a/packages/typegpu/src/core/function/shelllessImpl.ts +++ b/packages/typegpu/src/core/function/shelllessImpl.ts @@ -23,6 +23,7 @@ import { createFnCore } from './fnCore.ts'; */ export interface ShelllessImpl extends SelfResolvable { readonly resourceType: 'shellless-impl'; + readonly argTypes: AnyData[]; readonly [$getNameForward]: unknown; } @@ -36,13 +37,16 @@ export function createShelllessImpl( [$internal]: true, [$getNameForward]: core, resourceType: 'shellless-impl' as const, + argTypes, [$resolve](ctx: ResolutionCtx): ResolvedSnippet { return core.resolve(ctx, argTypes, undefined); }, toString(): string { - return `fn*:${getName(core) ?? ''}`; + return `fn*:${getName(core) ?? ''}(${ + argTypes.map((t) => t.toString()).join(', ') + })`; }, }; } diff --git a/packages/typegpu/src/core/function/tgpuFn.ts b/packages/typegpu/src/core/function/tgpuFn.ts index 4c5a3cc937..cd83970ce4 100644 --- a/packages/typegpu/src/core/function/tgpuFn.ts +++ b/packages/typegpu/src/core/function/tgpuFn.ts @@ -1,14 +1,10 @@ import type { AnyData } from '../../data/dataTypes.ts'; import type { DualFn } from '../../data/dualFn.ts'; -import { - type ResolvedSnippet, - snip, - type Snippet, -} from '../../data/snippet.ts'; +import type { ResolvedSnippet } from '../../data/snippet.ts'; import { schemaCallWrapper } from '../../data/schemaCallWrapper.ts'; import { Void } from '../../data/wgslTypes.ts'; import { ExecutionError } from '../../errors.ts'; -import { provideInsideTgpuFn } from '../../execMode.ts'; +import { getResolutionCtx, provideInsideTgpuFn } from '../../execMode.ts'; import type { TgpuNamable } from '../../shared/meta.ts'; import { getName, setName } from '../../shared/meta.ts'; import { isMarkedInternal } from '../../shared/symbols.ts'; @@ -16,7 +12,6 @@ import type { Infer } from '../../shared/repr.ts'; import { $getNameForward, $internal, - $ownSnippet, $providing, $resolve, } from '../../shared/symbols.ts'; @@ -36,7 +31,7 @@ import { type TgpuAccessor, type TgpuSlot, } from '../slot/slotTypes.ts'; -import { createDualImpl } from './dualImpl.ts'; +import { dualImpl } from './dualImpl.ts'; import { createFnCore, type FnCore } from './fnCore.ts'; import type { AnyFn, @@ -215,8 +210,11 @@ function createFn( }, } as This; - const call = createDualImpl>( - (...args) => + const call = dualImpl>({ + name: 'tgpuFnCall', + noComptime: true, + signature: { argTypes: shell.argTypes, returnType: shell.returnType }, + normalImpl: (...args) => provideInsideTgpuFn(() => { try { if (typeof implementation === 'string') { @@ -239,10 +237,14 @@ function createFn( throw new ExecutionError(err, [fn]); } }), - (...args) => snip(new FnCall(fn, args), shell.returnType), - 'tgpuFnCall', - shell.argTypes, - ); + codegenImpl: (...args) => { + // biome-ignore lint/style/noNonNullAssertion: it's there + const ctx = getResolutionCtx()!; + return ctx.withResetIndentLevel(() => + stitch`${ctx.resolve(fn).value}(${args})` + ); + }, + }); const fn = Object.assign(call, fnBase as This) as unknown as TgpuFn< ImplSchema @@ -296,12 +298,22 @@ function createBoundFunction( }, }; - const call = createDualImpl>( - (...args) => innerFn(...args), - (...args) => snip(new FnCall(fn, args), innerFn.shell.returnType), - 'tgpuFnCall', - innerFn.shell.argTypes, - ); + const call = dualImpl>({ + name: 'tgpuFnCall', + noComptime: true, + signature: { + argTypes: innerFn.shell.argTypes, + returnType: innerFn.shell.returnType, + }, + normalImpl: innerFn, + codegenImpl: (...args) => { + // biome-ignore lint/style/noNonNullAssertion: it's there + const ctx = getResolutionCtx()!; + return ctx.withResetIndentLevel(() => + stitch`${ctx.resolve(fn).value}(${args})` + ); + }, + }); const fn = Object.assign(call, fnBase) as unknown as TgpuFn; fn[$internal].implementation = innerFn[$internal].implementation; @@ -314,40 +326,5 @@ function createBoundFunction( }, }); - fn[$internal].implementation = innerFn[$internal].implementation; - return fn; } - -class FnCall implements SelfResolvable { - readonly [$internal] = true; - readonly [$ownSnippet]: Snippet; - readonly [$getNameForward]: unknown; - readonly #fn: TgpuFnBase; - readonly #params: Snippet[]; - - constructor( - fn: TgpuFnBase, - params: Snippet[], - ) { - this.#fn = fn; - this.#params = params; - this[$getNameForward] = fn; - this[$ownSnippet] = snip(this, this.#fn.shell.returnType); - } - - [$resolve](ctx: ResolutionCtx): ResolvedSnippet { - // We need to reset the indentation level during function body resolution to ignore the indentation level of the function call - return ctx.withResetIndentLevel(() => { - // TODO: Resolve the params first, then the function (just for consistency) - return snip( - stitch`${ctx.resolve(this.#fn).value}(${this.#params})`, - this.#fn.shell.returnType, - ); - }); - } - - toString() { - return `call:${getName(this) ?? ''}`; - } -} diff --git a/packages/typegpu/src/core/pipeline/computePipeline.ts b/packages/typegpu/src/core/pipeline/computePipeline.ts index 00e11c3a15..d2400c924e 100644 --- a/packages/typegpu/src/core/pipeline/computePipeline.ts +++ b/packages/typegpu/src/core/pipeline/computePipeline.ts @@ -250,7 +250,7 @@ class ComputePipelineCore implements SelfResolvable { [$resolve](ctx: ResolutionCtx) { return ctx.withSlots(this._slotBindings, () => { ctx.resolve(this._entryFn); - return snip('', Void); + return snip('', Void, /* ref */ 'runtime'); }); } diff --git a/packages/typegpu/src/core/pipeline/renderPipeline.ts b/packages/typegpu/src/core/pipeline/renderPipeline.ts index 7d28baca8c..98dd59c1b1 100644 --- a/packages/typegpu/src/core/pipeline/renderPipeline.ts +++ b/packages/typegpu/src/core/pipeline/renderPipeline.ts @@ -724,7 +724,7 @@ class RenderPipelineCore implements SelfResolvable { if (fragmentFn) { ctx.resolve(fragmentFn); } - return snip('', Void); + return snip('', Void, /* ref */ 'runtime'); }), ); } diff --git a/packages/typegpu/src/core/resolve/tgpuResolve.ts b/packages/typegpu/src/core/resolve/tgpuResolve.ts index 146d9a8d42..89c991deb4 100644 --- a/packages/typegpu/src/core/resolve/tgpuResolve.ts +++ b/packages/typegpu/src/core/resolve/tgpuResolve.ts @@ -107,6 +107,7 @@ export function resolveWithContext( return snip( replaceExternalsInWgsl(ctx, dependencies, template ?? ''), Void, + /* ref */ 'runtime', ); }, diff --git a/packages/typegpu/src/core/root/init.ts b/packages/typegpu/src/core/root/init.ts index d3fc3a1cbd..75da0df76e 100644 --- a/packages/typegpu/src/core/root/init.ts +++ b/packages/typegpu/src/core/root/init.ts @@ -48,6 +48,7 @@ import { type VertexFlag, } from '../buffer/buffer.ts'; import { + type TgpuBufferShorthand, TgpuBufferShorthandImpl, type TgpuMutable, type TgpuReadonly, @@ -203,7 +204,12 @@ class WithBindingImpl implements WithBinding { with( slot: TgpuSlot | TgpuAccessor, - value: T | TgpuFn<() => T> | TgpuBufferUsage | Infer, + value: + | T + | TgpuFn<() => T> + | TgpuBufferUsage + | TgpuBufferShorthand + | Infer, ): WithBinding { return new WithBindingImpl(this._getRoot, [ ...this._slotBindings, diff --git a/packages/typegpu/src/core/sampler/sampler.ts b/packages/typegpu/src/core/sampler/sampler.ts index 2a8099acf7..ea94af10c5 100644 --- a/packages/typegpu/src/core/sampler/sampler.ts +++ b/packages/typegpu/src/core/sampler/sampler.ts @@ -129,7 +129,7 @@ export class TgpuLaidOutSamplerImpl< };`, ); - return snip(id, this.schema); + return snip(id, this.schema, /* ref */ 'handle'); } get [$gpuValueOf](): Infer { @@ -138,7 +138,7 @@ export class TgpuLaidOutSamplerImpl< { [$internal]: true, get [$ownSnippet]() { - return snip(this, schema); + return snip(this, schema, /* ref */ 'handle'); }, [$resolve]: (ctx) => ctx.resolve(this), toString: () => `${this.toString()}.$`, @@ -226,7 +226,7 @@ class TgpuFixedSamplerImpl };`, ); - return snip(id, this.schema); + return snip(id, this.schema, /* ref */ 'handle'); } get [$gpuValueOf](): Infer { @@ -235,7 +235,7 @@ class TgpuFixedSamplerImpl { [$internal]: true, get [$ownSnippet]() { - return snip(this, schema); + return snip(this, schema, /* ref */ 'handle'); }, [$resolve]: (ctx) => ctx.resolve(this), toString: () => `${this.toString()}.$`, diff --git a/packages/typegpu/src/core/slot/accessor.ts b/packages/typegpu/src/core/slot/accessor.ts index dc711a4211..5f8c07c60c 100644 --- a/packages/typegpu/src/core/slot/accessor.ts +++ b/packages/typegpu/src/core/slot/accessor.ts @@ -1,3 +1,4 @@ +import { schemaCallWrapper } from '../../data/schemaCallWrapper.ts'; import { type ResolvedSnippet, snip } from '../../data/snippet.ts'; import type { AnyWgslData } from '../../data/wgslTypes.ts'; import { getResolutionCtx, inCodegenMode } from '../../execMode.ts'; @@ -10,7 +11,12 @@ import { $ownSnippet, $resolve, } from '../../shared/symbols.ts'; -import type { ResolutionCtx, SelfResolvable } from '../../types.ts'; +import { + getOwnSnippet, + type ResolutionCtx, + type SelfResolvable, +} from '../../types.ts'; +import type { TgpuBufferShorthand } from '../buffer/bufferShorthand.ts'; import type { TgpuBufferUsage } from '../buffer/bufferUsage.ts'; import { isTgpuFn, type TgpuFn } from '../function/tgpuFn.ts'; import { @@ -26,7 +32,11 @@ import type { TgpuAccessor, TgpuSlot } from './slotTypes.ts'; export function accessor( schema: T, - defaultValue?: TgpuFn<() => T> | TgpuBufferUsage | Infer, + defaultValue?: + | TgpuFn<() => T> + | TgpuBufferUsage + | TgpuBufferShorthand + | Infer, ): TgpuAccessor { return new TgpuAccessorImpl(schema, defaultValue); } @@ -40,13 +50,16 @@ export class TgpuAccessorImpl readonly [$internal] = true; readonly [$getNameForward]: unknown; readonly resourceType = 'accessor'; - readonly slot: TgpuSlot T> | TgpuBufferUsage | Infer>; + readonly slot: TgpuSlot< + TgpuFn<() => T> | TgpuBufferUsage | TgpuBufferShorthand | Infer + >; constructor( public readonly schema: T, public readonly defaultValue: | TgpuFn<() => T> | TgpuBufferUsage + | TgpuBufferShorthand | Infer | undefined = undefined, ) { @@ -75,7 +88,16 @@ export class TgpuAccessorImpl return value[$internal].gpuImpl(); } - return snip(value, this.schema); + const ownSnippet = getOwnSnippet(value); + if (ownSnippet) { + return ownSnippet; + } + + // Doing a deep copy each time so that we don't have to deal with refs + return schemaCallWrapper( + this.schema, + snip(value, this.schema, /* ref */ 'constant'), + ); } $name(label: string) { @@ -106,6 +128,7 @@ export class TgpuAccessorImpl return snip( ctx.resolve(snippet.value, snippet.dataType).value, snippet.dataType as T, + snippet.origin, ); } } diff --git a/packages/typegpu/src/core/slot/slotTypes.ts b/packages/typegpu/src/core/slot/slotTypes.ts index efe335745a..4008dfbc41 100644 --- a/packages/typegpu/src/core/slot/slotTypes.ts +++ b/packages/typegpu/src/core/slot/slotTypes.ts @@ -2,6 +2,7 @@ import type { AnyData } from '../../data/dataTypes.ts'; import type { TgpuNamable } from '../../shared/meta.ts'; import type { GPUValueOf, Infer, InferGPU } from '../../shared/repr.ts'; import { $gpuValueOf, $internal, $providing } from '../../shared/symbols.ts'; +import type { TgpuBufferShorthand } from '../buffer/bufferShorthand.ts'; import type { TgpuFn } from '../function/tgpuFn.ts'; import type { TgpuBufferUsage } from './../buffer/bufferUsage.ts'; @@ -50,9 +51,12 @@ export interface TgpuAccessor extends TgpuNamable { readonly defaultValue: | TgpuFn<() => T> | TgpuBufferUsage + | TgpuBufferShorthand | Infer | undefined; - readonly slot: TgpuSlot T> | TgpuBufferUsage | Infer>; + readonly slot: TgpuSlot< + TgpuFn<() => T> | TgpuBufferUsage | TgpuBufferShorthand | Infer + >; readonly [$gpuValueOf]: InferGPU; readonly value: InferGPU; diff --git a/packages/typegpu/src/core/texture/externalTexture.ts b/packages/typegpu/src/core/texture/externalTexture.ts index 0ece389fa9..1892b5df9f 100644 --- a/packages/typegpu/src/core/texture/externalTexture.ts +++ b/packages/typegpu/src/core/texture/externalTexture.ts @@ -58,7 +58,7 @@ export class TgpuExternalTextureImpl };`, ); - return snip(id, textureExternal()); + return snip(id, textureExternal(), 'handle'); } get [$gpuValueOf](): Infer { @@ -68,7 +68,7 @@ export class TgpuExternalTextureImpl { [$internal]: true, get [$ownSnippet]() { - return snip(this, schema); + return snip(this, schema, 'handle'); }, [$resolve]: (ctx) => ctx.resolve(this), toString: () => `textureExternal:${getName(this) ?? ''}.$`, diff --git a/packages/typegpu/src/core/texture/texture.ts b/packages/typegpu/src/core/texture/texture.ts index f06145d99b..01cd9e2b0e 100644 --- a/packages/typegpu/src/core/texture/texture.ts +++ b/packages/typegpu/src/core/texture/texture.ts @@ -648,7 +648,7 @@ class TgpuFixedTextureViewImpl { [$internal]: true, get [$ownSnippet]() { - return snip(this, schema); + return snip(this, schema, /* ref */ 'handle'); }, [$resolve]: (ctx) => ctx.resolve(this), toString: () => `${this.toString()}.$`, @@ -696,7 +696,7 @@ class TgpuFixedTextureViewImpl };`, ); - return snip(id, this.schema); + return snip(id, this.schema, /* ref */ 'handle'); } } @@ -731,7 +731,7 @@ export class TgpuLaidOutTextureViewImpl< };`, ); - return snip(id, this.schema); + return snip(id, this.schema, /* ref */ 'handle'); } get [$gpuValueOf](): Infer { @@ -740,7 +740,7 @@ export class TgpuLaidOutTextureViewImpl< { [$internal]: true, get [$ownSnippet]() { - return snip(this, schema); + return snip(this, schema, /* ref */ 'handle'); }, [$resolve]: (ctx) => ctx.resolve(this), toString: () => `${this.toString()}.$`, diff --git a/packages/typegpu/src/core/valueProxyUtils.ts b/packages/typegpu/src/core/valueProxyUtils.ts index 6cf30fe172..7851a62330 100644 --- a/packages/typegpu/src/core/valueProxyUtils.ts +++ b/packages/typegpu/src/core/valueProxyUtils.ts @@ -1,8 +1,7 @@ -import type { AnyData } from '../data/dataTypes.ts'; -import { snip, type Snippet } from '../data/snippet.ts'; +import type { Snippet } from '../data/snippet.ts'; import { getGPUValue } from '../getGPUValue.ts'; import { $internal, $ownSnippet, $resolve } from '../shared/symbols.ts'; -import { getTypeForPropAccess } from '../tgsl/generationHelpers.ts'; +import { accessProp } from '../tgsl/generationHelpers.ts'; import { getOwnSnippet, type SelfResolvable, @@ -30,20 +29,16 @@ export const valueProxyHandler: ProxyHandler< } const targetSnippet = getOwnSnippet(target) as Snippet; - const targetDataType = targetSnippet.dataType as AnyData; - const propType = getTypeForPropAccess(targetDataType, String(prop)); - if (propType.type === 'unknown') { + const accessed = accessProp(targetSnippet, String(prop)); + if (!accessed) { // Prop was not found, must be missing from this object return undefined; } return new Proxy({ [$internal]: true, - [$resolve]: (ctx) => - snip(`${ctx.resolve(target).value}.${String(prop)}`, propType), - get [$ownSnippet]() { - return snip(this, propType); - }, + [$resolve]: (ctx) => ctx.resolve(accessed.value, accessed.dataType), + [$ownSnippet]: accessed, toString: () => `${String(target)}.${prop}`, }, valueProxyHandler); }, diff --git a/packages/typegpu/src/core/variable/tgpuVariable.ts b/packages/typegpu/src/core/variable/tgpuVariable.ts index d82bb154c8..eaacfbd5fb 100644 --- a/packages/typegpu/src/core/variable/tgpuVariable.ts +++ b/packages/typegpu/src/core/variable/tgpuVariable.ts @@ -1,5 +1,6 @@ import type { AnyData } from '../../data/dataTypes.ts'; import { type ResolvedSnippet, snip } from '../../data/snippet.ts'; +import { isNaturallyEphemeral } from '../../data/wgslTypes.ts'; import { IllegalVarAccessError } from '../../errors.ts'; import { getExecMode, isInsideTgpuFn } from '../../execMode.ts'; import type { TgpuNamable } from '../../shared/meta.ts'; @@ -103,7 +104,11 @@ class TgpuVarImpl ctx.addDeclaration(`${pre};`); } - return snip(id, this.#dataType); + return snip( + id, + this.#dataType, + isNaturallyEphemeral(this.#dataType) ? 'runtime' : this.#scope, + ); } $name(label: string) { @@ -117,11 +122,12 @@ class TgpuVarImpl get [$gpuValueOf](): InferGPU { const dataType = this.#dataType; + const ref = isNaturallyEphemeral(dataType) ? 'runtime' : this.#scope; return new Proxy({ [$internal]: true, get [$ownSnippet]() { - return snip(this, dataType); + return snip(this, dataType, ref); }, [$resolve]: (ctx) => ctx.resolve(this), toString: () => `var:${getName(this) ?? ''}.$`, diff --git a/packages/typegpu/src/data/array.ts b/packages/typegpu/src/data/array.ts index 52873b4cb6..ff6430bf31 100644 --- a/packages/typegpu/src/data/array.ts +++ b/packages/typegpu/src/data/array.ts @@ -53,7 +53,7 @@ export const arrayOf = createDualImpl( // Marking so the WGSL generator lets this function through partial[$internal] = true; - return snip(partial, UnknownData); + return snip(partial, UnknownData, /* ref*/ 'runtime'); } if (typeof elementCount.value !== 'number') { @@ -65,6 +65,7 @@ export const arrayOf = createDualImpl( return snip( cpu_arrayOf(elementType.value as AnyWgslData, elementCount.value), elementType.value as AnyWgslData, + /* ref */ 'runtime', ); }, 'arrayOf', diff --git a/packages/typegpu/src/data/dataTypes.ts b/packages/typegpu/src/data/dataTypes.ts index 5d37ed82fd..11216a7e20 100644 --- a/packages/typegpu/src/data/dataTypes.ts +++ b/packages/typegpu/src/data/dataTypes.ts @@ -135,6 +135,17 @@ export function undecorate(data: AnyData): AnyData { return data; } +export function unptr(data: AnyData): AnyData { + if (data.type === 'ptr') { + return data.inner as AnyData; + } + return data; +} + +export function toStorable(schema: AnyData): AnyData { + return undecorate(unptr(undecorate(schema))); +} + const looseTypeLiterals = [ 'unstruct', 'disarray', diff --git a/packages/typegpu/src/data/disarray.ts b/packages/typegpu/src/data/disarray.ts index 68cf26317a..cfd0b3444d 100644 --- a/packages/typegpu/src/data/disarray.ts +++ b/packages/typegpu/src/data/disarray.ts @@ -59,7 +59,7 @@ export const disarrayOf = createDualImpl( // Marking so the WGSL generator lets this function through partial[$internal] = true; - return snip(partial, UnknownData); + return snip(partial, UnknownData, /* ref */ 'runtime'); } if (typeof elementCount.value !== 'number') { @@ -71,6 +71,7 @@ export const disarrayOf = createDualImpl( return snip( cpu_disarrayOf(elementType.value as AnyWgslData, elementCount.value), elementType.value as AnyWgslData, + /* ref */ 'runtime', ); }, 'disarrayOf', diff --git a/packages/typegpu/src/data/dualFn.ts b/packages/typegpu/src/data/dualFn.ts index d208460552..e63e3a330e 100644 --- a/packages/typegpu/src/data/dualFn.ts +++ b/packages/typegpu/src/data/dualFn.ts @@ -1,13 +1,19 @@ import type { $internal } from '../shared/symbols.ts'; import type { FnArgsConversionHint } from '../types.ts'; +import type { AnyData } from './dataTypes.ts'; import type { MapValueToSnippet, Snippet } from './snippet.ts'; -export type DualFn unknown> = +export type DualFn< + TImpl extends (...args: never[]) => unknown = (...args: never[]) => unknown, +> = & TImpl & { readonly [$internal]: { jsImpl: TImpl; gpuImpl: (...args: MapValueToSnippet>) => Snippet; argConversionHint: FnArgsConversionHint; + strictSignature?: + | { argTypes: AnyData[]; returnType: AnyData } + | undefined; }; }; diff --git a/packages/typegpu/src/data/index.ts b/packages/typegpu/src/data/index.ts index a4f026eeb8..d41be82d14 100644 --- a/packages/typegpu/src/data/index.ts +++ b/packages/typegpu/src/data/index.ts @@ -2,7 +2,10 @@ * @module typegpu/data */ -import { type InfixOperator, infixOperators } from '../tgsl/wgslGenerator.ts'; +import { + type InfixOperator, + infixOperators, +} from '../tgsl/generationHelpers.ts'; import { $internal } from '../shared/symbols.ts'; import { MatBase } from './matrix.ts'; import { VecBase } from './vectorImpl.ts'; @@ -190,6 +193,7 @@ export { unstruct } from './unstruct.ts'; export { mat2x2f, mat3x3f, mat4x4f, matToArray } from './matrix.ts'; export * from './vertexFormatData.ts'; export { atomic } from './atomic.ts'; +export { ref } from './ref.ts'; export { align, type AnyAttribute, diff --git a/packages/typegpu/src/data/matrix.ts b/packages/typegpu/src/data/matrix.ts index a011827714..d95bd547e8 100644 --- a/packages/typegpu/src/data/matrix.ts +++ b/packages/typegpu/src/data/matrix.ts @@ -93,7 +93,11 @@ function createMatSchema< }, // CODEGEN implementation (...args) => - snip(stitch`${options.type}(${args})`, schema as unknown as AnyData), + snip( + stitch`${options.type}(${args})`, + schema as unknown as AnyData, + /* ref */ 'runtime', + ), options.type, ); @@ -178,6 +182,7 @@ abstract class mat2x2Impl extends MatBase .join(', ') })`, mat2x2f, + /* ref */ 'runtime', ); } @@ -327,6 +332,7 @@ abstract class mat3x3Impl extends MatBase this[5] }, ${this[6]}, ${this[8]}, ${this[9]}, ${this[10]})`, mat3x3f, + /* ref */ 'runtime', ); } @@ -525,6 +531,7 @@ abstract class mat4x4Impl extends MatBase .join(', ') })`, mat4x4f, + /* ref */ 'runtime', ); } @@ -553,7 +560,7 @@ export const identity2 = createDualImpl( // CPU implementation () => mat2x2f(1, 0, 0, 1), // CODEGEN implementation - () => snip('mat2x2f(1, 0, 0, 1)', mat2x2f), + () => snip('mat2x2f(1, 0, 0, 1)', mat2x2f, /* ref */ 'runtime'), 'identity2', ); @@ -565,7 +572,8 @@ export const identity3 = createDualImpl( // CPU implementation () => mat3x3f(1, 0, 0, 0, 1, 0, 0, 0, 1), // CODEGEN implementation - () => snip('mat3x3f(1, 0, 0, 0, 1, 0, 0, 0, 1)', mat3x3f), + () => + snip('mat3x3f(1, 0, 0, 0, 1, 0, 0, 0, 1)', mat3x3f, /* ref */ 'runtime'), 'identity3', ); @@ -578,7 +586,11 @@ export const identity4 = createDualImpl( () => mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), // CODEGEN implementation () => - snip('mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)', mat4x4f), + snip( + 'mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)', + mat4x4f, + /* ref */ 'runtime', + ), 'identity4', ); @@ -608,6 +620,7 @@ export const translation4 = createDualImpl( snip( stitch`mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, ${v}.x, ${v}.y, ${v}.z, 1)`, mat4x4f, + /* ref */ 'runtime', ), 'translation4', ); @@ -632,6 +645,7 @@ export const scaling4 = createDualImpl( snip( stitch`mat4x4f(${v}.x, 0, 0, 0, 0, ${v}.y, 0, 0, 0, 0, ${v}.z, 0, 0, 0, 0, 1)`, mat4x4f, + /* ref */ 'runtime', ), 'scaling4', ); @@ -656,6 +670,7 @@ export const rotationX4 = createDualImpl( snip( stitch`mat4x4f(1, 0, 0, 0, 0, cos(${a}), sin(${a}), 0, 0, -sin(${a}), cos(${a}), 0, 0, 0, 0, 1)`, mat4x4f, + /* ref */ 'runtime', ), 'rotationX4', ); @@ -680,6 +695,7 @@ export const rotationY4 = createDualImpl( snip( stitch`mat4x4f(cos(${a}), 0, -sin(${a}), 0, 0, 1, 0, 0, sin(${a}), 0, cos(${a}), 0, 0, 0, 0, 1)`, mat4x4f, + /* ref */ 'runtime', ), 'rotationY4', ); @@ -704,6 +720,7 @@ export const rotationZ4 = createDualImpl( snip( stitch`mat4x4f(cos(${a}), sin(${a}), 0, 0, -sin(${a}), cos(${a}), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)`, mat4x4f, + /* ref */ 'runtime', ), 'rotationZ4', ); diff --git a/packages/typegpu/src/data/ptr.ts b/packages/typegpu/src/data/ptr.ts index ed5c28f1a7..3db83c5d53 100644 --- a/packages/typegpu/src/data/ptr.ts +++ b/packages/typegpu/src/data/ptr.ts @@ -1,4 +1,9 @@ import { $internal } from '../shared/symbols.ts'; +import { + type Origin, + type OriginToPtrParams, + originToPtrParams, +} from './snippet.ts'; import type { Access, AddressSpace, Ptr, StorableData } from './wgslTypes.ts'; export function ptrFn( @@ -38,7 +43,7 @@ export function ptrHandle( return INTERNAL_createPtr('handle', inner, 'read'); } -function INTERNAL_createPtr< +export function INTERNAL_createPtr< TAddressSpace extends AddressSpace, TInner extends StorableData, TAccess extends Access, @@ -46,6 +51,7 @@ function INTERNAL_createPtr< addressSpace: TAddressSpace, inner: TInner, access: TAccess, + implicit: boolean = false, ): Ptr { return { [$internal]: true, @@ -53,5 +59,42 @@ function INTERNAL_createPtr< addressSpace, inner, access, + implicit, + toString: () => `ptr<${addressSpace}, ${inner}, ${access}>`, } as Ptr; } + +export function createPtrFromOrigin( + origin: Origin, + innerDataType: StorableData, +): Ptr | undefined { + const ptrParams = originToPtrParams[origin as keyof OriginToPtrParams]; + + if (ptrParams) { + return INTERNAL_createPtr( + ptrParams.space, + innerDataType, + ptrParams.access, + ); + } + + return undefined; +} + +export function implicitFrom(ptr: Ptr): Ptr { + return INTERNAL_createPtr( + ptr.addressSpace, + ptr.inner, + ptr.access, + /* implicit */ true, + ); +} + +export function explicitFrom(ptr: Ptr): Ptr { + return INTERNAL_createPtr( + ptr.addressSpace, + ptr.inner, + ptr.access, + /* implicit */ false, + ); +} diff --git a/packages/typegpu/src/data/ref.ts b/packages/typegpu/src/data/ref.ts new file mode 100644 index 0000000000..7bc7ca1f3a --- /dev/null +++ b/packages/typegpu/src/data/ref.ts @@ -0,0 +1,183 @@ +import { stitch } from '../core/resolve/stitch.ts'; +import { invariant, WgslTypeError } from '../errors.ts'; +import { inCodegenMode } from '../execMode.ts'; +import { setName } from '../shared/meta.ts'; +import { $internal, $ownSnippet, $resolve } from '../shared/symbols.ts'; +import type { ResolutionCtx, SelfResolvable } from '../types.ts'; +import { UnknownData } from './dataTypes.ts'; +import type { DualFn } from './dualFn.ts'; +import { createPtrFromOrigin, explicitFrom } from './ptr.ts'; +import { type ResolvedSnippet, snip, type Snippet } from './snippet.ts'; +import { + isNaturallyEphemeral, + isPtr, + type Ptr, + type StorableData, +} from './wgslTypes.ts'; + +// ---------- +// Public API +// ---------- + +/** + * A reference to a value `T`. Can be passed to other functions to give them + * mutable access to the underlying value. + * + * Conceptually, it represents a WGSL pointer. + */ +export interface ref { + readonly [$internal]: unknown; + readonly type: 'ref'; + + /** + * Derefences the reference, and gives access to the underlying value. + * + * @example ```ts + * const boid = Boid({ pos: d.vec3f(3, 2, 1) }); + * const posRef = d.ref(boid.pos); + * + * // Actually updates `boid.pos` + * posRef.$ = d.vec3f(1, 2, 3); + * console.log(boid.pos); // Output: vec3f(1, 2, 3) + * ``` + */ + $: T; +} + +export const ref: DualFn<(value: T) => ref> = (() => { + const gpuImpl = (value: Snippet) => { + if (value.origin === 'argument') { + throw new WgslTypeError( + stitch`d.ref(${value}) is illegal, cannot take a reference of an argument. Copy the value locally first, and take a reference of the copy.`, + ); + } + + if (value.dataType.type === 'ptr') { + // This can happen if we take a reference of an *implicit* pointer, one + // made by assigning a reference to a `const`. + return snip(value.value, explicitFrom(value.dataType), value.origin); + } + + /** + * Pointer type only exists if the ref was created from a reference (buttery-butter). + * + * @example + * ```ts + * const life = ref(42); // created from a value + * const boid = ref(layout.$.boids[0]); // created from a reference + * ``` + */ + const ptrType = createPtrFromOrigin( + value.origin, + value.dataType as StorableData, + ); + return snip( + new RefOperator(value, ptrType), + ptrType ?? UnknownData, + /* origin */ 'runtime', + ); + }; + + const jsImpl = (value: T) => new refImpl(value); + + const impl = (value: T) => { + if (inCodegenMode()) { + return gpuImpl(value as Snippet); + } + return jsImpl(value); + }; + + setName(impl, 'ref'); + impl.toString = () => 'ref'; + Object.defineProperty(impl, $internal, { + value: { + jsImpl, + gpuImpl, + strictSignature: undefined, + argConversionHint: 'keep', + }, + }); + + return impl as unknown as DualFn<(value: T) => ref>; +})(); + +export function isRef(value: unknown | ref): value is ref { + return value instanceof refImpl; +} + +// -------------- +// Implementation +// -------------- + +class refImpl implements ref { + readonly [$internal]: true; + readonly type: 'ref'; + #value: T; + + constructor(value: T) { + this[$internal] = true; + this.type = 'ref'; + this.#value = value; + } + + get $(): T { + return this.#value as T; + } + + set $(value: T) { + if (value && typeof value === 'object') { + // Setting an object means updating the properties of the original object. + // e.g.: foo.$ = Boid(); + for (const key of Object.keys(value) as (keyof T)[]) { + this.#value[key] = value[key]; + } + } else { + this.#value = value; + } + } +} + +/** + * The result of calling `d.ref(...)`. The code responsible for + * generating shader code can check if the value of a snippet is + * an instance of `RefOperator`, and act accordingly. + */ +export class RefOperator implements SelfResolvable { + readonly [$internal]: true; + readonly snippet: Snippet; + + readonly #ptrType: Ptr | undefined; + + constructor(snippet: Snippet, ptrType: Ptr | undefined) { + this[$internal] = true; + this.snippet = snippet; + this.#ptrType = ptrType; + } + + get [$ownSnippet](): Snippet { + if (!this.#ptrType) { + throw new Error(stitch`Cannot take a reference of ${this.snippet}`); + } + return snip(this, this.#ptrType, this.snippet.origin); + } + + [$resolve](ctx: ResolutionCtx): ResolvedSnippet { + if (!this.#ptrType) { + throw new Error(stitch`Cannot take a reference of ${this.snippet}`); + } + return snip(stitch`(&${this.snippet})`, this.#ptrType, this.snippet.origin); + } +} + +export function derefSnippet(snippet: Snippet): Snippet { + invariant(isPtr(snippet.dataType), 'Only pointers can be dereferenced'); + + const innerType = snippet.dataType.inner; + const origin = isNaturallyEphemeral(innerType) ? 'runtime' : snippet.origin; + + if (snippet.value instanceof RefOperator) { + return snip(stitch`${snippet.value.snippet}`, innerType, origin); + } + + return snip(stitch`(*${snippet})`, innerType, origin); +} diff --git a/packages/typegpu/src/data/snippet.ts b/packages/typegpu/src/data/snippet.ts index bd0b651705..3c6a834acf 100644 --- a/packages/typegpu/src/data/snippet.ts +++ b/packages/typegpu/src/data/snippet.ts @@ -3,6 +3,46 @@ import type { AnyData, UnknownData } from './dataTypes.ts'; import { DEV } from '../shared/env.ts'; import { isNumericSchema } from './wgslTypes.ts'; +export type Origin = + | 'uniform' + | 'readonly' // equivalent to ptr + | 'mutable' // equivalent to ptr + | 'workgroup' + | 'private' + | 'function' + | 'handle' + // is an argument (or part of an argument) given to the + // function we're resolving. This includes primitives, to + // catch cases where we update an argument's primitive member + // prop, e.g.: `vec.x += 1;` + | 'argument' + // not a ref to anything, known at runtime + | 'runtime' + // not a ref to anything, known at pipeline creation time + // (not to be confused with 'comptime') + // note that this doesn't automatically mean the value can be stored in a `const` + // variable, more so that it's valid to do so in WGSL (but not necessarily safe to do in JS shaders) + | 'constant' + | 'constant-ref'; + +export function isEphemeralOrigin(space: Origin) { + return space === 'runtime' || space === 'constant' || space === 'argument'; +} + +export function isEphemeralSnippet(snippet: Snippet) { + return isEphemeralOrigin(snippet.origin); +} + +export const originToPtrParams = { + uniform: { space: 'uniform', access: 'read' }, + readonly: { space: 'storage', access: 'read' }, + mutable: { space: 'storage', access: 'read-write' }, + workgroup: { space: 'workgroup', access: 'read-write' }, + private: { space: 'private', access: 'read-write' }, + function: { space: 'function', access: 'read-write' }, +} as const; +export type OriginToPtrParams = typeof originToPtrParams; + export interface Snippet { readonly value: unknown; /** @@ -10,6 +50,7 @@ export interface Snippet { * E.g. `1.1` is assignable to `f32`, but `1.1` itself is an abstract float */ readonly dataType: AnyData | UnknownData; + readonly origin: Origin; } export interface ResolvedSnippet { @@ -19,6 +60,7 @@ export interface ResolvedSnippet { * E.g. `1.1` is assignable to `f32`, but `1.1` itself is an abstract float */ readonly dataType: AnyData; + readonly origin: Origin; } export type MapValueToSnippet = { [K in keyof T]: Snippet }; @@ -27,6 +69,7 @@ class SnippetImpl implements Snippet { constructor( readonly value: unknown, readonly dataType: AnyData | UnknownData, + readonly origin: Origin, ) {} } @@ -38,11 +81,20 @@ export function isSnippetNumeric(snippet: Snippet) { return isNumericSchema(snippet.dataType); } -export function snip(value: string, dataType: AnyData): ResolvedSnippet; -export function snip(value: unknown, dataType: AnyData | UnknownData): Snippet; +export function snip( + value: string, + dataType: AnyData, + origin: Origin, +): ResolvedSnippet; +export function snip( + value: unknown, + dataType: AnyData | UnknownData, + origin: Origin, +): Snippet; export function snip( value: unknown, dataType: AnyData | UnknownData, + origin: Origin, ): Snippet | ResolvedSnippet { if (DEV && isSnippet(value)) { // An early error, but not worth checking every time in production @@ -53,5 +105,6 @@ export function snip( value, // We don't care about attributes in snippet land, so we discard that information. undecorate(dataType as AnyData), + origin, ); } diff --git a/packages/typegpu/src/data/vector.ts b/packages/typegpu/src/data/vector.ts index 40c01b0faa..29d74a15ab 100644 --- a/packages/typegpu/src/data/vector.ts +++ b/packages/typegpu/src/data/vector.ts @@ -1,7 +1,7 @@ import { dualImpl } from '../core/function/dualImpl.ts'; import { stitch } from '../core/resolve/stitch.ts'; import { $repr } from '../shared/symbols.ts'; -import { type AnyData, undecorate } from './dataTypes.ts'; +import type { AnyData } from './dataTypes.ts'; import { bool, f16, f32, i32, u32 } from './numeric.ts'; import { Vec2bImpl, @@ -310,15 +310,18 @@ function makeVecSchema( const construct = dualImpl({ name: type, signature: (...args) => ({ - argTypes: args.map((arg) => { - const argType = undecorate(arg); - return isVec(argType) ? argType : primitive; - }), + argTypes: args.map((arg) => isVec(arg) ? arg : primitive), returnType: schema as AnyData, }), normalImpl: cpuConstruct, ignoreImplicitCastWarning: true, - codegenImpl: (...args) => stitch`${type}(${args})`, + codegenImpl: (...args) => { + if (args.length === 1 && args[0]?.dataType === schema) { + // Already typed as the schema + return stitch`${args[0]}`; + } + return stitch`${type}(${args})`; + }, }); const schema: diff --git a/packages/typegpu/src/data/vectorImpl.ts b/packages/typegpu/src/data/vectorImpl.ts index 4478422280..1944bd8fbc 100644 --- a/packages/typegpu/src/data/vectorImpl.ts +++ b/packages/typegpu/src/data/vectorImpl.ts @@ -41,12 +41,12 @@ export abstract class VecBase extends Array implements SelfResolvable { [$resolve](): ResolvedSnippet { const schema = this[$internal].elementSchema; if (this.every((e) => !e)) { - return snip(`${this.kind}()`, schema); + return snip(`${this.kind}()`, schema, /* ref */ 'constant'); } if (this.every((e) => this[0] === e)) { - return snip(`${this.kind}(${this[0]})`, schema); + return snip(`${this.kind}(${this[0]})`, schema, /* ref */ 'runtime'); } - return snip(`${this.kind}(${this.join(', ')})`, schema); + return snip(`${this.kind}(${this.join(', ')})`, schema, /* ref */ 'runtime'); } toString() { diff --git a/packages/typegpu/src/data/wgslTypes.ts b/packages/typegpu/src/data/wgslTypes.ts index 12c15b070f..1fd7ab1bc4 100644 --- a/packages/typegpu/src/data/wgslTypes.ts +++ b/packages/typegpu/src/data/wgslTypes.ts @@ -1,5 +1,4 @@ import type { TgpuNamable } from '../shared/meta.ts'; -import { isMarkedInternal } from '../shared/symbols.ts'; import type { ExtractInvalidSchemaError, Infer, @@ -24,7 +23,7 @@ import type { $validUniformSchema, $validVertexSchema, } from '../shared/symbols.ts'; -import { $internal } from '../shared/symbols.ts'; +import { $internal, isMarkedInternal } from '../shared/symbols.ts'; import type { Prettify, SwapNever } from '../shared/utilityTypes.ts'; import type { DualFn } from './dualFn.ts'; import type { @@ -33,6 +32,7 @@ import type { WgslTexture, } from './texture.ts'; import type { WgslComparisonSampler, WgslSampler } from './sampler.ts'; +import type { ref } from './ref.ts'; type DecoratedLocation = Decorated; @@ -1376,9 +1376,10 @@ export interface Ptr< readonly inner: TInner; readonly addressSpace: TAddr; readonly access: TAccess; + readonly implicit: boolean; // Type-tokens, not available at runtime - readonly [$repr]: Infer; + readonly [$repr]: ref>; readonly [$invalidSchemaReason]: 'Pointers are not host-shareable'; // --- } @@ -1601,7 +1602,8 @@ export type StorableData = | ScalarData | VecData | MatData - | Atomic + | Atomic + | Atomic | WgslArray | WgslStruct; @@ -1903,3 +1905,25 @@ export function isHalfPrecisionSchema( type === 'vec4h') ); } + +const ephemeralTypes = [ + 'abstractInt', + 'abstractFloat', + 'f32', + 'f16', + 'i32', + 'u32', + 'bool', +]; + +/** + * Returns true for schemas that are not naturally referential in JS (primitives). + * @param schema + * @returns + */ +export function isNaturallyEphemeral(schema: unknown): boolean { + return ( + !isMarkedInternal(schema) || + ephemeralTypes.includes((schema as BaseData)?.type) + ); +} diff --git a/packages/typegpu/src/errors.ts b/packages/typegpu/src/errors.ts index a4a9d6f9dd..1d4ff08abf 100644 --- a/packages/typegpu/src/errors.ts +++ b/packages/typegpu/src/errors.ts @@ -4,7 +4,7 @@ import type { TgpuVertexLayout } from './core/vertexLayout/vertexLayout.ts'; import type { AnyData, Disarray } from './data/dataTypes.ts'; import type { WgslArray } from './data/wgslTypes.ts'; import { getName, hasTinyestMetadata } from './shared/meta.ts'; -import { DEV } from './shared/env.ts'; +import { DEV, TEST } from './shared/env.ts'; import type { TgpuBindGroupLayout } from './tgpuBindGroupLayout.ts'; const prefix = 'Invariant failed'; @@ -22,7 +22,7 @@ export function invariant( } // In production we strip the message but still throw - if (!DEV) { + if (!DEV && !TEST) { throw new Error(prefix); } diff --git a/packages/typegpu/src/resolutionCtx.ts b/packages/typegpu/src/resolutionCtx.ts index b4bc895dc4..d9952c0f85 100644 --- a/packages/typegpu/src/resolutionCtx.ts +++ b/packages/typegpu/src/resolutionCtx.ts @@ -653,7 +653,8 @@ export class ResolutionCtxImpl implements ResolutionCtx { // If we got here, no item with the given slot-to-value combo exists in cache yet let result: ResolvedSnippet; if (isData(item)) { - result = snip(resolveData(this, item), Void); + // Ref is arbitrary, as we're resolving a schema + result = snip(resolveData(this, item), Void, /* ref */ 'runtime'); } else if (isDerived(item) || isSlot(item)) { result = this.resolve(this.unwrap(item)); } else if (isSelfResolvable(item)) { @@ -731,6 +732,7 @@ export class ResolutionCtxImpl implements ResolutionCtx { return snip( `${[...this._declarations].join('\n\n')}${result.value}`, Void, + /* ref */ 'runtime', // arbitrary ); } finally { this.popMode('codegen'); @@ -749,13 +751,13 @@ export class ResolutionCtxImpl implements ResolutionCtx { ); if (realSchema.type === 'abstractInt') { - return snip(`${item}`, realSchema); + return snip(`${item}`, realSchema, /* ref */ 'constant'); } if (realSchema.type === 'u32') { - return snip(`${item}u`, realSchema); + return snip(`${item}u`, realSchema, /* ref */ 'constant'); } if (realSchema.type === 'i32') { - return snip(`${item}i`, realSchema); + return snip(`${item}i`, realSchema, /* ref */ 'constant'); } const exp = item.toExponential(); @@ -767,21 +769,21 @@ export class ResolutionCtxImpl implements ResolutionCtx { // Just picking the shorter one const base = exp.length < decimal.length ? exp : decimal; if (realSchema.type === 'f32') { - return snip(`${base}f`, realSchema); + return snip(`${base}f`, realSchema, /* ref */ 'constant'); } if (realSchema.type === 'f16') { - return snip(`${base}h`, realSchema); + return snip(`${base}h`, realSchema, /* ref */ 'constant'); } - return snip(base, realSchema); + return snip(base, realSchema, /* ref */ 'constant'); } if (typeof item === 'boolean') { - return snip(item ? 'true' : 'false', bool); + return snip(item ? 'true' : 'false', bool, /* ref */ 'constant'); } if (typeof item === 'string') { // Already resolved - return snip(item, Void); + return snip(item, Void, /* ref */ 'runtime'); } if (schema && isWgslArray(schema)) { @@ -800,9 +802,16 @@ export class ResolutionCtxImpl implements ResolutionCtx { const elementTypeString = this.resolve(schema.elementType); return snip( stitch`array<${elementTypeString}, ${schema.elementCount}>(${ - item.map((element) => snip(element, schema.elementType as AnyData)) + item.map((element) => + snip( + element, + schema.elementType as AnyData, + /* ref */ 'runtime', + ) + ) })`, schema, + /* ref */ 'runtime', ); } @@ -810,6 +819,7 @@ export class ResolutionCtxImpl implements ResolutionCtx { return snip( stitch`array(${item.map((element) => this.resolve(element))})`, UnknownData, + /* ref */ 'runtime', ) as ResolvedSnippet; } @@ -817,10 +827,15 @@ export class ResolutionCtxImpl implements ResolutionCtx { return snip( stitch`${this.resolve(schema)}(${ Object.entries(schema.propTypes).map(([key, propType]) => - snip((item as Infer)[key], propType as AnyData) + snip( + (item as Infer)[key], + propType as AnyData, + /* ref */ 'runtime', + ) ) })`, schema, + /* ref */ 'runtime', // a new struct, not referenced from anywhere ); } diff --git a/packages/typegpu/src/std/array.ts b/packages/typegpu/src/std/array.ts index 2ab46689f4..3a00c4d21d 100644 --- a/packages/typegpu/src/std/array.ts +++ b/packages/typegpu/src/std/array.ts @@ -2,6 +2,7 @@ import { dualImpl } from '../core/function/dualImpl.ts'; import { stitch } from '../core/resolve/stitch.ts'; import { abstractInt, u32 } from '../data/numeric.ts'; import { ptrFn } from '../data/ptr.ts'; +import { isRef, type ref } from '../data/ref.ts'; import { isPtr, isWgslArray, type StorableData } from '../data/wgslTypes.ts'; const sizeOfPointedToArray = (dataType: unknown) => @@ -18,7 +19,8 @@ export const arrayLength = dualImpl({ returnType: sizeOfPointedToArray(ptrArg) > 0 ? abstractInt : u32, }); }, - normalImpl: (a: unknown[]) => a.length, + normalImpl: (a: unknown[] | ref) => + isRef(a) ? a.$.length : a.length, codegenImpl(a) { const length = sizeOfPointedToArray(a.dataType); return length > 0 ? String(length) : stitch`arrayLength(${a})`; diff --git a/packages/typegpu/src/std/atomic.ts b/packages/typegpu/src/std/atomic.ts index ee17a07b69..373b0cffd7 100644 --- a/packages/typegpu/src/std/atomic.ts +++ b/packages/typegpu/src/std/atomic.ts @@ -16,7 +16,7 @@ export const workgroupBarrier = createDualImpl( // CPU implementation () => console.warn('workgroupBarrier is a no-op outside of CODEGEN mode.'), // CODEGEN implementation - () => snip('workgroupBarrier()', Void), + () => snip('workgroupBarrier()', Void, /* ref */ 'runtime'), 'workgroupBarrier', ); @@ -24,7 +24,7 @@ export const storageBarrier = createDualImpl( // CPU implementation () => console.warn('storageBarrier is a no-op outside of CODEGEN mode.'), // CODEGEN implementation - () => snip('storageBarrier()', Void), + () => snip('storageBarrier()', Void, /* ref */ 'runtime'), 'storageBarrier', ); @@ -32,7 +32,7 @@ export const textureBarrier = createDualImpl( // CPU implementation () => console.warn('textureBarrier is a no-op outside of CODEGEN mode.'), // CODEGEN implementation - () => snip('textureBarrier()', Void), + () => snip('textureBarrier()', Void, /* ref */ 'runtime'), 'textureBarrier', ); @@ -46,7 +46,11 @@ export const atomicLoad = createDualImpl( // CODEGEN implementation (a) => { if (isWgslData(a.dataType) && a.dataType.type === 'atomic') { - return snip(stitch`atomicLoad(&${a})`, a.dataType.inner); + return snip( + stitch`atomicLoad(&${a})`, + a.dataType.inner, + /* ref */ 'runtime', + ); } throw new Error( `Invalid atomic type: ${safeStringify(a.dataType)}`, @@ -69,7 +73,11 @@ export const atomicStore = createDualImpl( `Invalid atomic type: ${safeStringify(a.dataType)}`, ); } - return snip(stitch`atomicStore(&${a}, ${value})`, Void); + return snip( + stitch`atomicStore(&${a}, ${value})`, + Void, + /* ref */ 'runtime', + ); }, 'atomicStore', ); @@ -91,7 +99,11 @@ export const atomicAdd = createDualImpl( // CODEGEN implementation (a, value) => { if (isWgslData(a.dataType) && a.dataType.type === 'atomic') { - return snip(stitch`atomicAdd(&${a}, ${value})`, a.dataType.inner); + return snip( + stitch`atomicAdd(&${a}, ${value})`, + a.dataType.inner, + /* ref */ 'runtime', + ); } throw new Error( `Invalid atomic type: ${safeStringify(a.dataType)}`, @@ -111,7 +123,11 @@ export const atomicSub = createDualImpl( // CODEGEN implementation (a, value) => { if (isWgslData(a.dataType) && a.dataType.type === 'atomic') { - return snip(stitch`atomicSub(&${a}, ${value})`, a.dataType.inner); + return snip( + stitch`atomicSub(&${a}, ${value})`, + a.dataType.inner, + /* ref */ 'runtime', + ); } throw new Error( `Invalid atomic type: ${safeStringify(a.dataType)}`, @@ -131,7 +147,11 @@ export const atomicMax = createDualImpl( // CODEGEN implementation (a, value) => { if (isWgslData(a.dataType) && a.dataType.type === 'atomic') { - return snip(stitch`atomicMax(&${a}, ${value})`, a.dataType.inner); + return snip( + stitch`atomicMax(&${a}, ${value})`, + a.dataType.inner, + /* ref */ 'runtime', + ); } throw new Error( `Invalid atomic type: ${safeStringify(a.dataType)}`, @@ -151,7 +171,11 @@ export const atomicMin = createDualImpl( // CODEGEN implementation (a, value) => { if (isWgslData(a.dataType) && a.dataType.type === 'atomic') { - return snip(stitch`atomicMin(&${a}, ${value})`, a.dataType.inner); + return snip( + stitch`atomicMin(&${a}, ${value})`, + a.dataType.inner, + /* ref */ 'runtime', + ); } throw new Error( `Invalid atomic type: ${safeStringify(a.dataType)}`, @@ -171,7 +195,11 @@ export const atomicAnd = createDualImpl( // CODEGEN implementation (a, value) => { if (isWgslData(a.dataType) && a.dataType.type === 'atomic') { - return snip(stitch`atomicAnd(&${a}, ${value})`, a.dataType.inner); + return snip( + stitch`atomicAnd(&${a}, ${value})`, + a.dataType.inner, + /* ref */ 'runtime', + ); } throw new Error( `Invalid atomic type: ${safeStringify(a.dataType)}`, @@ -191,7 +219,11 @@ export const atomicOr = createDualImpl( // CODEGEN implementation (a, value) => { if (isWgslData(a.dataType) && a.dataType.type === 'atomic') { - return snip(stitch`atomicOr(&${a}, ${value})`, a.dataType.inner); + return snip( + stitch`atomicOr(&${a}, ${value})`, + a.dataType.inner, + /* ref */ 'runtime', + ); } throw new Error( `Invalid atomic type: ${safeStringify(a.dataType)}`, @@ -211,7 +243,11 @@ export const atomicXor = createDualImpl( // CODEGEN implementation (a, value) => { if (isWgslData(a.dataType) && a.dataType.type === 'atomic') { - return snip(stitch`atomicXor(&${a}, ${value})`, a.dataType.inner); + return snip( + stitch`atomicXor(&${a}, ${value})`, + a.dataType.inner, + /* ref */ 'runtime', + ); } throw new Error( `Invalid atomic type: ${safeStringify(a.dataType)}`, diff --git a/packages/typegpu/src/std/boolean.ts b/packages/typegpu/src/std/boolean.ts index 6fdaffc13b..a55469036a 100644 --- a/packages/typegpu/src/std/boolean.ts +++ b/packages/typegpu/src/std/boolean.ts @@ -284,7 +284,11 @@ export const isCloseTo = dualImpl({ return false; }, // GPU implementation - codegenImpl: (lhs, rhs, precision = snip(0.01, f32)) => { + codegenImpl: ( + lhs, + rhs, + precision = snip(0.01, f32, /* ref */ 'constant'), + ) => { if (isSnippetNumeric(lhs) && isSnippetNumeric(rhs)) { return stitch`(abs(f32(${lhs}) - f32(${rhs})) <= ${precision})`; } diff --git a/packages/typegpu/src/std/derivative.ts b/packages/typegpu/src/std/derivative.ts index f29408b287..97d1bfaa6b 100644 --- a/packages/typegpu/src/std/derivative.ts +++ b/packages/typegpu/src/std/derivative.ts @@ -11,7 +11,7 @@ function cpuDpdx(value: T): T { export const dpdx = createDualImpl( cpuDpdx, - (value) => snip(stitch`dpdx(${value})`, value.dataType), + (value) => snip(stitch`dpdx(${value})`, value.dataType, /* ref */ 'runtime'), 'dpdx', ); @@ -25,7 +25,8 @@ function cpuDpdxCoarse( export const dpdxCoarse = createDualImpl( cpuDpdxCoarse, - (value) => snip(stitch`dpdxCoarse(${value})`, value.dataType), + (value) => + snip(stitch`dpdxCoarse(${value})`, value.dataType, /* ref */ 'runtime'), 'dpdxCoarse', ); @@ -37,7 +38,8 @@ function cpuDpdxFine(value: T): T { export const dpdxFine = createDualImpl( cpuDpdxFine, - (value) => snip(stitch`dpdxFine(${value})`, value.dataType), + (value) => + snip(stitch`dpdxFine(${value})`, value.dataType, /* ref */ 'runtime'), 'dpdxFine', ); @@ -49,7 +51,7 @@ function cpuDpdy(value: T): T { export const dpdy = createDualImpl( cpuDpdy, - (value) => snip(stitch`dpdy(${value})`, value.dataType), + (value) => snip(stitch`dpdy(${value})`, value.dataType, /* ref */ 'runtime'), 'dpdy', ); @@ -63,7 +65,8 @@ function cpuDpdyCoarse( export const dpdyCoarse = createDualImpl( cpuDpdyCoarse, - (value) => snip(stitch`dpdyCoarse(${value})`, value.dataType), + (value) => + snip(stitch`dpdyCoarse(${value})`, value.dataType, /* ref */ 'runtime'), 'dpdyCoarse', ); @@ -75,7 +78,8 @@ function cpuDpdyFine(value: T): T { export const dpdyFine = createDualImpl( cpuDpdyFine, - (value) => snip(stitch`dpdyFine(${value})`, value.dataType), + (value) => + snip(stitch`dpdyFine(${value})`, value.dataType, /* ref */ 'runtime'), 'dpdyFine', ); @@ -87,7 +91,8 @@ function cpuFwidth(value: T): T { export const fwidth = createDualImpl( cpuFwidth, - (value) => snip(stitch`fwidth(${value})`, value.dataType), + (value) => + snip(stitch`fwidth(${value})`, value.dataType, /* ref */ 'runtime'), 'fwidth', ); @@ -101,7 +106,8 @@ function cpuFwidthCoarse( export const fwidthCoarse = createDualImpl( cpuFwidthCoarse, - (value) => snip(stitch`fwidthCoarse(${value})`, value.dataType), + (value) => + snip(stitch`fwidthCoarse(${value})`, value.dataType, /* ref */ 'runtime'), 'fwidthCoarse', ); @@ -115,6 +121,7 @@ function cpuFwidthFine( export const fwidthFine = createDualImpl( cpuFwidthFine, - (value) => snip(stitch`fwidthFine(${value})`, value.dataType), + (value) => + snip(stitch`fwidthFine(${value})`, value.dataType, /* ref */ 'runtime'), 'fwidthFine', ); diff --git a/packages/typegpu/src/std/discard.ts b/packages/typegpu/src/std/discard.ts index e959775868..2d5e25bccd 100644 --- a/packages/typegpu/src/std/discard.ts +++ b/packages/typegpu/src/std/discard.ts @@ -10,6 +10,6 @@ export const discard = createDualImpl( ); }, // GPU - () => snip('discard;', Void), + () => snip('discard;', Void, /* ref */ 'runtime'), 'discard', ); diff --git a/packages/typegpu/src/std/extensions.ts b/packages/typegpu/src/std/extensions.ts index 78d1829e71..d42c616fc2 100644 --- a/packages/typegpu/src/std/extensions.ts +++ b/packages/typegpu/src/std/extensions.ts @@ -28,7 +28,7 @@ export const extensionEnabled: DualFn< `extensionEnabled has to be called with a string literal representing a valid WGSL extension name. Got: ${value}`, ); } - return snip(jsImpl(value as WgslExtension), bool); + return snip(jsImpl(value as WgslExtension), bool, /* ref */ 'constant'); }; const impl = (extensionName: WgslExtension) => { diff --git a/packages/typegpu/src/std/matrix.ts b/packages/typegpu/src/std/matrix.ts index 86e0e169a2..ef643521bd 100644 --- a/packages/typegpu/src/std/matrix.ts +++ b/packages/typegpu/src/std/matrix.ts @@ -40,7 +40,11 @@ export const translate4 = createDualImpl( (matrix: m4x4f, vector: v3f) => cpuMul(cpuTranslation4(vector), matrix), // GPU implementation (matrix, vector) => - snip(stitch`(${gpuTranslation4(vector)} * ${matrix})`, matrix.dataType), + snip( + stitch`(${gpuTranslation4(vector)} * ${matrix})`, + matrix.dataType, + /* ref */ 'runtime', + ), 'translate4', ); @@ -55,7 +59,11 @@ export const scale4 = createDualImpl( (matrix: m4x4f, vector: v3f) => cpuMul(cpuScaling4(vector), matrix), // GPU implementation (matrix, vector) => - snip(stitch`(${(gpuScaling4(vector))} * ${matrix})`, matrix.dataType), + snip( + stitch`(${(gpuScaling4(vector))} * ${matrix})`, + matrix.dataType, + /* ref */ 'runtime', + ), 'scale4', ); @@ -70,7 +78,11 @@ export const rotateX4 = createDualImpl( (matrix: m4x4f, angle: number) => cpuMul(cpuRotationX4(angle), matrix), // GPU implementation (matrix, angle) => - snip(stitch`(${(gpuRotationX4(angle))} * ${matrix})`, matrix.dataType), + snip( + stitch`(${(gpuRotationX4(angle))} * ${matrix})`, + matrix.dataType, + /* ref */ 'runtime', + ), 'rotateX4', ); @@ -85,7 +97,11 @@ export const rotateY4 = createDualImpl( (matrix: m4x4f, angle: number) => cpuMul(cpuRotationY4(angle), matrix), // GPU implementation (matrix, angle) => - snip(stitch`(${(gpuRotationY4(angle))} * ${matrix})`, matrix.dataType), + snip( + stitch`(${(gpuRotationY4(angle))} * ${matrix})`, + matrix.dataType, + /* ref */ 'runtime', + ), 'rotateY4', ); @@ -100,6 +116,10 @@ export const rotateZ4 = createDualImpl( (matrix: m4x4f, angle: number) => cpuMul(cpuRotationZ4(angle), matrix), // GPU implementation (matrix, angle) => - snip(stitch`(${(gpuRotationZ4(angle))} * ${matrix})`, matrix.dataType), + snip( + stitch`(${(gpuRotationZ4(angle))} * ${matrix})`, + matrix.dataType, + /* ref */ 'runtime', + ), 'rotateZ4', ); diff --git a/packages/typegpu/src/std/numeric.ts b/packages/typegpu/src/std/numeric.ts index 4af9353db4..50d1bb5fd8 100644 --- a/packages/typegpu/src/std/numeric.ts +++ b/packages/typegpu/src/std/numeric.ts @@ -4,6 +4,7 @@ import { MissingCpuImplError, } from '../core/function/dualImpl.ts'; import { stitch } from '../core/resolve/stitch.ts'; +import type { AnyData } from '../data/dataTypes.ts'; import { smoothstepScalar } from '../data/numberOps.ts'; import { abstractFloat, @@ -64,9 +65,16 @@ function cpuAbs(value: T): T { return VectorOps.abs[value.kind](value) as T; } +const unaryIdentitySignature = (arg: AnyData) => { + return { + argTypes: [arg], + returnType: arg, + }; +}; + export const abs = dualImpl({ name: 'abs', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuAbs, codegenImpl: (value) => stitch`abs(${value})`, }); @@ -82,7 +90,7 @@ function cpuAcos(value: T): T { export const acos = dualImpl({ name: 'acos', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuAcos, codegenImpl: (value) => stitch`acos(${value})`, }); @@ -98,7 +106,7 @@ function cpuAcosh(value: T): T { export const acosh = dualImpl({ name: 'acosh', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuAcosh, codegenImpl: (value) => stitch`acosh(${value})`, }); @@ -114,7 +122,7 @@ function cpuAsin(value: T): T { export const asin = dualImpl({ name: 'asin', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuAsin, codegenImpl: (value) => stitch`asin(${value})`, }); @@ -130,7 +138,7 @@ function cpuAsinh(value: T): T { export const asinh = dualImpl({ name: 'asinh', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuAsinh, codegenImpl: (value) => stitch`asinh(${value})`, }); @@ -146,7 +154,7 @@ function cpuAtan(value: T): T { export const atan = dualImpl({ name: 'atan', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuAtan, codegenImpl: (value) => stitch`atan(${value})`, }); @@ -162,7 +170,7 @@ function cpuAtanh(value: T): T { export const atanh = dualImpl({ name: 'atanh', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuAtanh, codegenImpl: (value) => stitch`atanh(${value})`, }); @@ -203,7 +211,7 @@ function cpuCeil(value: T): T { export const ceil = dualImpl({ name: 'ceil', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuCeil, codegenImpl: (value) => stitch`ceil(${value})`, }); @@ -242,7 +250,7 @@ function cpuCos(value: T): T { export const cos = dualImpl({ name: 'cos', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuCos, codegenImpl: (value) => stitch`cos(${value})`, }); @@ -258,7 +266,7 @@ function cpuCosh(value: T): T { export const cosh = dualImpl({ name: 'cosh', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuCosh, codegenImpl: (value) => stitch`cosh(${value})`, }); @@ -273,7 +281,7 @@ function cpuCountLeadingZeros( export const countLeadingZeros = dualImpl({ name: 'countLeadingZeros', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for countLeadingZeros not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (value) => stitch`countLeadingZeros(${value})`, @@ -289,7 +297,7 @@ function cpuCountOneBits( export const countOneBits = dualImpl({ name: 'countOneBits', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for countOneBits not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (value) => stitch`countOneBits(${value})`, @@ -305,7 +313,7 @@ function cpuCountTrailingZeros( export const countTrailingZeros = dualImpl({ name: 'countTrailingZeros', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for countTrailingZeros not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (value) => stitch`countTrailingZeros(${value})`, @@ -313,7 +321,9 @@ export const countTrailingZeros = dualImpl({ export const cross = dualImpl({ name: 'cross', - signature: (lhs, rhs) => ({ argTypes: [lhs, rhs], returnType: lhs }), + signature: (...args) => { + return ({ argTypes: args, returnType: args[0] }); + }, normalImpl: (a: T, b: T): T => VectorOps.cross[a.kind](a, b), codegenImpl: (a, b) => stitch`cross(${a}, ${b})`, @@ -332,7 +342,7 @@ function cpuDegrees(value: T): T { export const degrees = dualImpl({ name: 'degrees', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuDegrees, codegenImpl: (value) => stitch`degrees(${value})`, }); @@ -340,7 +350,7 @@ export const degrees = dualImpl({ export const determinant = dualImpl<(value: AnyMatInstance) => number>({ name: 'determinant', // TODO: The return type is potentially wrong here, it should return whatever the matrix element type is. - signature: (arg) => ({ argTypes: [arg], returnType: f32 }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for determinant not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (value) => stitch`determinant(${value})`, @@ -362,19 +372,21 @@ function cpuDistance( export const distance = dualImpl({ name: 'distance', - signature: (lhs, rhs) => ({ - argTypes: [lhs, rhs], - returnType: isHalfPrecisionSchema(lhs) ? f16 : f32, - }), + signature: (...args) => { + return ({ + argTypes: args, + returnType: isHalfPrecisionSchema(args[0]) ? f16 : f32, + }); + }, normalImpl: cpuDistance, codegenImpl: (a, b) => stitch`distance(${a}, ${b})`, }); export const dot = dualImpl({ name: 'dot', - signature: (e1, e2) => ({ - argTypes: [e1, e2], - returnType: (e1 as VecData).primitive, + signature: (...args) => ({ + argTypes: args, + returnType: (args[0] as VecData).primitive, }), normalImpl: (lhs: T, rhs: T): number => VectorOps.dot[lhs.kind](lhs, rhs), @@ -383,7 +395,7 @@ export const dot = dualImpl({ export const dot4U8Packed = dualImpl<(e1: number, e2: number) => number>({ name: 'dot4U8Packed', - signature: (lhs, rhs) => ({ argTypes: [u32, u32], returnType: u32 }), + signature: { argTypes: [u32, u32], returnType: u32 }, normalImpl: 'CPU implementation for dot4U8Packed not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (e1, e2) => stitch`dot4U8Packed(${e1}, ${e2})`, @@ -391,7 +403,7 @@ export const dot4U8Packed = dualImpl<(e1: number, e2: number) => number>({ export const dot4I8Packed = dualImpl<(e1: number, e2: number) => number>({ name: 'dot4I8Packed', - signature: (lhs, rhs) => ({ argTypes: [u32, u32], returnType: i32 }), + signature: { argTypes: [u32, u32], returnType: i32 }, normalImpl: 'CPU implementation for dot4I8Packed not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (e1, e2) => stitch`dot4I8Packed(${e1}, ${e2})`, @@ -408,7 +420,7 @@ function cpuExp(value: T): T { export const exp = dualImpl({ name: 'exp', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuExp, codegenImpl: (value) => stitch`exp(${value})`, }); @@ -424,7 +436,7 @@ function cpuExp2(value: T): T { export const exp2 = dualImpl({ name: 'exp2', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuExp2, codegenImpl: (value) => stitch`exp2(${value})`, }); @@ -445,7 +457,7 @@ function cpuExtractBits( export const extractBits = dualImpl({ name: 'extractBits', - signature: (arg, offset, count) => ({ + signature: (arg, _offset, _count) => ({ argTypes: [arg, u32, u32], returnType: arg, }), @@ -459,10 +471,12 @@ export const faceForward = dualImpl< (e1: T, e2: T, e3: T) => T >({ name: 'faceForward', - signature: (arg1, arg2, arg3) => ({ - argTypes: [arg1, arg2, arg3], - returnType: arg1, - }), + signature: (...args) => { + return ({ + argTypes: args, + returnType: args[0], + }); + }, normalImpl: 'CPU implementation for faceForward not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (e1, e2, e3) => stitch`faceForward(${e1}, ${e2}, ${e3})`, @@ -478,7 +492,7 @@ function cpuFirstLeadingBit( export const firstLeadingBit = dualImpl({ name: 'firstLeadingBit', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for firstLeadingBit not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (value) => stitch`firstLeadingBit(${value})`, @@ -494,7 +508,7 @@ function cpuFirstTrailingBit( export const firstTrailingBit = dualImpl({ name: 'firstTrailingBit', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for firstTrailingBit not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (value) => stitch`firstTrailingBit(${value})`, @@ -511,7 +525,7 @@ function cpuFloor(value: T): T { export const floor = dualImpl({ name: 'floor', - signature: (...argTypes) => ({ argTypes, returnType: argTypes[0] }), + signature: unaryIdentitySignature, normalImpl: cpuFloor, codegenImpl: (arg) => stitch`floor(${arg})`, }); @@ -533,9 +547,9 @@ function cpuFma( export const fma = dualImpl({ name: 'fma', - signature: (arg1, arg2, arg3) => ({ - argTypes: [arg1, arg2, arg3], - returnType: arg1, + signature: (...args) => ({ + argTypes: args, + returnType: args[0], }), normalImpl: cpuFma, codegenImpl: (e1, e2, e3) => stitch`fma(${e1}, ${e2}, ${e3})`, @@ -552,7 +566,7 @@ function cpuFract(value: T): T { export const fract = dualImpl({ name: 'fract', - signature: (...argTypes) => ({ argTypes, returnType: argTypes[0] }), + signature: unaryIdentitySignature, normalImpl: cpuFract, codegenImpl: (a) => stitch`fract(${a})`, }); @@ -597,7 +611,7 @@ export const frexp: FrexpOverload = createDualImpl( ); } - return snip(stitch`frexp(${value})`, returnType); + return snip(stitch`frexp(${value})`, returnType, /* ref */ 'runtime'); }, 'frexp', ); @@ -625,7 +639,7 @@ function cpuInsertBits( export const insertBits = dualImpl({ name: 'insertBits', - signature: (e, newbits, offset, count) => ({ + signature: (e, newbits, _offset, _count) => ({ argTypes: [e, newbits, u32, u32], returnType: e, }), @@ -648,7 +662,7 @@ function cpuInverseSqrt(value: T): T { export const inverseSqrt = dualImpl({ name: 'inverseSqrt', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuInverseSqrt, codegenImpl: (value) => stitch`inverseSqrt(${value})`, }); @@ -666,10 +680,10 @@ function cpuLdexp( export const ldexp = dualImpl({ name: 'ldexp', - signature: (e1, e2) => { + signature: (e1, _e2) => { switch (e1.type) { case 'abstractFloat': - return { argTypes: [abstractFloat, abstractInt], returnType: e1 }; + return { argTypes: [e1, abstractInt], returnType: e1 }; case 'f32': case 'f16': return { argTypes: [e1, i32], returnType: e1 }; @@ -723,7 +737,7 @@ function cpuLog(value: T): T { export const log = dualImpl({ name: 'log', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuLog, codegenImpl: (value) => stitch`log(${value})`, }); @@ -739,7 +753,7 @@ function cpuLog2(value: T): T { export const log2 = dualImpl({ name: 'log2', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuLog2, codegenImpl: (value) => stitch`log2(${value})`, }); @@ -814,7 +828,13 @@ function cpuMix( export const mix = dualImpl({ name: 'mix', - signature: (e1, e2, e3) => ({ argTypes: [e1, e2, e3], returnType: e1 }), + signature: (...args) => { + const uargs = unify(args) ?? args; + return ({ + argTypes: uargs, + returnType: uargs[0], + }); + }, normalImpl: cpuMix, codegenImpl: (e1, e2, e3) => stitch`mix(${e1}, ${e2}, ${e3})`, }); @@ -867,7 +887,7 @@ export const modf: ModfOverload = dualImpl({ export const normalize = dualImpl({ name: 'normalize', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: (v: T): T => VectorOps.normalize[v.kind](v), codegenImpl: (v) => stitch`normalize(${v})`, @@ -913,7 +933,7 @@ function cpuQuantizeToF16( export const quantizeToF16 = dualImpl({ name: 'quantizeToF16', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for quantizeToF16 not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (value) => stitch`quantizeToF16(${value})`, @@ -942,7 +962,7 @@ export const radians = dualImpl({ export const reflect = dualImpl({ name: 'reflect', - signature: (lhs, rhs) => ({ argTypes: [lhs, rhs], returnType: lhs }), + signature: (...args) => ({ argTypes: args, returnType: args[0] }), normalImpl: (e1: T, e2: T): T => sub(e1, mul(2 * dot(e2, e1), e2)), codegenImpl: (e1, e2) => stitch`reflect(${e1}, ${e2})`, @@ -956,7 +976,12 @@ export const refract = createDualImpl( ); }, // GPU implementation - (e1, e2, e3) => snip(stitch`refract(${e1}, ${e2}, ${e3})`, e1.dataType), + (e1, e2, e3) => + snip( + stitch`refract(${e1}, ${e2}, ${e3})`, + e1.dataType, + /* ref */ 'runtime', + ), 'refract', (e1, e2, e3) => [ e1.dataType as AnyWgslData, @@ -972,7 +997,7 @@ function cpuReverseBits(value: T): T { export const reverseBits = dualImpl({ name: 'reverseBits', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for reverseBits not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (value) => stitch`reverseBits(${value})`, @@ -991,7 +1016,7 @@ function cpuRound(value: T): T { export const round = dualImpl({ name: 'round', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuRound, codegenImpl: (value) => stitch`round(${value})`, }); @@ -1009,7 +1034,7 @@ function cpuSaturate(value: T): T { export const saturate = dualImpl({ name: 'saturate', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuSaturate, codegenImpl: (value) => stitch`saturate(${value})`, }); @@ -1025,7 +1050,7 @@ function cpuSign(e: T): T { export const sign = dualImpl({ name: 'sign', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuSign, codegenImpl: (e) => stitch`sign(${e})`, }); @@ -1041,7 +1066,7 @@ function cpuSin(value: T): T { export const sin = dualImpl({ name: 'sin', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuSin, codegenImpl: (value) => stitch`sin(${value})`, }); @@ -1059,7 +1084,7 @@ function cpuSinh(value: T): T { export const sinh = dualImpl({ name: 'sinh', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuSinh, codegenImpl: (value) => stitch`sinh(${value})`, }); @@ -1091,9 +1116,9 @@ function cpuSmoothstep( export const smoothstep = dualImpl({ name: 'smoothstep', - signature: (edge0, edge1, x) => ({ - argTypes: [edge0, edge1, x], - returnType: x, + signature: (...args) => ({ + argTypes: args, + returnType: args[2], }), normalImpl: cpuSmoothstep, codegenImpl: (edge0, edge1, x) => @@ -1111,7 +1136,7 @@ function cpuSqrt(value: T): T { export const sqrt = dualImpl({ name: 'sqrt', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuSqrt, codegenImpl: (value) => stitch`sqrt(${value})`, }); @@ -1150,7 +1175,7 @@ function cpuTan(value: T): T { export const tan = dualImpl({ name: 'tan', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuTan, codegenImpl: (value) => stitch`tan(${value})`, }); @@ -1166,14 +1191,14 @@ function cpuTanh(value: T): T { export const tanh = dualImpl({ name: 'tanh', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: cpuTanh, codegenImpl: (value) => stitch`tanh(${value})`, }); export const transpose = dualImpl<(e: T) => T>({ name: 'transpose', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for transpose not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (e) => stitch`transpose(${e})`, @@ -1187,7 +1212,7 @@ function cpuTrunc(value: T): T { export const trunc = dualImpl({ name: 'trunc', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: unaryIdentitySignature, normalImpl: 'CPU implementation for trunc not implemented yet. Please submit an issue at https://github.com/software-mansion/TypeGPU/issues', codegenImpl: (value) => stitch`trunc(${value})`, diff --git a/packages/typegpu/src/std/operators.ts b/packages/typegpu/src/std/operators.ts index 160ab8d646..521b69d17c 100644 --- a/packages/typegpu/src/std/operators.ts +++ b/packages/typegpu/src/std/operators.ts @@ -253,7 +253,10 @@ function cpuNeg(value: NumVec | number): NumVec | number { export const neg = dualImpl({ name: 'neg', - signature: (arg) => ({ argTypes: [arg], returnType: arg }), + signature: (arg) => ({ + argTypes: [arg], + returnType: arg, + }), normalImpl: cpuNeg, codegenImpl: (arg) => stitch`-(${arg})`, }); diff --git a/packages/typegpu/src/std/packing.ts b/packages/typegpu/src/std/packing.ts index af12a7452a..6f7a88e9d8 100644 --- a/packages/typegpu/src/std/packing.ts +++ b/packages/typegpu/src/std/packing.ts @@ -20,7 +20,7 @@ export const unpack2x16float = createDualImpl( return vec2f(reader.readFloat16(), reader.readFloat16()); }, // GPU implementation - (e) => snip(stitch`unpack2x16float(${e})`, vec2f), + (e) => snip(stitch`unpack2x16float(${e})`, vec2f, /* ref */ 'runtime'), 'unpack2x16float', ); @@ -39,7 +39,7 @@ export const pack2x16float = createDualImpl( return u32(reader.readUint32()); }, // GPU implementation - (e) => snip(stitch`pack2x16float(${e})`, u32), + (e) => snip(stitch`pack2x16float(${e})`, u32, /* ref */ 'runtime'), 'pack2x16float', ); @@ -62,7 +62,7 @@ export const unpack4x8unorm = createDualImpl( ); }, // GPU implementation - (e) => snip(stitch`unpack4x8unorm(${e})`, vec4f), + (e) => snip(stitch`unpack4x8unorm(${e})`, vec4f, /* ref */ 'runtime'), 'unpack4x8unorm', ); @@ -83,6 +83,6 @@ export const pack4x8unorm = createDualImpl( return u32(reader.readUint32()); }, // GPU implementation - (e) => snip(stitch`pack4x8unorm(${e})`, u32), + (e) => snip(stitch`pack4x8unorm(${e})`, u32, /* ref */ 'runtime'), 'pack4x8unorm', ); diff --git a/packages/typegpu/src/tgsl/consoleLog/logGenerator.ts b/packages/typegpu/src/tgsl/consoleLog/logGenerator.ts index bec51d66de..972f0e7982 100644 --- a/packages/typegpu/src/tgsl/consoleLog/logGenerator.ts +++ b/packages/typegpu/src/tgsl/consoleLog/logGenerator.ts @@ -36,16 +36,14 @@ const defaultOptions: Required = { messagePrefix: ' GPU ', }; -const fallbackSnippet = snip('/* console.log() */', Void); +const fallbackSnippet = snip('/* console.log() */', Void, /* ref */ 'runtime'); export class LogGeneratorNullImpl implements LogGenerator { get logResources(): undefined { return undefined; } generateLog(): Snippet { - console.warn( - "'console.log' is currently only supported in compute pipelines.", - ); + console.warn("'console.log' is only supported when resolving pipelines."); return fallbackSnippet; } } @@ -114,7 +112,11 @@ export class LogGeneratorImpl implements LogGenerator { this.#logIdToMeta.set(id, { op: op as SupportedLogOps, argTypes }); - return snip(stitch`${ctx.resolve(logFn).value}(${nonStringArgs})`, Void); + return snip( + stitch`${ctx.resolve(logFn).value}(${nonStringArgs})`, + Void, + /* ref */ 'runtime', + ); } get logResources(): LogResources | undefined { diff --git a/packages/typegpu/src/tgsl/conversion.ts b/packages/typegpu/src/tgsl/conversion.ts index a4502c9f3a..8a97451e0f 100644 --- a/packages/typegpu/src/tgsl/conversion.ts +++ b/packages/typegpu/src/tgsl/conversion.ts @@ -1,6 +1,7 @@ import { stitch } from '../core/resolve/stitch.ts'; import type { AnyData, UnknownData } from '../data/dataTypes.ts'; import { undecorate } from '../data/dataTypes.ts'; +import { derefSnippet, RefOperator } from '../data/ref.ts'; import { snip, type Snippet } from '../data/snippet.ts'; import { type AnyWgslData, @@ -9,6 +10,7 @@ import { type I32, isMat, isVec, + type Ptr, type U32, type WgslStruct, } from '../data/wgslTypes.ts'; @@ -72,6 +74,8 @@ function getImplicitConversionRank( if ( trueSrc.type === 'ptr' && + // Only dereferencing implicit pointers, otherwise we'd have a types mismatch between TS and WGSL + trueSrc.implicit && getAutoConversionRank(trueSrc.inner as AnyData, trueDst).rank < Number.POSITIVE_INFINITY ) { @@ -182,16 +186,15 @@ function findBestType( if (!bestResult) { return undefined; } - const actions: ConversionResultAction[] = bestResult.details.map(( - detail, - index, - ) => ({ - sourceIndex: index, - action: detail.action, - ...(detail.action === 'cast' && { - targetType: detail.targetType as U32 | F32 | I32 | F16, + const actions: ConversionResultAction[] = bestResult.details.map( + (detail, index) => ({ + sourceIndex: index, + action: detail.action, + ...(detail.action === 'cast' && { + targetType: detail.targetType as U32 | F32 | I32 | F16, + }), }), - })); + ); return { targetType: bestResult.type, @@ -229,14 +232,23 @@ function applyActionToSnippet( targetType: AnyData, ): Snippet { if (action.action === 'none') { - return snip(snippet.value, targetType); + return snip( + snippet.value, + targetType, + // if it was a ref, then it's still a ref + /* origin */ snippet.origin, + ); } switch (action.action) { case 'ref': - return snip(stitch`&${snippet}`, targetType); + return snip( + new RefOperator(snippet, targetType as Ptr), + targetType, + snippet.origin, + ); case 'deref': - return snip(stitch`*${snippet}`, targetType); + return derefSnippet(snippet); case 'cast': { // Casting means calling the schema with the snippet as an argument. return (targetType as unknown as (val: Snippet) => Snippet)(snippet); @@ -313,19 +325,25 @@ export function tryConvertSnippet( verbose = true, ): Snippet { if (targetDataType === snippet.dataType) { - return snip(snippet.value, targetDataType); + return snip(snippet.value, targetDataType, snippet.origin); } if (snippet.dataType.type === 'unknown') { // This is it, it's now or never. We expect a specific type, and we're going to get it - return snip(stitch`${snip(snippet.value, targetDataType)}`, targetDataType); + return snip( + stitch`${snip(snippet.value, targetDataType, snippet.origin)}`, + targetDataType, + snippet.origin, + ); } const converted = convertToCommonType([snippet], [targetDataType], verbose); if (!converted) { throw new WgslTypeError( - `Cannot convert value of type '${snippet.dataType.type}' to type '${targetDataType.type}'`, + `Cannot convert value of type '${ + String(snippet.dataType) + }' to type '${targetDataType.type}'`, ); } diff --git a/packages/typegpu/src/tgsl/generationHelpers.ts b/packages/typegpu/src/tgsl/generationHelpers.ts index c932b23273..7726438648 100644 --- a/packages/typegpu/src/tgsl/generationHelpers.ts +++ b/packages/typegpu/src/tgsl/generationHelpers.ts @@ -1,7 +1,10 @@ import { type AnyData, + InfixDispatch, isDisarray, isUnstruct, + MatrixColumnsAccess, + undecorate, UnknownData, } from '../data/dataTypes.ts'; import { mat2x2f, mat3x3f, mat4x4f } from '../data/matrix.ts'; @@ -14,7 +17,12 @@ import { i32, u32, } from '../data/numeric.ts'; -import { isSnippet, snip, type Snippet } from '../data/snippet.ts'; +import { + isEphemeralSnippet, + isSnippet, + snip, + type Snippet, +} from '../data/snippet.ts'; import { vec2b, vec2f, @@ -36,15 +44,25 @@ import { type AnyWgslData, type F32, type I32, + isMat, isMatInstance, - isNumericSchema, + isNaturallyEphemeral, + isPtr, isVec, isVecInstance, isWgslArray, isWgslStruct, } from '../data/wgslTypes.ts'; -import { getOwnSnippet, type ResolutionCtx } from '../types.ts'; +import { + getOwnSnippet, + isKnownAtComptime, + type ResolutionCtx, +} from '../types.ts'; import type { ShelllessRepository } from './shellless.ts'; +import { add, div, mul, sub } from '../std/operators.ts'; +import { $internal } from '../shared/symbols.ts'; +import { stitch } from '../core/resolve/stitch.ts'; +import { derefSnippet, isRef } from '../data/ref.ts'; type SwizzleableType = 'f' | 'h' | 'i' | 'u' | 'b'; type SwizzleLength = 1 | 2 | 3 | 4; @@ -106,36 +124,155 @@ const kindToSchema = { mat4x4f: mat4x4f, } as const; -export function getTypeForPropAccess( - targetType: AnyData, +const infixKinds = [ + 'vec2f', + 'vec3f', + 'vec4f', + 'vec2h', + 'vec3h', + 'vec4h', + 'vec2i', + 'vec3i', + 'vec4i', + 'vec2u', + 'vec3u', + 'vec4u', + 'mat2x2f', + 'mat3x3f', + 'mat4x4f', +]; + +export const infixOperators = { + add, + sub, + mul, + div, +} as const; + +export type InfixOperator = keyof typeof infixOperators; + +export function accessProp( + target: Snippet, propName: string, -): AnyData | UnknownData { - if (isWgslStruct(targetType) || isUnstruct(targetType)) { - return targetType.propTypes[propName] as AnyData ?? UnknownData; +): Snippet | undefined { + if ( + infixKinds.includes(target.dataType.type) && + propName in infixOperators + ) { + return snip( + new InfixDispatch( + propName, + target, + infixOperators[propName as InfixOperator][$internal].gpuImpl, + ), + UnknownData, + /* origin */ target.origin, + ); + } + + if (isWgslArray(target.dataType) && propName === 'length') { + if (target.dataType.elementCount === 0) { + // Dynamically-sized array + return snip( + stitch`arrayLength(&${target})`, + u32, + /* ref */ 'runtime', + ); + } + + return snip( + target.dataType.elementCount, + abstractInt, + /* ref */ 'constant', + ); + } + + if (isMat(target.dataType) && propName === 'columns') { + return snip( + new MatrixColumnsAccess(target), + UnknownData, + /* origin */ target.origin, + ); + } + + if (isWgslStruct(target.dataType) || isUnstruct(target.dataType)) { + let propType = target.dataType.propTypes[propName]; + if (!propType) { + return undefined; + } + propType = undecorate(propType); + + return snip( + stitch`${target}.${propName}`, + propType, + /* origin */ target.origin === 'argument' + ? 'argument' + : !isEphemeralSnippet(target) && + !isNaturallyEphemeral(propType) + ? target.origin + : target.origin === 'constant' || target.origin === 'constant-ref' + ? 'constant' + : 'runtime', + ); + } + + if (isPtr(target.dataType)) { + const derefed = derefSnippet(target); + + if (propName === '$') { + // Dereference pointer + return derefed; + } + + // Sometimes values that are typed as pointers aren't instances of `d.ref`, so we + // allow access to member props as if it wasn't a pointer. + return accessProp(derefed, propName); } - if (targetType === bool || isNumericSchema(targetType)) { - // No props to be accessed here - return UnknownData; + if (isVec(target.dataType)) { + // Example: d.vec3f().kind === 'vec3f' + if (propName === 'kind') { + return snip(target.dataType.type, UnknownData, 'constant'); + } } const propLength = propName.length; if ( - isVec(targetType) && + isVec(target.dataType) && propLength >= 1 && propLength <= 4 ) { - const swizzleTypeChar = targetType.type.includes('bool') + const swizzleTypeChar = target.dataType.type.includes('bool') ? 'b' - : (targetType.type[4] as SwizzleableType); + : (target.dataType.type[4] as SwizzleableType); const swizzleType = swizzleLenToType[swizzleTypeChar][propLength as SwizzleLength]; - if (swizzleType) { - return swizzleType; + if (!swizzleType) { + return undefined; } + + return snip( + isKnownAtComptime(target) + // biome-ignore lint/suspicious/noExplicitAny: it's fine, the prop is there + ? (target.value as any)[propName] + : stitch`${target}.${propName}`, + swizzleType, + // Swizzling creates new vectors (unless they're on the lhs of an assignment, but that's not yet supported in WGSL) + /* origin */ target.origin === 'argument' && propLength === 1 + ? 'argument' + : target.origin === 'constant' || + target.origin === 'constant-ref' + ? 'constant' + : 'runtime', + ); } - return UnknownData; + if (isKnownAtComptime(target) || target.dataType.type === 'unknown') { + // biome-ignore lint/suspicious/noExplicitAny: we either know exactly what it is, or have no idea at all + return coerceToSnippet((target.value as any)[propName]); + } + + return undefined; } const indexableTypeToResult = { @@ -144,32 +281,87 @@ const indexableTypeToResult = { mat4x4f: vec4f, } as const; -export function getTypeForIndexAccess( - dataType: AnyData, -): AnyData | UnknownData { +export function accessIndex( + target: Snippet, + index: Snippet, +): Snippet | undefined { // array - if (isWgslArray(dataType) || isDisarray(dataType)) { - return dataType.elementType as AnyData; + if (isWgslArray(target.dataType) || isDisarray(target.dataType)) { + const elementType = target.dataType.elementType as AnyData; + + return snip( + isKnownAtComptime(target) && isKnownAtComptime(index) + // biome-ignore lint/suspicious/noExplicitAny: it's fine, it's there + ? (target.value as any)[index.value as number] + : stitch`${target}[${index}]`, + elementType, + /* origin */ !isEphemeralSnippet(target) && + !isNaturallyEphemeral(elementType) + ? target.origin + : target.origin === 'constant' || target.origin === 'constant-ref' + ? 'constant' + : 'runtime', + ); } // vector - if (isVec(dataType)) { - return dataType.primitive; + if (isVec(target.dataType)) { + return snip( + isKnownAtComptime(target) && isKnownAtComptime(index) + // biome-ignore lint/suspicious/noExplicitAny: it's fine, it's there + ? (target.value as any)[index.value as any] + : stitch`${target}[${index}]`, + target.dataType.primitive, + /* origin */ target.origin === 'constant' || + target.origin === 'constant-ref' + ? 'constant' + : 'runtime', + ); } - // matrix - if (dataType.type in indexableTypeToResult) { - return indexableTypeToResult[ - dataType.type as keyof typeof indexableTypeToResult + if (isPtr(target.dataType)) { + // Sometimes values that are typed as pointers aren't instances of `d.ref`, so we + // allow indexing as if it wasn't a pointer. + return accessIndex(derefSnippet(target), index); + } + + // matrix.columns + if (target.value instanceof MatrixColumnsAccess) { + const propType = indexableTypeToResult[ + target.value.matrix.dataType.type as keyof typeof indexableTypeToResult ]; + + return snip( + stitch`${target.value.matrix}[${index}]`, + propType, + /* origin */ target.origin, + ); + } + + // matrix + if (target.dataType.type in indexableTypeToResult) { + throw new Error( + "The only way of accessing matrix elements in TGSL is through the 'columns' property.", + ); + } + + if ( + (isKnownAtComptime(target) && isKnownAtComptime(index)) || + target.dataType.type === 'unknown' + ) { + // No idea what the type is, so we act on the snippet's value and try to guess + return coerceToSnippet( + // biome-ignore lint/suspicious/noExplicitAny: we're inspecting the value, and it could be any value + (target.value as any)[index.value as number], + ); } - return UnknownData; + return undefined; } export function numericLiteralToSnippet(value: number): Snippet { if (value >= 2 ** 63 || value < -(2 ** 63)) { - return snip(value, abstractFloat); + return snip(value, abstractFloat, /* ref */ 'constant'); } // WGSL AbstractInt uses 64-bit precision, but JS numbers are only safe up to 2^53 - 1. // Warn when values exceed this range to prevent precision loss. @@ -179,9 +371,9 @@ export function numericLiteralToSnippet(value: number): Snippet { `The integer ${value} exceeds the safe integer range and may have lost precision.`, ); } - return snip(value, abstractInt); + return snip(value, abstractInt, /* ref */ 'constant'); } - return snip(value, abstractFloat); + return snip(value, abstractFloat, /* ref */ 'constant'); } export function concretize(type: T): T | F32 | I32 { @@ -198,18 +390,22 @@ export function concretize(type: T): T | F32 | I32 { export function concretizeSnippets(args: Snippet[]): Snippet[] { return args.map((snippet) => - snip(snippet.value, concretize(snippet.dataType as AnyWgslData)) + snip( + snippet.value, + concretize(snippet.dataType as AnyWgslData), + /* origin */ snippet.origin, + ) ); } export type GenerationCtx = ResolutionCtx & { readonly pre: string; /** - * Used by `generateTypedExpression` to signal downstream + * Used by `typedExpression` to signal downstream * expression resolution what type is expected of them. * * It is used exclusively for inferring the types of structs and arrays. - * It is modified exclusively by `generateTypedExpression` function. + * It is modified exclusively by `typedExpression` function. */ expectedType: AnyData | undefined; @@ -239,6 +435,10 @@ export function coerceToSnippet(value: unknown): Snippet { return value; } + if (isRef(value)) { + throw new Error('Cannot use refs (d.ref(...)) from the outer scope.'); + } + // Maybe the value can tell us what snippet it is const ownSnippet = getOwnSnippet(value); if (ownSnippet) { @@ -246,7 +446,7 @@ export function coerceToSnippet(value: unknown): Snippet { } if (isVecInstance(value) || isMatInstance(value)) { - return snip(value, kindToSchema[value.kind]); + return snip(value, kindToSchema[value.kind], /* origin */ 'constant'); } if ( @@ -255,7 +455,7 @@ export function coerceToSnippet(value: unknown): Snippet { typeof value === 'undefined' || value === null ) { // Nothing representable in WGSL as-is, so unknown - return snip(value, UnknownData); + return snip(value, UnknownData, /* origin */ 'constant'); } if (typeof value === 'number') { @@ -263,8 +463,8 @@ export function coerceToSnippet(value: unknown): Snippet { } if (typeof value === 'boolean') { - return snip(value, bool); + return snip(value, bool, /* origin */ 'constant'); } - return snip(value, UnknownData); + return snip(value, UnknownData, /* origin */ 'constant'); } diff --git a/packages/typegpu/src/tgsl/shellless.ts b/packages/typegpu/src/tgsl/shellless.ts index 04b7e3ccd9..b265bf7344 100644 --- a/packages/typegpu/src/tgsl/shellless.ts +++ b/packages/typegpu/src/tgsl/shellless.ts @@ -3,19 +3,37 @@ import { type ShelllessImpl, } from '../core/function/shelllessImpl.ts'; import type { AnyData } from '../data/dataTypes.ts'; +import { RefOperator } from '../data/ref.ts'; import type { Snippet } from '../data/snippet.ts'; +import { isPtr } from '../data/wgslTypes.ts'; +import { WgslTypeError } from '../errors.ts'; +import { getResolutionCtx } from '../execMode.ts'; import { getMetaData, getName } from '../shared/meta.ts'; import { concretize } from './generationHelpers.ts'; -interface ShelllessVariant { - argTypes: AnyData[]; - value: ShelllessImpl; -} - type AnyFn = (...args: never[]) => unknown; +function shallowEqualSchemas(a: AnyData, b: AnyData): boolean { + if (a.type !== b.type) return false; + if (a.type === 'ptr' && b.type === 'ptr') { + return a.access === b.access && + a.addressSpace === b.addressSpace && + a.implicit === b.implicit && + shallowEqualSchemas(a.inner, b.inner); + } + if (a.type === 'array' && b.type === 'array') { + return a.elementCount === b.elementCount && + shallowEqualSchemas(a.elementType as AnyData, b.elementType as AnyData); + } + if (a.type === 'struct' && b.type === 'struct') { + // Only structs with the same identity are considered equal + return a === b; + } + return true; +} + export class ShelllessRepository { - cache = new Map(); + cache = new Map(); get( fn: AnyFn, @@ -31,23 +49,67 @@ export class ShelllessRepository { ); } - const argTypes = (argSnippets ?? []).map((s) => - concretize(s.dataType as AnyData) - ); + const argTypes = (argSnippets ?? []).map((s, index) => { + if (s.value instanceof RefOperator) { + if (s.dataType.type === 'unknown') { + throw new WgslTypeError( + `d.ref() created with primitive types must be stored in a variable before use`, + ); + } + return s.dataType; + } + + if (s.dataType.type === 'unknown') { + throw new Error( + `Passed illegal value ${s.value} as the #${index} argument to ${meta.name}(...)`, + ); + } + + let type = concretize(s.dataType as AnyData); + + if (s.origin === 'constant-ref') { + // biome-ignore lint/style/noNonNullAssertion: it's there + const ctx = getResolutionCtx()!; + throw new Error( + `Cannot pass constant references as function arguments. Explicitly copy them by wrapping them in a schema: '${ + ctx.resolve(type).value + }(...)'`, + ); + } + + if (isPtr(type) && type.implicit) { + // If the pointer was made implicitly (e.g. by assigning a reference to a const variable),// then we dereference the pointer before passing it to the function. The main reason for this, + // is that in TypeScript, the type of the function accepts a value, not the value wrapped in + // d.ref<> (so it's not considered mutable from the perspective of the function) + + // Example: + // const foo = layout.$.boids; + // bar(foo) + // ^^^ + type = type.inner; + } + + return type; + }); let cache = this.cache.get(fn); if (cache) { const variant = cache.find((v) => - v.argTypes.every((t, i) => t === argTypes[i]) + v.argTypes.length === argTypes.length && + v.argTypes.every((t, i) => + shallowEqualSchemas(t, argTypes[i] as AnyData) + ) ); - if (variant) return variant.value; + if (variant) { + return variant; + } } else { cache = []; this.cache.set(fn, cache); } const shellless = createShelllessImpl(argTypes, fn); - cache.push({ argTypes, value: shellless }); + cache.push(shellless); return shellless; } } diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index c7956b9701..637d96df78 100644 --- a/packages/typegpu/src/tgsl/wgslGenerator.ts +++ b/packages/typegpu/src/tgsl/wgslGenerator.ts @@ -5,13 +5,19 @@ import { type AnyData, ConsoleLog, InfixDispatch, - isData, isLooseData, - MatrixColumnsAccess, + toStorable, UnknownData, } from '../data/dataTypes.ts'; -import { abstractInt, bool, u32 } from '../data/numeric.ts'; -import { isSnippet, snip, type Snippet } from '../data/snippet.ts'; +import { bool, i32, u32 } from '../data/numeric.ts'; +import { + isEphemeralOrigin, + isEphemeralSnippet, + isSnippet, + type Origin, + snip, + type Snippet, +} from '../data/snippet.ts'; import * as wgsl from '../data/wgslTypes.ts'; import { invariant, ResolutionError, WgslTypeError } from '../errors.ts'; import { getName } from '../shared/meta.ts'; @@ -19,22 +25,24 @@ import { isMarkedInternal } from '../shared/symbols.ts'; import { safeStringify } from '../shared/stringify.ts'; import { $internal } from '../shared/symbols.ts'; import { pow } from '../std/numeric.ts'; -import { add, div, mul, sub } from '../std/operators.ts'; -import type { FnArgsConversionHint } from '../types.ts'; +import { add, div, mul, neg, sub } from '../std/operators.ts'; +import { type FnArgsConversionHint, isKnownAtComptime } from '../types.ts'; import { convertStructValues, convertToCommonType, tryConvertSnippet, } from './conversion.ts'; import { - coerceToSnippet, + accessIndex, + accessProp, concretize, type GenerationCtx, - getTypeForIndexAccess, - getTypeForPropAccess, numericLiteralToSnippet, } from './generationHelpers.ts'; import type { ShaderGenerator } from './shaderGenerator.ts'; +import type { DualFn } from '../data/dualFn.ts'; +import { createPtrFromOrigin, implicitFrom, ptrFn } from '../data/ptr.ts'; +import { RefOperator } from '../data/ref.ts'; import { constant } from '../core/constant/tgpuConstant.ts'; const { NodeTypeCatalog: NODE } = tinyest; @@ -42,6 +50,8 @@ const { NodeTypeCatalog: NODE } = tinyest; const parenthesizedOps = [ '==', '!=', + '===', + '!==', '<', '<=', '>', @@ -60,34 +70,60 @@ const parenthesizedOps = [ '||', ]; -const binaryLogicalOps = ['&&', '||', '==', '!=', '<', '<=', '>', '>=']; - -const infixKinds = [ - 'vec2f', - 'vec3f', - 'vec4f', - 'vec2h', - 'vec3h', - 'vec4h', - 'vec2i', - 'vec3i', - 'vec4i', - 'vec2u', - 'vec3u', - 'vec4u', - 'mat2x2f', - 'mat3x3f', - 'mat4x4f', +const binaryLogicalOps = [ + '&&', + '||', + '==', + '!=', + '===', + '!==', + '<', + '<=', + '>', + '>=', ]; -export const infixOperators = { - add, - sub, - mul, - div, -} as const; - -export type InfixOperator = keyof typeof infixOperators; +const OP_MAP = { + // + // binary + // + '===': '==', + '!==': '!=', + get '>>>'(): never { + throw new Error('The `>>>` operator is unsupported in TypeGPU functions.'); + }, + get in(): never { + throw new Error('The `in` operator is unsupported in TypeGPU functions.'); + }, + get instanceof(): never { + throw new Error( + 'The `instanceof` operator is unsupported in TypeGPU functions.', + ); + }, + get '|>'(): never { + throw new Error('The `|>` operator is unsupported in TypeGPU functions.'); + }, + // + // logical + // + '||': '||', + '&&': '&&', + get '??'(): never { + throw new Error('The `??` operator is unsupported in TypeGPU functions.'); + }, + // + // assignment + // + get '>>>='(): never { + throw new Error('The `>>>=` operator is unsupported in TypeGPU functions.'); + }, + get '**='(): never { + throw new Error('The `**=` operator is unsupported in TypeGPU functions.'); + }, + get '??='(): never { + throw new Error('The `??=` operator is unsupported in TypeGPU functions.'); + }, +} as Record; type Operator = | tinyest.BinaryOperator @@ -118,7 +154,13 @@ function operatorToType< return lhs; } -const opCodeToCodegen = { +const unaryOpCodeToCodegen = { + '-': neg[$internal].gpuImpl, +} satisfies Partial< + Record unknown> +>; + +const binaryOpCodeToCodegen = { '+': add[$internal].gpuImpl, '-': sub[$internal].gpuImpl, '*': mul[$internal].gpuImpl, @@ -161,11 +203,45 @@ ${this.ctx.pre}}`; } } + public refVariable( + id: string, + dataType: wgsl.StorableData, + ): string { + const varName = this.ctx.makeNameValid(id); + const ptrType = ptrFn(dataType); + const snippet = snip( + new RefOperator(snip(varName, dataType, 'function'), ptrType), + ptrType, + 'function', + ); + this.ctx.defineVariable(id, snippet); + return varName; + } + public blockVariable( + varType: 'var' | 'let' | 'const', id: string, dataType: wgsl.AnyWgslData | UnknownData, + origin: Origin, ): Snippet { - const snippet = snip(this.ctx.makeNameValid(id), dataType); + let varOrigin: Origin = 'runtime'; + if (origin === 'constant-ref') { + // Even types that aren't naturally referential (like vectors or structs) should + // be treated as constant references when assigned to a const. + varOrigin = 'constant-ref'; + } else if (origin === 'argument' && !wgsl.isNaturallyEphemeral(dataType)) { + varOrigin = 'argument'; + } else if (!wgsl.isNaturallyEphemeral(dataType)) { + varOrigin = isEphemeralOrigin(origin) ? 'function' : origin; + } else if (origin === 'constant' && varType === 'const') { + varOrigin = 'constant'; + } + + const snippet = snip( + this.ctx.makeNameValid(id), + dataType, + /* origin */ varOrigin, + ); this.ctx.defineVariable(id, snippet); return snippet; } @@ -210,7 +286,7 @@ ${this.ctx.pre}}`; } if (typeof expression === 'boolean') { - return snip(expression, bool); + return snip(expression, bool, /* ref */ 'constant'); } if ( @@ -219,19 +295,44 @@ ${this.ctx.pre}}`; expression[0] === NODE.assignmentExpr ) { // Logical/Binary/Assignment Expression - const [_, lhs, op, rhs] = expression; + const [exprType, lhs, op, rhs] = expression; const lhsExpr = this.expression(lhs); const rhsExpr = this.expression(rhs); - const codegen = opCodeToCodegen[op as keyof typeof opCodeToCodegen]; + if (rhsExpr.value instanceof RefOperator) { + throw new WgslTypeError( + stitch`Cannot assign a ref to an existing variable '${lhsExpr}', define a new variable instead.`, + ); + } + + if (op === '==') { + throw new Error('Please use the === operator instead of =='); + } + + if ( + op === '===' && isKnownAtComptime(lhsExpr) && isKnownAtComptime(rhsExpr) + ) { + return snip(lhsExpr.value === rhsExpr.value, bool, 'constant'); + } + + if (lhsExpr.dataType.type === 'unknown') { + throw new WgslTypeError(`Left-hand side of '${op}' is of unknown type`); + } + + if (rhsExpr.dataType.type === 'unknown') { + throw new WgslTypeError( + `Right-hand side of '${op}' is of unknown type`, + ); + } + + const codegen = + binaryOpCodeToCodegen[op as keyof typeof binaryOpCodeToCodegen]; if (codegen) { return codegen(lhsExpr, rhsExpr); } - const forcedType = expression[0] === NODE.assignmentExpr - ? lhsExpr.dataType.type === 'ptr' - ? [lhsExpr.dataType.inner as AnyData] - : [lhsExpr.dataType as AnyData] + const forcedType = exprType === NODE.assignmentExpr + ? [toStorable(lhsExpr.dataType)] : undefined; const [convLhs, convRhs] = @@ -242,11 +343,48 @@ ${this.ctx.pre}}`; const rhsStr = this.ctx.resolve(convRhs.value, convRhs.dataType).value; const type = operatorToType(convLhs.dataType, op, convRhs.dataType); + if (exprType === NODE.assignmentExpr) { + if ( + convLhs.origin === 'constant' || convLhs.origin === 'constant-ref' + ) { + throw new WgslTypeError( + `'${lhsStr} = ${rhsStr}' is invalid, because ${lhsStr} is a constant.`, + ); + } + + if (lhsExpr.origin === 'argument') { + throw new WgslTypeError( + `'${lhsStr} ${op} ${rhsStr}' is invalid, because non-pointer arguments cannot be mutated.`, + ); + } + + if ( + rhsExpr.origin === 'argument' && + !wgsl.isNaturallyEphemeral(rhsExpr.dataType) + ) { + throw new WgslTypeError( + `'${lhsStr} = ${rhsStr}' is invalid, because argument references cannot be assigned.\n-----\nTry '${lhsStr} = ${ + this.ctx.resolve(rhsExpr.dataType).value + }(${rhsStr})' to copy the value instead.\n-----`, + ); + } + + if (!isEphemeralSnippet(rhsExpr)) { + throw new WgslTypeError( + `'${lhsStr} = ${rhsStr}' is invalid, because references cannot be assigned.\n-----\nTry '${lhsStr} = ${ + this.ctx.resolve(rhsExpr.dataType).value + }(${rhsStr})' to copy the value instead.\n-----`, + ); + } + } + return snip( parenthesizedOps.includes(op) - ? `(${lhsStr} ${op} ${rhsStr})` - : `${lhsStr} ${op} ${rhsStr}`, + ? `(${lhsStr} ${OP_MAP[op] ?? op} ${rhsStr})` + : `${lhsStr} ${OP_MAP[op] ?? op} ${rhsStr}`, type, + // Result of an operation, so not a reference to anything + /* ref */ 'runtime', ); } @@ -256,17 +394,26 @@ ${this.ctx.pre}}`; const argExpr = this.expression(arg); const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value; - return snip(`${argStr}${op}`, argExpr.dataType); + // Result of an operation, so not a reference to anything + return snip(`${argStr}${op}`, argExpr.dataType, /* ref */ 'runtime'); } if (expression[0] === NODE.unaryExpr) { // Unary Expression const [_, op, arg] = expression; const argExpr = this.expression(arg); + + const codegen = + unaryOpCodeToCodegen[op as keyof typeof unaryOpCodeToCodegen]; + if (codegen) { + return codegen(argExpr); + } + const argStr = this.ctx.resolve(argExpr.value, argExpr.dataType).value; const type = operatorToType(argExpr.dataType, op); - return snip(`${op}${argStr}`, type); + // Result of an operation, so not a reference to anything + return snip(`${op}${argStr}`, type, /* ref */ 'runtime'); } if (expression[0] === NODE.memberAccess) { @@ -275,125 +422,43 @@ ${this.ctx.pre}}`; const target = this.expression(targetNode); if (target.value === console) { - return snip( - new ConsoleLog(property), - UnknownData, - ); - } - - if ( - infixKinds.includes(target.dataType.type) && - property in infixOperators - ) { - return { - value: new InfixDispatch( - property, - target, - infixOperators[property as InfixOperator][$internal].gpuImpl, - ), - dataType: UnknownData, - }; - } - - if (target.dataType.type === 'unknown') { - // No idea what the type is, so we act on the snippet's value and try to guess - - // biome-ignore lint/suspicious/noExplicitAny: we're inspecting the value, and it could be any value - const propValue = (target.value as any)[property]; - - // We try to extract any type information based on the prop's value - return coerceToSnippet(propValue); + return snip(new ConsoleLog(property), UnknownData, /* ref */ 'runtime'); } - if (wgsl.isPtr(target.dataType)) { - return snip( - `(*${this.ctx.resolve(target.value).value}).${property}`, - getTypeForPropAccess(target.dataType.inner as AnyData, property), + const accessed = accessProp(target, property); + if (!accessed) { + throw new Error( + stitch`Property '${property}' not found on value '${target}' of type ${ + this.ctx.resolve(target.dataType) + }`, ); } - - if (wgsl.isWgslArray(target.dataType) && property === 'length') { - if (target.dataType.elementCount === 0) { - // Dynamically-sized array - return snip( - `arrayLength(&${this.ctx.resolve(target.value).value})`, - u32, - ); - } - - return snip(String(target.dataType.elementCount), abstractInt); - } - - if (wgsl.isMat(target.dataType) && property === 'columns') { - return snip(new MatrixColumnsAccess(target), UnknownData); - } - - if ( - wgsl.isVec(target.dataType) && wgsl.isVecInstance(target.value) - ) { - // We're operating on a vector that's known at resolution time - // biome-ignore lint/suspicious/noExplicitAny: it's probably a swizzle - return coerceToSnippet((target.value as any)[property]); - } - - return snip( - `${this.ctx.resolve(target.value).value}.${property}`, - getTypeForPropAccess(target.dataType, property), - ); + return accessed; } if (expression[0] === NODE.indexAccess) { // Index Access const [_, targetNode, propertyNode] = expression; const target = this.expression(targetNode); - const property = this.expression(propertyNode); - const propertyStr = - this.ctx.resolve(property.value, property.dataType).value; + const inProperty = this.expression(propertyNode); + const property = convertToCommonType( + [inProperty], + [u32, i32], + /* verbose */ false, + )?.[0] ?? inProperty; + + const accessed = accessIndex(target, property); + if (!accessed) { + const targetStr = this.ctx.resolve(target.value, target.dataType).value; + const propertyStr = + this.ctx.resolve(property.value, property.dataType).value; - if (target.value instanceof MatrixColumnsAccess) { - return snip( - stitch`${target.value.matrix}[${propertyStr}]`, - getTypeForIndexAccess(target.value.matrix.dataType as AnyData), - ); - } - const targetStr = this.ctx.resolve(target.value, target.dataType).value; - - if (target.dataType.type === 'unknown') { - // No idea what the type is, so we act on the snippet's value and try to guess - - if ( - Array.isArray(propertyNode) && propertyNode[0] === NODE.numericLiteral - ) { - return coerceToSnippet( - // biome-ignore lint/suspicious/noExplicitAny: we're inspecting the value, and it could be any value - (target.value as any)[propertyNode[1] as number], - ); - } - - throw new Error( - `Unable to index a value of unknown type with index ${propertyStr}. If the value is an array, to address this, consider one of the following approaches: (1) declare the array using 'tgpu.const', (2) store the array in a buffer, or (3) define the array within the GPU function scope.`, - ); - } - - if (wgsl.isMat(target.dataType)) { throw new Error( - "The only way of accessing matrix elements in TGSL is through the 'columns' property.", + `Unable to index value ${targetStr} of unknown type with index ${propertyStr}. If the value is an array, to address this, consider one of the following approaches: (1) declare the array using 'tgpu.const', (2) store the array in a buffer, or (3) define the array within the GPU function scope.`, ); } - if (wgsl.isPtr(target.dataType)) { - return snip( - `(*${targetStr})[${propertyStr}]`, - getTypeForIndexAccess(target.dataType.inner as AnyData), - ); - } - - return snip( - `${targetStr}[${propertyStr}]`, - isData(target.dataType) - ? getTypeForIndexAccess(target.dataType) - : UnknownData, - ); + return accessed; } if (expression[0] === NODE.numericLiteral) { @@ -426,6 +491,8 @@ ${this.ctx.pre}}`; return snip( `${this.ctx.resolve(callee.value).value}()`, callee.value, + // A new struct, so not a reference + /* ref */ 'runtime', ); } @@ -439,6 +506,8 @@ ${this.ctx.pre}}`; return snip( this.ctx.resolve(arg.value, callee.value).value, callee.value, + // A new struct, so not a reference + /* ref */ 'runtime', ); } @@ -466,9 +535,18 @@ ${this.ctx.pre}}`; args, ); if (shellless) { + const converted = args.map((s, idx) => { + const argType = shellless.argTypes[idx] as AnyData; + return tryConvertSnippet(s, argType, /* verbose */ false); + }); + return this.ctx.withResetIndentLevel(() => { const snippet = this.ctx.resolve(shellless); - return snip(stitch`${snippet.value}(${args})`, snippet.dataType); + return snip( + stitch`${snippet.value}(${converted})`, + snippet.dataType, + /* ref */ 'runtime', + ); }); } @@ -484,10 +562,27 @@ ${this.ctx.pre}}`; const argConversionHint = (callee.value[$internal] as Record) ?.argConversionHint as FnArgsConversionHint ?? 'keep'; + const strictSignature = (callee.value as DualFn)[$internal] + ?.strictSignature; + try { let convertedArguments: Snippet[]; - if (Array.isArray(argConversionHint)) { + if (strictSignature) { + // The function's signature does not depend on the context, so it can be used to + // give a hint to the argument expressions that a specific type is expected. + convertedArguments = argNodes.map((arg, i) => { + const argType = strictSignature.argTypes[i]; + if (!argType) { + throw new WgslTypeError( + `Function '${ + getName(callee.value) + }' was called with too many arguments`, + ); + } + return this.typedExpression(arg, argType); + }); + } else if (Array.isArray(argConversionHint)) { // The hint is an array of schemas. convertedArguments = argNodes.map((arg, i) => { const argType = argConversionHint[i]; @@ -533,9 +628,13 @@ ${this.ctx.pre}}`; ); } return fnRes; - } catch (error) { - throw new ResolutionError(error, [{ - toString: () => getName(callee.value), + } catch (err) { + if (err instanceof ResolutionError) { + throw err; + } + + throw new ResolutionError(err, [{ + toString: () => `fn:${getName(callee.value)}`, }]); } } @@ -575,6 +674,7 @@ ${this.ctx.pre}}`; return snip( stitch`${this.ctx.resolve(structType).value}(${convertedSnippets})`, structType, + /* ref */ 'runtime', ); } @@ -630,11 +730,12 @@ ${this.ctx.pre}}`; elemType as wgsl.AnyWgslData, values.length, ) as wgsl.AnyWgslData, + /* ref */ 'runtime', ); } if (expression[0] === NODE.stringLiteral) { - return snip(expression[1], UnknownData); + return snip(expression[1], UnknownData, /* origin */ 'constant'); } if (expression[0] === NODE.preUpdate) { @@ -668,13 +769,65 @@ ${this.ctx.pre}}`; if (returnNode !== undefined) { const expectedReturnType = this.ctx.topFunctionReturnType; - const returnSnippet = expectedReturnType + let returnSnippet = expectedReturnType ? this.typedExpression( returnNode, expectedReturnType, ) : this.expression(returnNode); + if (returnSnippet.value instanceof RefOperator) { + throw new WgslTypeError( + stitch`Cannot return references, returning '${returnSnippet.value.snippet}'`, + ); + } + + // Arguments cannot be returned from functions without copying. A simple example why is: + // const identity = (x) => { + // 'use gpu'; + // return x; + // }; + // + // const foo = (arg: d.v3f) => { + // 'use gpu'; + // const marg = identity(arg); + // marg.x = 1; // 'marg's origin would be 'runtime', so we wouldn't be able to track this misuse. + // }; + if ( + returnSnippet.origin === 'argument' && + !isEphemeralSnippet(returnSnippet) + ) { + throw new WgslTypeError( + stitch`Cannot return references to arguments, returning '${returnSnippet}'. Copy the argument before returning it.`, + ); + } + + if ( + !expectedReturnType && + !isEphemeralSnippet(returnSnippet) && + returnSnippet.origin !== 'function' + ) { + const str = this.ctx.resolve( + returnSnippet.value, + returnSnippet.dataType, + ).value; + const typeStr = this.ctx.resolve( + toStorable(returnSnippet.dataType as wgsl.StorableData), + ).value; + throw new WgslTypeError( + `'return ${str};' is invalid, cannot return references. +----- +Try 'return ${typeStr}(${str});' instead. +-----`, + ); + } + + returnSnippet = tryConvertSnippet( + returnSnippet, + toStorable(returnSnippet.dataType as wgsl.StorableData), + false, + ); + invariant( returnSnippet.dataType.type !== 'unknown', 'Return type should be known', @@ -716,7 +869,8 @@ ${this.ctx.pre}else ${alternate}`; } if (statement[0] === NODE.let || statement[0] === NODE.const) { - const [_, rawId, rawValue] = statement; + let varType: 'var' | 'let' | 'const' = 'var'; + const [stmtType, rawId, rawValue] = statement; const eq = rawValue !== undefined ? this.expression(rawValue) : undefined; if (!eq) { @@ -725,18 +879,117 @@ ${this.ctx.pre}else ${alternate}`; ); } + const ephemeral = isEphemeralSnippet(eq); + let dataType = eq.dataType as wgsl.AnyWgslData; + const naturallyEphemeral = wgsl.isNaturallyEphemeral(dataType); + if (isLooseData(eq.dataType)) { throw new Error( `Cannot create variable '${rawId}' with loose data type.`, ); } + if (eq.value instanceof RefOperator) { + // We're assigning a newly created `d.ref()` + if (eq.dataType.type !== 'unknown') { + throw new WgslTypeError( + `Cannot store d.ref() in a variable if it references another value. Copy the value passed into d.ref() instead.`, + ); + } + const refSnippet = eq.value.snippet; + const varName = this.refVariable( + rawId, + concretize(refSnippet.dataType as AnyData) as wgsl.StorableData, + ); + return stitch`${this.ctx.pre}var ${varName} = ${ + tryConvertSnippet( + refSnippet, + refSnippet.dataType as wgsl.AnyWgslData, + false, + ) + };`; + } + + // Assigning a reference to a `const` variable means we store the pointer + // of the rhs. + if (!ephemeral) { + // Referential + if (stmtType === NODE.let) { + const rhsStr = this.ctx.resolve(eq.value).value; + const rhsTypeStr = + this.ctx.resolve(toStorable(eq.dataType as wgsl.StorableData)) + .value; + + throw new WgslTypeError( + `'let ${rawId} = ${rhsStr}' is invalid, because references cannot be assigned to 'let' variable declarations. +----- +- Try 'let ${rawId} = ${rhsTypeStr}(${rhsStr})' if you need to reassign '${rawId}' later +- Try 'const ${rawId} = ${rhsStr}' if you won't reassign '${rawId}' later. +-----`, + ); + } + + if (eq.origin === 'constant-ref') { + varType = 'const'; + } else { + varType = 'let'; + if (!wgsl.isPtr(dataType)) { + const ptrType = createPtrFromOrigin( + eq.origin, + concretize(dataType) as wgsl.StorableData, + ); + invariant( + ptrType !== undefined, + `Creating pointer type from origin ${eq.origin}`, + ); + dataType = ptrType; + } + + if (!(eq.value instanceof RefOperator)) { + // If what we're assigning is something preceded by `&`, then it's a value + // created using `d.ref()`. Otherwise, it's an implicit pointer + dataType = implicitFrom(dataType); + } + } + } else { + // Non-referential + + if (eq.origin === 'argument') { + if (stmtType === NODE.let) { + const rhsStr = this.ctx.resolve(eq.value).value; + const rhsTypeStr = + this.ctx.resolve(toStorable(eq.dataType as wgsl.StorableData)) + .value; + + throw new WgslTypeError( + `'let ${rawId} = ${rhsStr}' is invalid, because references to arguments cannot be assigned to 'let' variable declarations. +----- +- Try 'let ${rawId} = ${rhsTypeStr}(${rhsStr})' if you need to reassign '${rawId}' later +- Try 'const ${rawId} = ${rhsStr}' if you won't reassign '${rawId}' later. +-----`, + ); + } + varType = 'let'; + } + + if (stmtType === NODE.const) { + if (eq.origin === 'argument') { + // Arguments cannot be mutated, so we 'let' them be (kill me) + varType = 'let'; + } else if (naturallyEphemeral) { + varType = eq.origin === 'constant' ? 'const' : 'let'; + } + } + } + const snippet = this.blockVariable( + varType, rawId, - concretize(eq.dataType as wgsl.AnyWgslData), + concretize(dataType), + eq.origin, ); - return stitch`${this.ctx.pre}var ${snippet - .value as string} = ${eq};`; + return stitch`${this.ctx.pre}${varType} ${snippet + .value as string} = ${tryConvertSnippet(eq, dataType, false)};`; } if (statement[0] === NODE.block) { @@ -747,13 +1000,11 @@ ${this.ctx.pre}else ${alternate}`; const [_, init, condition, update, body] = statement; const [initStatement, conditionExpr, updateStatement] = this.ctx - .withResetIndentLevel( - () => [ - init ? this.statement(init) : undefined, - condition ? this.typedExpression(condition, bool) : undefined, - update ? this.statement(update) : undefined, - ], - ); + .withResetIndentLevel(() => [ + init ? this.statement(init) : undefined, + condition ? this.typedExpression(condition, bool) : undefined, + update ? this.statement(update) : undefined, + ]); const initStr = initStatement ? initStatement.slice(0, -1) : ''; const updateStr = updateStatement ? updateStatement.slice(0, -1) : ''; diff --git a/packages/typegpu/src/types.ts b/packages/typegpu/src/types.ts index 377e1905cb..01d07a1a16 100644 --- a/packages/typegpu/src/types.ts +++ b/packages/typegpu/src/types.ts @@ -317,6 +317,12 @@ export function getOwnSnippet(value: unknown): Snippet | undefined { return (value as WithOwnSnippet)?.[$ownSnippet]; } +export function isKnownAtComptime(snippet: Snippet): boolean { + return (typeof snippet.value !== 'string' || + snippet.dataType.type === 'unknown') && + getOwnSnippet(snippet.value) === undefined; +} + export function isWgsl(value: unknown): value is Wgsl { return ( typeof value === 'number' || diff --git a/packages/typegpu/tests/accessor.test.ts b/packages/typegpu/tests/accessor.test.ts index 97ab627e72..3edec5835a 100644 --- a/packages/typegpu/tests/accessor.test.ts +++ b/packages/typegpu/tests/accessor.test.ts @@ -136,11 +136,11 @@ describe('tgpu.accessor', () => { fn main() { var color = vec3f(1, 0, 0); - var color2 = redUniform; + let color2 = (&redUniform); var color3 = getColor(); - var colorX = 1; - var color2X = redUniform.x; - var color3X = getColor().x; + const colorX = 1f; + let color2X = redUniform.x; + let color3X = getColor().x; }" `); }); @@ -155,7 +155,7 @@ describe('tgpu.accessor', () => { expect(asWgsl(main)).toMatchInlineSnapshot(` "fn main() { - var foo = 1f; + const foo = 1f; }" `); }); diff --git a/packages/typegpu/tests/array.test.ts b/packages/typegpu/tests/array.test.ts index fdd184b694..33dd4a9f22 100644 --- a/packages/typegpu/tests/array.test.ts +++ b/packages/typegpu/tests/array.test.ts @@ -216,7 +216,7 @@ describe('array', () => { expect(asWgsl(testFn)).toMatchInlineSnapshot(` "fn testFn() { var myArrays = array, 1>(array(10i)); - var myClone = myArrays[0]; + var myClone = myArrays[0i]; return; }" `); @@ -255,7 +255,7 @@ describe('array', () => { [Error: Resolution of the following tree failed: - - fn:foo - - arrayOf: Cannot create array schema with count unknown at compile-time: 'count'] + - fn:arrayOf: Cannot create array schema with count unknown at compile-time: 'count'] `); }); @@ -383,7 +383,7 @@ describe('array.length', () => { fn foo() { var acc = 1f; - for (var i = 0; (i < 128); i++) { + for (var i = 0; (i < 128i); i++) { values[i] = acc; acc *= 2f; } @@ -406,9 +406,7 @@ describe('array.length', () => { }); expect(asWgsl(testFn)).toMatchInlineSnapshot(` - "@group(0) @binding(0) var values: array; - - fn testFn() -> i32 { + "fn testFn() -> i32 { return 5; }" `); @@ -431,7 +429,7 @@ describe('array.length', () => { "@group(0) @binding(0) var values: array; fn testFn() -> u32 { - return arrayLength(&values); + return arrayLength((&values)); }" `); }); diff --git a/packages/typegpu/tests/bindGroupLayout.test.ts b/packages/typegpu/tests/bindGroupLayout.test.ts index 2272bb106d..4c2b0724ff 100644 --- a/packages/typegpu/tests/bindGroupLayout.test.ts +++ b/packages/typegpu/tests/bindGroupLayout.test.ts @@ -229,6 +229,38 @@ describe('TgpuBindGroupLayout', () => { fn main() { textureLoad(fooTexture); }" `); }); + + it('takes a pointer to layout.$... if assigned to a const variable', () => { + const Boid = d.struct({ + pos: d.vec3f, + vel: d.vec3f, + }); + + const layout = tgpu.bindGroupLayout({ + boids: { storage: d.arrayOf(Boid), access: 'mutable' }, + }); + + const getFirst = () => { + 'use gpu'; + const boids = layout.$.boids; + // biome-ignore lint/style/noNonNullAssertion: it's definitely there + return Boid(boids[0]!); + }; + + expect(asWgsl(getFirst)).toMatchInlineSnapshot(` + "struct Boid { + pos: vec3f, + vel: vec3f, + } + + @group(0) @binding(0) var boids: array; + + fn getFirst() -> Boid { + let boids = (&boids); + return (*boids)[0i]; + }" + `); + }); }); describe('TgpuBindGroup', () => { diff --git a/packages/typegpu/tests/bufferUsage.test.ts b/packages/typegpu/tests/bufferUsage.test.ts index 84a80e7e1b..567415981d 100644 --- a/packages/typegpu/tests/bufferUsage.test.ts +++ b/packages/typegpu/tests/bufferUsage.test.ts @@ -41,7 +41,7 @@ describe('TgpuBufferUniform', () => { "@group(0) @binding(0) var param: f32; fn func() { - var x = param; + let x = param; }" `); }); @@ -69,8 +69,8 @@ describe('TgpuBufferUniform', () => { @group(0) @binding(0) var boid: Boid; fn func() { - var pos = boid.pos; - var velX = boid.vel.x; + let pos = (&boid.pos); + let velX = boid.vel.x; }" `); }); @@ -110,7 +110,7 @@ describe('TgpuBufferMutable', () => { "@group(0) @binding(0) var param: f32; fn func() { - var x = param; + let x = param; }" `); }); @@ -140,8 +140,8 @@ describe('TgpuBufferMutable', () => { @group(0) @binding(0) var boid: Boid; fn func() { - var pos = boid.pos; - var velX = boid.vel.x; + let pos = (&boid.pos); + let velX = boid.vel.x; }" `); }); @@ -200,7 +200,7 @@ describe('TgpuBufferReadonly', () => { "@group(0) @binding(0) var paramBuffer: f32; fn func() { - var x = paramBuffer; + let x = paramBuffer; }" `); }); @@ -229,8 +229,8 @@ describe('TgpuBufferReadonly', () => { @group(0) @binding(0) var boid: Boid; fn func() { - var pos = boid.pos; - var velX = boid.vel.x; + let pos = (&boid.pos); + let velX = boid.vel.x; }" `); }); diff --git a/packages/typegpu/tests/computePipeline.test.ts b/packages/typegpu/tests/computePipeline.test.ts index 197240337d..3b7bbe0829 100644 --- a/packages/typegpu/tests/computePipeline.test.ts +++ b/packages/typegpu/tests/computePipeline.test.ts @@ -548,10 +548,10 @@ describe('TgpuComputePipeline', () => { @compute @workgroup_size(1) fn fn_0(_arg_0: fn_Input_1) { var a = array(); { - a[0] = f16(_arg_0.gid.x); + a[0i] = f16(_arg_0.gid.x); } { - a[1] = 1h; + a[1i] = 1h; } }" diff --git a/packages/typegpu/tests/constant.test.ts b/packages/typegpu/tests/constant.test.ts index 53d30eaa9d..2588f5513e 100644 --- a/packages/typegpu/tests/constant.test.ts +++ b/packages/typegpu/tests/constant.test.ts @@ -3,8 +3,13 @@ import * as d from '../src/data/index.ts'; import tgpu from '../src/index.ts'; import { asWgsl } from './utils/parseResolved.ts'; +const Boid = d.struct({ + pos: d.vec3f, + vel: d.vec3u, +}); + describe('tgpu.const', () => { - it('should inject const declaration when used in functions', () => { + it('should inject const declaration when used in shelled WGSL functions', () => { const x = tgpu.const(d.u32, 2); const fn1 = tgpu.fn([], d.u32)`() { return x; }`.$uses({ x }); @@ -15,12 +20,7 @@ describe('tgpu.const', () => { `); }); - it('allows accessing constants in tgsl through .value', () => { - const Boid = d.struct({ - pos: d.vec3f, - vel: d.vec3u, - }); - + it('allows accessing constants in TypeGPU functions through .$', () => { const boid = tgpu.const(Boid, { pos: d.vec3f(1, 2, 3), vel: d.vec3u(4, 5, 6), @@ -41,10 +41,55 @@ describe('tgpu.const', () => { const boid: Boid = Boid(vec3f(1, 2, 3), vec3u(4, 5, 6)); fn func() { - var pos = boid; - var vel = boid.vel; - var velX = boid.vel.x; + const pos = boid; + const vel = boid.vel; + const velX = boid.vel.x; }" `); }); + + it('cannot be passed directly to shellless functions', () => { + const fn1 = (v: d.v3f) => { + 'use gpu'; + return v.x * v.y * v.z; + }; + + const foo = tgpu.const(d.vec3f, d.vec3f(1, 2, 3)); + const fn2 = () => { + 'use gpu'; + return fn1(foo.$); + }; + + expect(() => asWgsl(fn2)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:fn2 + - fn*:fn2(): Cannot pass constant references as function arguments. Explicitly copy them by wrapping them in a schema: 'vec3f(...)'] + `); + }); + + it('cannot be mutated', () => { + const boid = tgpu.const(Boid, { + pos: d.vec3f(1, 2, 3), + vel: d.vec3u(4, 5, 6), + }); + + const fn = () => { + 'use gpu'; + // @ts-expect-error: Cannot assign to read-only property + boid.$.pos = d.vec3f(0, 0, 0); + }; + + expect(() => asWgsl(fn)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:fn + - fn*:fn(): 'boid.pos = vec3f()' is invalid, because boid.pos is a constant.] + `); + + // Since we freeze the object, we cannot mutate when running the function in JS either + expect(() => fn()).toThrowErrorMatchingInlineSnapshot( + `[TypeError: Cannot assign to read only property 'pos' of object '#']`, + ); + }); }); diff --git a/packages/typegpu/tests/data/ptr.test.ts b/packages/typegpu/tests/data/ptr.test.ts index 99bbf173e4..3fdeb32d3f 100644 --- a/packages/typegpu/tests/data/ptr.test.ts +++ b/packages/typegpu/tests/data/ptr.test.ts @@ -21,13 +21,13 @@ describe('d.ptrFn', () => { it('modifies reference types in JS', () => { const modifyVec = tgpu.fn([d.ptrFn(d.vec2f)])((ptr) => { - ptr.x += 1; + ptr.$.x += 1; }); const testFn = tgpu.fn([], d.vec2f)(() => { - const vec = d.vec2f(1, 2); + const vec = d.ref(d.vec2f(1, 2)); modifyVec(vec); - return vec; + return vec.$; }); expect(testFn()).toStrictEqual(d.vec2f(2, 2)); diff --git a/packages/typegpu/tests/derived.test.ts b/packages/typegpu/tests/derived.test.ts index ae2856db5b..2d16786315 100644 --- a/packages/typegpu/tests/derived.test.ts +++ b/packages/typegpu/tests/derived.test.ts @@ -150,11 +150,11 @@ describe('TgpuDerived', () => { fn func() { var pos = vec3f(2, 4, 6); - var posX = 2; - var vel = boid.vel; - var velX = boid.vel.x; - var vel_ = boid.vel; - var velX_ = boid.vel.x; + const posX = 2f; + let vel = (&boid.vel); + let velX = boid.vel.x; + let vel_ = (&boid.vel); + let velX_ = boid.vel.x; }" `); }); diff --git a/packages/typegpu/tests/examples/individual/3d-fish.test.ts b/packages/typegpu/tests/examples/individual/3d-fish.test.ts index 797bbbb0f0..2e2ac23d73 100644 --- a/packages/typegpu/tests/examples/individual/3d-fish.test.ts +++ b/packages/typegpu/tests/examples/individual/3d-fish.test.ts @@ -40,8 +40,8 @@ describe('3d fish example', () => { } fn item_8() -> f32 { - var a = dot(seed_6, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_6, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_6, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_6, vec2f(54.47856521606445, 345.8415222167969)); seed_6.x = fract((cos(a) * 136.8168f)); seed_6.y = fract((cos(b) * 534.7645f)); return seed_6.y; @@ -122,7 +122,7 @@ describe('3d fish example', () => { fn projectPointOnLine_10(point: vec3f, line: Line3_9) -> vec3f { var pointVector = (point - line.origin); - var projection = dot(pointVector, line.dir); + let projection = dot(pointVector, line.dir); return (line.origin + (line.dir * projection)); } @@ -131,7 +131,7 @@ describe('3d fish example', () => { @group(1) @binding(1) var nextFishData_12: array; fn simulate_2(fishIndex: u32, _arg_1: u32, _arg_2: u32) { - var fishData = ModelData_4(currentFishData_3[fishIndex].position, currentFishData_3[fishIndex].direction, currentFishData_3[fishIndex].scale, currentFishData_3[fishIndex].variant, currentFishData_3[fishIndex].applySinWave, currentFishData_3[fishIndex].applySeaFog, currentFishData_3[fishIndex].applySeaDesaturation); + let fishData = (¤tFishData_3[fishIndex]); var separation = vec3f(); var alignment = vec3f(); var alignmentCount = 0; @@ -143,57 +143,59 @@ describe('3d fish example', () => { if ((u32(i) == fishIndex)) { continue; } - var other = ModelData_4(currentFishData_3[i].position, currentFishData_3[i].direction, currentFishData_3[i].scale, currentFishData_3[i].variant, currentFishData_3[i].applySinWave, currentFishData_3[i].applySeaFog, currentFishData_3[i].applySeaDesaturation); - var dist = length((fishData.position - other.position)); + let other = (¤tFishData_3[i]); + let dist = length(((*fishData).position - (*other).position)); if ((dist < fishBehavior_5.separationDist)) { - separation = (separation + (fishData.position - other.position)); + separation = (separation + ((*fishData).position - (*other).position)); } if ((dist < fishBehavior_5.alignmentDist)) { - alignment = (alignment + other.direction); + alignment = (alignment + (*other).direction); alignmentCount = (alignmentCount + 1i); } if ((dist < fishBehavior_5.cohesionDist)) { - cohesion = (cohesion + other.position); + cohesion = (cohesion + (*other).position); cohesionCount = (cohesionCount + 1i); } } if ((alignmentCount > 0i)) { - alignment = ((1f / f32(alignmentCount)) * alignment); + alignment = (alignment * (1f / f32(alignmentCount))); } if ((cohesionCount > 0i)) { - cohesion = (((1f / f32(cohesionCount)) * cohesion) - fishData.position); + cohesion = (((1f / f32(cohesionCount)) * cohesion) - (*fishData).position); } for (var i = 0; (i < 3i); i += 1i) { var repulsion = vec3f(); repulsion[i] = 1f; - var axisAquariumSize = (vec3f(10, 4, 10)[i] / 2f); - var axisPosition = fishData.position[i]; - var distance = 0.1; + let axisAquariumSize = (vec3f(10, 4, 10)[i] / 2f); + let axisPosition = (*fishData).position[i]; + const distance = 0.1; if ((axisPosition > (axisAquariumSize - distance))) { - var str = (axisPosition - (axisAquariumSize - distance)); - wallRepulsion = (wallRepulsion - (str * repulsion)); + let str = (axisPosition - (axisAquariumSize - distance)); + wallRepulsion = (wallRepulsion - (repulsion * str)); } - if ((axisPosition < (-axisAquariumSize + distance))) { - var str = ((-axisAquariumSize + distance) - axisPosition); - wallRepulsion = (wallRepulsion + (str * repulsion)); + if ((axisPosition < (-(axisAquariumSize) + distance))) { + let str = ((-(axisAquariumSize) + distance) - axisPosition); + wallRepulsion = (wallRepulsion + (repulsion * str)); } } if ((mouseRay_7.activated == 1u)) { - var proj = projectPointOnLine_10(fishData.position, mouseRay_7.line); - var diff = (fishData.position - proj); - var limit = 0.9; - var str = (pow(2f, clamp((limit - length(diff)), 0f, limit)) - 1f); - rayRepulsion = (str * normalize(diff)); + var proj = projectPointOnLine_10((*fishData).position, mouseRay_7.line); + var diff = ((*fishData).position - proj); + const limit = 0.9; + let str = (pow(2f, clamp((limit - length(diff)), 0f, limit)) - 1f); + rayRepulsion = (normalize(diff) * str); } - fishData.direction = (fishData.direction + (fishBehavior_5.separationStr * separation)); - fishData.direction = (fishData.direction + (fishBehavior_5.alignmentStr * alignment)); - fishData.direction = (fishData.direction + (fishBehavior_5.cohesionStr * cohesion)); - fishData.direction = (fishData.direction + (1e-4 * wallRepulsion)); - fishData.direction = (fishData.direction + (5e-4 * rayRepulsion)); - fishData.direction = (clamp(length(fishData.direction), 0f, 0.01f) * normalize(fishData.direction)); - var translation = ((min(999f, timePassed_11) / 8f) * fishData.direction); - fishData.position = (fishData.position + translation); - nextFishData_12[fishIndex] = fishData; + var direction = (*fishData).direction; + direction = (direction + (separation * fishBehavior_5.separationStr)); + direction = (direction + (alignment * fishBehavior_5.alignmentStr)); + direction = (direction + (cohesion * fishBehavior_5.cohesionStr)); + direction = (direction + (wallRepulsion * 1e-4)); + direction = (direction + (rayRepulsion * 5e-4)); + direction = (normalize(direction) * clamp(length((*fishData).direction), 0f, 0.01f)); + var translation = (direction * (min(999f, timePassed_11) / 8f)); + let nextFishData = (&nextFishData_12[fishIndex]); + (*nextFishData).position = ((*fishData).position + translation); + (*nextFishData).direction = direction; } struct mainCompute_Input_13 { @@ -225,14 +227,14 @@ describe('3d fish example', () => { } fn applySinWave_4(index: u32, vertex: PosAndNormal_3, time: f32) -> PosAndNormal_3 { - var a = -60.1; - var b = 0.8; - var c = 6.1; + const a = -60.1; + const b = 0.8; + const c = 6.1; var posMod = vec3f(); posMod.z = (sin((f32(index) + (((time / a) + vertex.position.x) / b))) / c); - var coeff = (cos((f32(index) + (((time / a) + vertex.position.x) / b))) / c); + let coeff = (cos((f32(index) + (((time / a) + vertex.position.x) / b))) / c); var newOX = normalize(vec3f(1f, 0f, coeff)); - var newOZ = vec3f(-newOX.z, 0f, newOX.x); + var newOZ = vec3f(-(newOX.z), 0f, newOX.x); var newNormalXZ = ((newOX * vertex.normal.x) + (newOZ * vertex.normal.z)); var wavedNormal = vec3f(newNormalXZ.x, vertex.normal.y, newNormalXZ.z); var wavedPosition = (vertex.position + posMod); @@ -274,16 +276,16 @@ describe('3d fish example', () => { wavedVertex = applySinWave_4(input.instanceIndex, PosAndNormal_3(input.modelPosition, input.modelNormal), currentTime_5); } var direction = normalize(currentModelData.direction); - var yaw = (-atan2(direction.z, direction.x) + 3.141592653589793f); - var pitch = asin(-direction.y); + let yaw = (-(atan2(direction.z, direction.x)) + 3.141592653589793f); + let pitch = asin(-(direction.y)); var scaleMatrix = mat4x4f(vec3f(currentModelData.scale).x, 0, 0, 0, 0, vec3f(currentModelData.scale).y, 0, 0, 0, 0, vec3f(currentModelData.scale).z, 0, 0, 0, 0, 1); var pitchMatrix = mat4x4f(cos(pitch), sin(pitch), 0, 0, -sin(pitch), cos(pitch), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); var yawMatrix = mat4x4f(cos(yaw), 0, -sin(yaw), 0, 0, 1, 0, 0, sin(yaw), 0, cos(yaw), 0, 0, 0, 0, 1); var translationMatrix = mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, currentModelData.position.x, currentModelData.position.y, currentModelData.position.z, 1); var worldPosition = (translationMatrix * (yawMatrix * (pitchMatrix * (scaleMatrix * vec4f(wavedVertex.position, 1f))))); var worldNormal = normalize((yawMatrix * (pitchMatrix * vec4f(wavedVertex.normal, 1f))).xyz); - var worldPositionUniform = worldPosition; - var canvasPosition = (camera_6.projection * (camera_6.view * worldPositionUniform)); + let worldPositionUniform = (&worldPosition); + var canvasPosition = (camera_6.projection * (camera_6.view * (*worldPositionUniform))); return vertexShader_Output_8(worldPosition.xyz, worldNormal, canvasPosition, currentModelData.variant, input.textureUV, currentModelData.applySeaFog, currentModelData.applySeaDesaturation); } @@ -292,12 +294,12 @@ describe('3d fish example', () => { @group(0) @binding(3) var sampler_12: sampler; fn rgbToHsv_13(rgb: vec3f) -> vec3f { - var r = rgb.x; - var g = rgb.y; - var b = rgb.z; - var maxC = max(r, max(g, b)); - var minC = min(r, min(g, b)); - var delta = (maxC - minC); + let r = rgb.x; + let g = rgb.y; + let b = rgb.z; + let maxC = max(r, max(g, b)); + let minC = min(r, min(g, b)); + let delta = (maxC - minC); var h = 0f; var s = 0f; if ((maxC == 0f)) { @@ -306,7 +308,7 @@ describe('3d fish example', () => { else { s = (delta / maxC); } - var v = maxC; + let v = maxC; if ((maxC == minC)) { h = 0f; } @@ -339,14 +341,14 @@ describe('3d fish example', () => { } fn hsvToRgb_14(hsv: vec3f) -> vec3f { - var h = hsv.x; - var s = hsv.y; - var v = hsv.z; - var i = floor((h * 6f)); - var f = ((h * 6f) - i); - var p = (v * (1f - s)); - var q = (v * (1f - (f * s))); - var t = (v * (1f - ((1f - f) * s))); + let h = hsv.x; + let s = hsv.y; + let v = hsv.z; + let i = floor((h * 6f)); + let f = ((h * 6f) - i); + let p = (v * (1f - s)); + let q = (v * (1f - (f * s))); + let t = (v * (1f - ((1f - f) * s))); var r = 0f; var g = 0f; var b = 0f; @@ -405,17 +407,17 @@ describe('3d fish example', () => { var textureColorWithAlpha = textureSample(modelTexture_11, sampler_12, input.textureUV); var textureColor = textureColorWithAlpha.xyz; var ambient = (0.5 * (textureColor * vec3f(0.800000011920929, 0.800000011920929, 1))); - var cosTheta = dot(input.worldNormal, vec3f(-0.2357022613286972, 0.9428090453147888, -0.2357022613286972)); + let cosTheta = dot(input.worldNormal, vec3f(-0.2357022613286972, 0.9428090453147888, -0.2357022613286972)); var diffuse = (max(0f, cosTheta) * (textureColor * vec3f(0.800000011920929, 0.800000011920929, 1))); var viewSource = normalize((camera_6.position.xyz - input.worldPosition)); - var reflectSource = normalize(reflect((-1 * vec3f(-0.2357022613286972, 0.9428090453147888, -0.2357022613286972)), input.worldNormal)); - var specularStrength = pow(max(0f, dot(viewSource, reflectSource)), 16f); + var reflectSource = normalize(reflect(vec3f(0.2357022613286972, -0.9428090453147888, 0.2357022613286972), input.worldNormal)); + let specularStrength = pow(max(0f, dot(viewSource, reflectSource)), 16f); var specular = (specularStrength * vec3f(0.800000011920929, 0.800000011920929, 1)); var lightedColor = (ambient + (diffuse + specular)); - var distanceFromCamera = length((camera_6.position.xyz - input.worldPosition)); + let distanceFromCamera = length((camera_6.position.xyz - input.worldPosition)); var desaturatedColor = lightedColor; if ((input.applySeaDesaturation == 1u)) { - var desaturationFactor = (-atan2(((distanceFromCamera - 5f) / 10f), 1f) / 3f); + let desaturationFactor = (-(atan2(((distanceFromCamera - 5f) / 10f), 1f)) / 3f); var hsv = rgbToHsv_13(desaturatedColor); hsv.y += (desaturationFactor / 2f); hsv.z += desaturationFactor; @@ -424,8 +426,8 @@ describe('3d fish example', () => { } var foggedColor = desaturatedColor; if ((input.applySeaFog == 1u)) { - var fogParameter = max(0f, ((distanceFromCamera - 1.5f) * 0.2f)); - var fogFactor = (fogParameter / (1f + fogParameter)); + let fogParameter = max(0f, ((distanceFromCamera - 1.5f) * 0.2f)); + let fogFactor = (fogParameter / (1f + fogParameter)); foggedColor = mix(foggedColor, vec3f(0, 0.47843137383461, 0.800000011920929), fogFactor); } return vec4f(foggedColor.xyz, 1f); diff --git a/packages/typegpu/tests/examples/individual/ascii-filter.test.ts b/packages/typegpu/tests/examples/individual/ascii-filter.test.ts index 9a8bbb95b7..c5eee8ab8a 100644 --- a/packages/typegpu/tests/examples/individual/ascii-filter.test.ts +++ b/packages/typegpu/tests/examples/individual/ascii-filter.test.ts @@ -46,11 +46,11 @@ describe('ascii filter example', () => { @group(0) @binding(4) var charsetExtended_9: u32; fn characterFn_10(n: u32, p: vec2f) -> f32 { - var pos = floor(((p * vec2f(-4, 4f)) + 2.5)); + var pos = floor(((p * vec2f(-4, 4)) + 2.5)); if (((((pos.x < 0f) || (pos.x > 4f)) || (pos.y < 0f)) || (pos.y > 4f))) { return 0f; } - var a = u32((pos.x + (5f * pos.y))); + let a = u32((pos.x + (5f * pos.y))); return f32(((n >> a) & 1u)); } @@ -64,12 +64,12 @@ describe('ascii filter example', () => { var uv2 = ((uvTransformBuffer_4 * (input.uv - 0.5)) + 0.5); var textureSize = vec2f(textureDimensions(externalTexture_5)); var pix = (uv2 * textureSize); - var cellSize = f32(glyphSize_6); - var halfCell = (cellSize * 0.5f); + let cellSize = f32(glyphSize_6); + let halfCell = (cellSize * 0.5f); var blockCoord = ((floor((pix / cellSize)) * cellSize) / textureSize); var color = textureSampleBaseClampToEdge(externalTexture_5, shaderSampler_7, blockCoord); - var rawGray = (((0.3f * color.x) + (0.59f * color.y)) + (0.11f * color.z)); - var gray = pow(rawGray, gammaCorrection_8); + let rawGray = (((0.3f * color.x) + (0.59f * color.y)) + (0.11f * color.z)); + let gray = pow(rawGray, gammaCorrection_8); var n = 4096u; if ((charsetExtended_9 == 0u)) { if ((gray > 0.2f)) { @@ -223,7 +223,7 @@ describe('ascii filter example', () => { } } var p = vec2f((((pix.x / halfCell) % 2f) - 1f), (((pix.y / halfCell) % 2f) - 1f)); - var charValue = characterFn_10(n, p); + let charValue = characterFn_10(n, p); var resultColor = vec3f(1); if ((displayMode_11 == 0u)) { resultColor = (color * charValue).xyz; diff --git a/packages/typegpu/tests/examples/individual/blur.test.ts b/packages/typegpu/tests/examples/individual/blur.test.ts index 345254a0e0..a90f60921a 100644 --- a/packages/typegpu/tests/examples/individual/blur.test.ts +++ b/packages/typegpu/tests/examples/individual/blur.test.ts @@ -42,17 +42,17 @@ describe('blur example', () => { } @compute @workgroup_size(32, 1, 1) fn computeFn_0(_arg_0: computeFn_Input_8) { - var settings2 = settingsUniform_1; - var filterOffset = i32((f32((settings2.filterDim - 1i)) / 2f)); + let settings2 = (&settingsUniform_1); + let filterOffset = i32((f32(((*settings2).filterDim - 1i)) / 2f)); var dims = vec2i(textureDimensions(inTexture_3)); - var baseIndex = (vec2i(((_arg_0.wid.xy * vec2u(settings2.blockDim, 4u)) + (_arg_0.lid.xy * vec2u(4, 1)))) - vec2i(filterOffset, 0i)); + var baseIndex = (vec2i(((_arg_0.wid.xy * vec2u((*settings2).blockDim, 4u)) + (_arg_0.lid.xy * vec2u(4, 1)))) - vec2i(filterOffset, 0i)); for (var r = 0; (r < 4i); r++) { for (var c = 0; (c < 4i); c++) { var loadIndex = (baseIndex + vec2i(c, r)); if ((flip_4 != 0u)) { loadIndex = loadIndex.yx; } - tileData_5[r][((_arg_0.lid.x * 4u) + u32(c))] = textureSampleLevel(inTexture_3, sampler_6, vec2f(((vec2f(loadIndex) + vec2f(0.5)) / vec2f(dims))), 0).xyz; + tileData_5[r][((_arg_0.lid.x * 4u) + u32(c))] = textureSampleLevel(inTexture_3, sampler_6, ((vec2f(loadIndex) + vec2f(0.5)) / vec2f(dims)), 0).xyz; } } workgroupBarrier(); @@ -62,12 +62,12 @@ describe('blur example', () => { if ((flip_4 != 0u)) { writeIndex = writeIndex.yx; } - var center = (i32((4u * _arg_0.lid.x)) + c); + let center = (i32((4u * _arg_0.lid.x)) + c); if ((((center >= filterOffset) && (center < (128i - filterOffset))) && all((writeIndex < dims)))) { var acc = vec3f(); - for (var f = 0; (f < settings2.filterDim); f++) { - var i = ((center + f) - filterOffset); - acc = (acc + (tileData_5[r][i] * (1f / f32(settings2.filterDim)))); + for (var f = 0; (f < (*settings2).filterDim); f++) { + let i = ((center + f) - filterOffset); + acc = (acc + (tileData_5[r][i] * (1f / f32((*settings2).filterDim)))); } textureStore(outTexture_7, writeIndex, vec4f(acc, 1f)); } diff --git a/packages/typegpu/tests/examples/individual/boids-next.test.ts b/packages/typegpu/tests/examples/individual/boids-next.test.ts index e1e0ab45c5..8294debddd 100644 --- a/packages/typegpu/tests/examples/individual/boids-next.test.ts +++ b/packages/typegpu/tests/examples/individual/boids-next.test.ts @@ -50,17 +50,17 @@ describe('boids next example', () => { if ((i == index)) { continue; } - var other = currentTrianglePos_3[i]; - var dist = distance(instanceInfo.position, other.position); + let other = (¤tTrianglePos_3[i]); + let dist = distance(instanceInfo.position, (*other).position); if ((dist < paramsBuffer_5.separationDistance)) { - separation = (separation + (instanceInfo.position - other.position)); + separation = (separation + (instanceInfo.position - (*other).position)); } if ((dist < paramsBuffer_5.alignmentDistance)) { - alignment = (alignment + other.velocity); + alignment = (alignment + (*other).velocity); alignmentCount++; } if ((dist < paramsBuffer_5.cohesionDistance)) { - cohesion = (cohesion + other.position); + cohesion = (cohesion + (*other).position); cohesionCount++; } } @@ -77,15 +77,15 @@ describe('boids next example', () => { instanceInfo.velocity = (instanceInfo.velocity + velocity); instanceInfo.velocity = (clamp(length(instanceInfo.velocity), 0f, 0.01f) * normalize(instanceInfo.velocity)); if ((instanceInfo.position.x > 1.03f)) { - instanceInfo.position.x = (-1 - 0.03); + instanceInfo.position.x = -1.03f; } if ((instanceInfo.position.y > 1.03f)) { - instanceInfo.position.y = (-1 - 0.03); + instanceInfo.position.y = -1.03f; } - if ((instanceInfo.position.x < (-1 - 0.03))) { + if ((instanceInfo.position.x < -1.03f)) { instanceInfo.position.x = 1.03f; } - if ((instanceInfo.position.y < (-1 - 0.03))) { + if ((instanceInfo.position.y < -1.03f)) { instanceInfo.position.y = 1.03f; } instanceInfo.position = (instanceInfo.position + instanceInfo.velocity); @@ -104,12 +104,12 @@ describe('boids next example', () => { } fn getRotationFromVelocity_1(velocity: vec2f) -> f32 { - return -atan2(velocity.x, velocity.y); + return -(atan2(velocity.x, velocity.y)); } fn rotate_2(v: vec2f, angle: f32) -> vec2f { - var cos = cos(angle); - var sin = sin(angle); + let cos = cos(angle); + let sin = sin(angle); return vec2f(((v.x * cos) - (v.y * sin)), ((v.x * sin) + (v.y * cos))); } @@ -127,7 +127,7 @@ describe('boids next example', () => { } @vertex fn mainVert_0(input: mainVert_Input_5) -> mainVert_Output_4 { - var angle = getRotationFromVelocity_1(input.velocity); + let angle = getRotationFromVelocity_1(input.velocity); var rotated = rotate_2(input.v, angle); var pos = vec4f((rotated + input.center), 0f, 1f); var color = vec4f(((sin((colorPalette_3 + angle)) * 0.45) + 0.45), 1f); diff --git a/packages/typegpu/tests/examples/individual/box-raytracing.test.ts b/packages/typegpu/tests/examples/individual/box-raytracing.test.ts index 464b79cb3e..669948dbfd 100644 --- a/packages/typegpu/tests/examples/individual/box-raytracing.test.ts +++ b/packages/typegpu/tests/examples/individual/box-raytracing.test.ts @@ -36,7 +36,7 @@ describe('box raytracing example', () => { } @vertex fn mainVertex_0(input: mainVertex_Input_4) -> mainVertex_Output_3 { - var pos = array(vec2f(-1, -1), vec2f(3f, -1), vec2f(-1, 3f)); + var pos = array(vec2f(-1), vec2f(3, -1), vec2f(-1, 3)); var rayWorldOrigin = (uniforms_1.invViewMatrix * vec4f(0, 0, 0, 1)).xyz; return mainVertex_Output_3(vec4f(pos[input.vertexIndex], 0f, 1f), rayWorldOrigin); } @@ -136,7 +136,7 @@ describe('box raytracing example', () => { var boxSize3 = vec3f(uniforms_1.boxSize); var halfBoxSize3 = (0.5 * boxSize3); var halfCanvasDims = (0.5 * uniforms_1.canvasDims); - var minDim = min(uniforms_1.canvasDims.x, uniforms_1.canvasDims.y); + let minDim = min(uniforms_1.canvasDims.x, uniforms_1.canvasDims.y); var viewCoords = ((input.position.xy - halfCanvasDims) / minDim); var ray = Ray_6(input.rayWorldOrigin, (uniforms_1.invViewMatrix * vec4f(normalize(vec3f(viewCoords, 1f)), 0f)).xyz); var bigBoxIntersection = getBoxIntersection_8(AxisAlignedBounds_7((-1 * halfBoxSize3), (vec3f(7) + halfBoxSize3)), ray); @@ -157,7 +157,7 @@ describe('box raytracing example', () => { var ijkScaled = vec3f(f32(i), f32(j), f32(k)); var intersection = getBoxIntersection_8(AxisAlignedBounds_7((ijkScaled - halfBoxSize3), (ijkScaled + halfBoxSize3)), ray); if (intersection.intersects) { - var boxDensity = (max(0f, (intersection.tMax - intersection.tMin)) * pow(uniforms_1.materialDensity, 2f)); + let boxDensity = (max(0f, (intersection.tMax - intersection.tMin)) * pow(uniforms_1.materialDensity, 2f)); density += boxDensity; invColor = (invColor + (boxDensity * (vec3f(1) / boxMatrix_10[i][j][k].albedo))); tMin = intersection.tMin; @@ -168,7 +168,7 @@ describe('box raytracing example', () => { } var linear = (vec3f(1) / invColor); var srgb = linearToSrgb_12(linear); - var gamma = 2.2; + const gamma = 2.2; var corrected = pow(srgb, vec3f((1f / gamma))); if (intersectionFound) { return (min(density, 1f) * vec4f(min(corrected, vec3f(1)), 1f)); diff --git a/packages/typegpu/tests/examples/individual/caustics.test.ts b/packages/typegpu/tests/examples/individual/caustics.test.ts index 1604d665fa..c893437a2f 100644 --- a/packages/typegpu/tests/examples/individual/caustics.test.ts +++ b/packages/typegpu/tests/examples/individual/caustics.test.ts @@ -27,7 +27,7 @@ describe('caustics example', () => { } @vertex fn mainVertex_0(_arg_0: mainVertex_Input_2) -> mainVertex_Output_1 { - var pos = array(vec2f(0, 0.800000011920929), vec2f(-0.8, -0.8), vec2f(0.8f, -0.8)); + var pos = array(vec2f(0, 0.800000011920929), vec2f(-0.800000011920929), vec2f(0.800000011920929, -0.800000011920929)); var uv = array(vec2f(0.5, 1), vec2f(), vec2f(1, 0)); return mainVertex_Output_1(vec4f(pos[_arg_0.vertexIndex], 0f, 1f), uv[_arg_0.vertexIndex]); } @@ -37,7 +37,7 @@ describe('caustics example', () => { fn tilePattern_5(uv: vec2f) -> f32 { var tiledUv = fract(uv); var proximity = abs(((tiledUv * 2) - 1)); - var maxProximity = max(proximity.x, proximity.y); + let maxProximity = max(proximity.x, proximity.y); return saturate((pow((1f - maxProximity), 0.6f) * 5f)); } @@ -54,19 +54,19 @@ describe('caustics example', () => { } fn item_15() -> f32 { - var a = dot(seed_13, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_13, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_13, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_13, vec2f(54.47856521606445, 345.8415222167969)); seed_13.x = fract((cos(a) * 136.8168f)); seed_13.y = fract((cos(b) * 534.7645f)); return seed_13.y; } fn randOnUnitSphere_14() -> vec3f { - var z = ((2f * item_15()) - 1f); - var oneMinusZSq = sqrt((1f - (z * z))); - var theta = (6.283185307179586f * item_15()); - var x = (cos(theta) * oneMinusZSq); - var y = (sin(theta) * oneMinusZSq); + let z = ((2f * item_15()) - 1f); + let oneMinusZSq = sqrt((1f - (z * z))); + let theta = (6.283185307179586f * item_15()); + let x = (cos(theta) * oneMinusZSq); + let y = (sin(theta) * oneMinusZSq); return vec3f(x, y, z); } @@ -87,34 +87,34 @@ describe('caustics example', () => { fn sample_8(pos: vec3f) -> f32 { var minJunction = floor(pos); - var xyz = dotProdGrid_9(pos, minJunction); - var xyZ = dotProdGrid_9(pos, (minJunction + vec3f(0, 0, 1))); - var xYz = dotProdGrid_9(pos, (minJunction + vec3f(0, 1, 0))); - var xYZ = dotProdGrid_9(pos, (minJunction + vec3f(0, 1, 1))); - var Xyz = dotProdGrid_9(pos, (minJunction + vec3f(1, 0, 0))); - var XyZ = dotProdGrid_9(pos, (minJunction + vec3f(1, 0, 1))); - var XYz = dotProdGrid_9(pos, (minJunction + vec3f(1, 1, 0))); - var XYZ = dotProdGrid_9(pos, (minJunction + vec3f(1))); + let xyz = dotProdGrid_9(pos, minJunction); + let xyZ = dotProdGrid_9(pos, (minJunction + vec3f(0, 0, 1))); + let xYz = dotProdGrid_9(pos, (minJunction + vec3f(0, 1, 0))); + let xYZ = dotProdGrid_9(pos, (minJunction + vec3f(0, 1, 1))); + let Xyz = dotProdGrid_9(pos, (minJunction + vec3f(1, 0, 0))); + let XyZ = dotProdGrid_9(pos, (minJunction + vec3f(1, 0, 1))); + let XYz = dotProdGrid_9(pos, (minJunction + vec3f(1, 1, 0))); + let XYZ = dotProdGrid_9(pos, (minJunction + vec3f(1))); var partial = (pos - minJunction); var smoothPartial = quinticInterpolationImpl_16(partial); - var xy = mix(xyz, xyZ, smoothPartial.z); - var xY = mix(xYz, xYZ, smoothPartial.z); - var Xy = mix(Xyz, XyZ, smoothPartial.z); - var XY = mix(XYz, XYZ, smoothPartial.z); - var x = mix(xy, xY, smoothPartial.y); - var X = mix(Xy, XY, smoothPartial.y); + let xy = mix(xyz, xyZ, smoothPartial.z); + let xY = mix(xYz, xYZ, smoothPartial.z); + let Xy = mix(Xyz, XyZ, smoothPartial.z); + let XY = mix(XYz, XYZ, smoothPartial.z); + let x = mix(xy, xY, smoothPartial.y); + let X = mix(Xy, XY, smoothPartial.y); return mix(x, X, smoothPartial.x); } fn caustics_7(uv: vec2f, time2: f32, profile: vec3f) -> vec3f { - var distortion = sample_8(vec3f((uv * 0.5), (time2 * 0.2f))); + let distortion = sample_8(vec3f((uv * 0.5), (time2 * 0.2f))); var uv2 = (uv + distortion); - var noise = abs(sample_8(vec3f((uv2 * 5), time2))); + let noise = abs(sample_8(vec3f((uv2 * 5), time2))); return pow(vec3f((1f - noise)), profile); } fn rotateXY_17(angle2: f32) -> mat2x2f { - return mat2x2f(vec2f(cos(angle2), sin(angle2)), vec2f(-sin(angle2), cos(angle2))); + return mat2x2f(vec2f(cos(angle2), sin(angle2)), vec2f(-(sin(angle2)), cos(angle2))); } struct mainFragment_Input_18 { @@ -122,19 +122,19 @@ describe('caustics example', () => { } @fragment fn mainFragment_3(_arg_0: mainFragment_Input_18) -> @location(0) vec4f { - var skewMat = mat2x2f(vec2f(0.9800665974617004, 0.19866932928562164), vec2f(((-0.19866933079506122 * 10.) + (_arg_0.uv.x * 3f)), 4.900332889206208f)); + var skewMat = mat2x2f(vec2f(0.9800665974617004, 0.19866932928562164), vec2f((-1.9866933079506122f + (_arg_0.uv.x * 3f)), 4.900332889206208f)); var skewedUv = (skewMat * _arg_0.uv); - var tile = tilePattern_5((skewedUv * tileDensity_4)); + let tile = tilePattern_5((skewedUv * tileDensity_4)); var albedo = mix(vec3f(0.10000000149011612), vec3f(1), tile); var cuv = vec2f(((_arg_0.uv.x * (pow((_arg_0.uv.y * 1.5f), 3f) + 0.1f)) * 5f), (pow((((_arg_0.uv.y * 1.5f) + 0.1f) * 1.5f), 3f) * 1f)); var c1 = (caustics_7(cuv, (time_6 * 0.2f), vec3f(4, 4, 1)) * vec3f(0.4000000059604645, 0.6499999761581421, 1)); var c2 = (caustics_7((cuv * 2), (time_6 * 0.4f), vec3f(16, 1, 4)) * vec3f(0.18000000715255737, 0.30000001192092896, 0.5)); var blendCoord = vec3f((_arg_0.uv * vec2f(5, 10)), ((time_6 * 0.2f) + 5f)); - var blend = saturate((sample_8(blendCoord) + 0.3f)); + let blend = saturate((sample_8(blendCoord) + 0.3f)); var noFogColor = (albedo * mix(vec3f(0.20000000298023224, 0.5, 1), (c1 + c2), blend)); - var fog = min((pow(_arg_0.uv.y, 0.5f) * 1.2f), 1f); - var godRayUv = ((rotateXY_17(-0.3) * _arg_0.uv) * vec2f(15, 3)); - var godRayFactor = pow(_arg_0.uv.y, 1f); + let fog = min((pow(_arg_0.uv.y, 0.5f) * 1.2f), 1f); + var godRayUv = ((rotateXY_17(-0.3f) * _arg_0.uv) * vec2f(15, 3)); + let godRayFactor = pow(_arg_0.uv.y, 1f); var godRay1 = ((sample_8(vec3f(godRayUv, (time_6 * 0.5f))) + 1f) * (vec3f(0.18000000715255737, 0.30000001192092896, 0.5) * godRayFactor)); var godRay2 = ((sample_8(vec3f((godRayUv * 2), (time_6 * 0.3f))) + 1f) * (vec3f(0.18000000715255737, 0.30000001192092896, 0.5) * (godRayFactor * 0.4f))); var godRays = (godRay1 + godRay2); diff --git a/packages/typegpu/tests/examples/individual/cubemap-reflection.test.ts b/packages/typegpu/tests/examples/individual/cubemap-reflection.test.ts index 11f9be33d8..f4fd098b0d 100644 --- a/packages/typegpu/tests/examples/individual/cubemap-reflection.test.ts +++ b/packages/typegpu/tests/examples/individual/cubemap-reflection.test.ts @@ -50,8 +50,8 @@ describe('cubemap reflection example', () => { @group(0) @binding(1) var nextVertices_7: array; fn packVec2u_8(toPack: vec4f) -> vec2u { - var xy = pack2x16float(toPack.xy); - var zw = pack2x16float(toPack.zw); + let xy = pack2x16float(toPack.xy); + let zw = pack2x16float(toPack.zw); return vec2u(xy, zw); } @@ -60,12 +60,12 @@ describe('cubemap reflection example', () => { } @compute @workgroup_size(256, 1, 1) fn computeFn_0(input: computeFn_Input_9) { - var triangleCount = u32((f32(arrayLength(&prevVertices_1)) / 3f)); - var triangleIndex = (input.gid.x + (input.gid.y * 65535u)); + let triangleCount = u32((f32(arrayLength(&prevVertices_1)) / 3f)); + let triangleIndex = (input.gid.x + (input.gid.y * 65535u)); if ((triangleIndex >= triangleCount)) { return; } - var baseIndexPrev = (triangleIndex * 3u); + let baseIndexPrev = (triangleIndex * 3u); var v1 = unpackVec2u_3(prevVertices_1[baseIndexPrev].position); var v2 = unpackVec2u_3(prevVertices_1[(baseIndexPrev + 1u)].position); var v3 = unpackVec2u_3(prevVertices_1[(baseIndexPrev + 2u)].position); @@ -73,19 +73,18 @@ describe('cubemap reflection example', () => { var v23 = vec4f(normalize(calculateMidpoint_4(v2, v3).xyz), 1f); var v31 = vec4f(normalize(calculateMidpoint_4(v3, v1).xyz), 1f); var newVertices = array(v1, v12, v31, v2, v23, v12, v3, v31, v23, v12, v23, v31); - var baseIndexNext = (triangleIndex * 12u); + let baseIndexNext = (triangleIndex * 12u); for (var i = 0u; (i < 12u); i++) { - var reprojectedVertex = newVertices[i]; - var triBase = (i - (i % 3u)); - var normal = reprojectedVertex; + let reprojectedVertex = (&newVertices[i]); + let triBase = (i - (i % 3u)); + var normal = (*reprojectedVertex); if ((smoothFlag_5 == 0u)) { normal = getAverageNormal_6(newVertices[triBase], newVertices[(triBase + 1u)], newVertices[(triBase + 2u)]); } - var outIndex = (baseIndexNext + i); - var nextVertex = nextVertices_7[outIndex]; - nextVertex.position = packVec2u_8(reprojectedVertex); - nextVertex.normal = packVec2u_8(normal); - nextVertices_7[outIndex] = nextVertex; + let outIndex = (baseIndexNext + i); + let nextVertex = (&nextVertices_7[outIndex]); + (*nextVertex).position = packVec2u_8((*reprojectedVertex)); + (*nextVertex).normal = packVec2u_8(normal); } } @@ -178,11 +177,11 @@ describe('cubemap reflection example', () => { var normalizedNormal = normalize(input.normal.xyz); var normalizedLightDir = normalize(light_6.direction); var ambientLight = (material_8.ambient * (light_6.intensity * light_6.color)); - var diffuseFactor = max(dot(normalizedNormal, normalizedLightDir), 0f); + let diffuseFactor = max(dot(normalizedNormal, normalizedLightDir), 0f); var diffuseLight = (diffuseFactor * (material_8.diffuse * (light_6.intensity * light_6.color))); var viewDirection = normalize((camera_1.position.xyz - input.worldPos.xyz)); var reflectionDirection = reflect(-(normalizedLightDir), normalizedNormal); - var specularFactor = pow(max(dot(viewDirection, reflectionDirection), 0f), material_8.shininess); + let specularFactor = pow(max(dot(viewDirection, reflectionDirection), 0f), material_8.shininess); var specularLight = (specularFactor * (material_8.specular * (light_6.intensity * light_6.color))); var reflectionVector = reflect(-(viewDirection), normalizedNormal); var environmentColor = textureSample(cubemap_10, texSampler_11, reflectionVector); diff --git a/packages/typegpu/tests/examples/individual/disco.test.ts b/packages/typegpu/tests/examples/individual/disco.test.ts index 39aa37b6a6..40dacd0bda 100644 --- a/packages/typegpu/tests/examples/individual/disco.test.ts +++ b/packages/typegpu/tests/examples/individual/disco.test.ts @@ -28,7 +28,7 @@ describe('disco example', () => { } @vertex fn mainVertex_0(_arg_0: mainVertex_Input_2) -> mainVertex_Output_1 { - var pos = array(vec2f(-1, 1f), vec2f(-1, -1), vec2f(1f, -1), vec2f(-1, 1f), vec2f(1f, -1), vec2f(1)); + var pos = array(vec2f(-1, 1), vec2f(-1), vec2f(1, -1), vec2f(-1, 1), vec2f(1, -1), vec2f(1)); var uv = array(vec2f(0, 1), vec2f(), vec2f(1, 0), vec2f(0, 1), vec2f(1, 0), vec2f(1)); return mainVertex_Output_1(vec4f(pos[_arg_0.vertexIndex], 0f, 1f), uv[_arg_0.vertexIndex]); } @@ -36,8 +36,8 @@ describe('disco example', () => { @group(0) @binding(0) var resolutionUniform_5: vec2f; fn aspectCorrected_4(uv: vec2f) -> vec2f { - var v = ((uv.xy - 0.5) * 2); - var aspect = (resolutionUniform_5.x / resolutionUniform_5.y); + var v = ((uv - 0.5) * 2); + let aspect = (resolutionUniform_5.x / resolutionUniform_5.y); if ((aspect > 1f)) { v.x *= aspect; } @@ -68,17 +68,17 @@ describe('disco example', () => { @fragment fn mainFragment_3(_arg_0: mainFragment_Input_9) -> @location(0) vec4f { { - var aspectUv = aspectCorrected_4(_arg_0.uv); - var originalUv = aspectUv; + var originalUv = aspectCorrected_4(_arg_0.uv); + var aspectUv = originalUv; var accumulatedColor = vec3f(); for (var iteration = 0; (iteration < 5i); iteration++) { aspectUv = (fract((aspectUv * (1.3f * sin(time_6)))) - 0.5); - var radialLength = (length(aspectUv) * exp((-length(originalUv) * 2f))); - var paletteColor = palette_7((length(originalUv) + (time_6 * 0.9f))); + var radialLength = (length(aspectUv) * exp((-(length(originalUv)) * 2f))); radialLength = (sin(((radialLength * 8f) + time_6)) / 8f); radialLength = abs(radialLength); radialLength = smoothstep(0, 0.1, radialLength); radialLength = (0.06f / radialLength); + var paletteColor = palette_7((length(originalUv) + (time_6 * 0.9f))); accumulatedColor = accumulate_8(accumulatedColor, paletteColor, radialLength); } return vec4f(accumulatedColor, 1f); diff --git a/packages/typegpu/tests/examples/individual/dispatch.test.ts b/packages/typegpu/tests/examples/individual/dispatch.test.ts index 9acddf61f3..db8a5db8b5 100644 --- a/packages/typegpu/tests/examples/individual/dispatch.test.ts +++ b/packages/typegpu/tests/examples/individual/dispatch.test.ts @@ -136,7 +136,7 @@ describe('tgsl parsing test example', () => { @group(1) @binding(0) var buffer_3: array; fn wrappedCallback_2(_arg_0: u32, _arg_1: u32, _arg_2: u32) { - for (var i = 0u; (i < arrayLength(&buffer_3)); i++) { + for (var i = 0u; (i < arrayLength((&buffer_3))); i++) { buffer_3[i] *= 2u; } } diff --git a/packages/typegpu/tests/examples/individual/fluid-double-buffering.test.ts b/packages/typegpu/tests/examples/individual/fluid-double-buffering.test.ts index 8efb582f93..3b5f09e290 100644 --- a/packages/typegpu/tests/examples/individual/fluid-double-buffering.test.ts +++ b/packages/typegpu/tests/examples/individual/fluid-double-buffering.test.ts @@ -37,14 +37,14 @@ describe('fluid double buffering example', () => { fn isInsideObstacle_6(x: i32, y: i32) -> bool { for (var obsIdx = 0; (obsIdx < 4i); obsIdx++) { - var obs = obstacles_7[obsIdx]; - if ((obs.enabled == 0u)) { + let obs = (&obstacles_7[obsIdx]); + if (((*obs).enabled == 0u)) { continue; } - var minX = max(0i, (obs.center.x - i32((f32(obs.size.x) / 2f)))); - var maxX = min(256i, (obs.center.x + i32((f32(obs.size.x) / 2f)))); - var minY = max(0i, (obs.center.y - i32((f32(obs.size.y) / 2f)))); - var maxY = min(256i, (obs.center.y + i32((f32(obs.size.y) / 2f)))); + let minX = max(0i, ((*obs).center.x - i32((f32((*obs).size.x) / 2f)))); + let maxX = min(256i, ((*obs).center.x + i32((f32((*obs).size.x) / 2f)))); + let minY = max(0i, ((*obs).center.y - i32((f32((*obs).size.y) / 2f)))); + let maxY = min(256i, ((*obs).center.y + i32((f32((*obs).size.y) / 2f)))); if (((((x >= minX) && (x <= maxX)) && (y >= minY)) && (y <= maxY))) { return true; } @@ -65,16 +65,16 @@ describe('fluid double buffering example', () => { @group(0) @binding(2) var gridBetaBuffer_9: array; fn wrappedCallback_2(xu: u32, yu: u32, _arg_2: u32) { - var x = i32(xu); - var y = i32(yu); - var index = coordsToIndex_3(x, y); + let x = i32(xu); + let y = i32(yu); + let index = coordsToIndex_3(x, y); var value = vec4f(); if (!isValidFlowOut_4(x, y)) { value = vec4f(); } else { if ((y < 128i)) { - var depth = (1f - (f32(y) / 128f)); + let depth = (1f - (f32(y) / 128f)); value = vec4f(0f, 0f, (10f + (depth * 10f)), 0f); } } @@ -130,14 +130,14 @@ describe('fluid double buffering example', () => { fn isInsideObstacle_13(x: i32, y: i32) -> bool { for (var obsIdx = 0; (obsIdx < 4i); obsIdx++) { - var obs = obstacles_14[obsIdx]; - if ((obs.enabled == 0u)) { + let obs = (&obstacles_14[obsIdx]); + if (((*obs).enabled == 0u)) { continue; } - var minX = max(0i, (obs.center.x - i32((f32(obs.size.x) / 2f)))); - var maxX = min(256i, (obs.center.x + i32((f32(obs.size.x) / 2f)))); - var minY = max(0i, (obs.center.y - i32((f32(obs.size.y) / 2f)))); - var maxY = min(256i, (obs.center.y + i32((f32(obs.size.y) / 2f)))); + let minX = max(0i, ((*obs).center.x - i32((f32((*obs).size.x) / 2f)))); + let maxX = min(256i, ((*obs).center.x + i32((f32((*obs).size.x) / 2f)))); + let minY = max(0i, ((*obs).center.y - i32((f32((*obs).size.y) / 2f)))); + let maxY = min(256i, ((*obs).center.y + i32((f32((*obs).size.y) / 2f)))); if (((((x >= minX) && (x <= maxX)) && (y >= minY)) && (y <= maxY))) { return true; } @@ -156,8 +156,8 @@ describe('fluid double buffering example', () => { } fn item_17() -> f32 { - var a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); seed_7.x = fract((cos(a) * 136.8168f)); seed_7.y = fract((cos(b) * 534.7645f)); return seed_7.y; @@ -168,33 +168,33 @@ describe('fluid double buffering example', () => { } fn computeVelocity_10(x: i32, y: i32) -> vec2f { - var gravityCost = 0.5; - var neighborOffsets = array(vec2i(0, 1), vec2i(0i, -1), vec2i(1, 0), vec2i(-1, 0i)); + const gravityCost = 0.5; + var neighborOffsets = array(vec2i(0, 1), vec2i(0, -1), vec2i(1, 0), vec2i(-1, 0)); var cell = getCell_8(x, y); var leastCost = cell.z; var dirChoices = array(vec2f(), vec2f(), vec2f(), vec2f()); var dirChoiceCount = 1; for (var i = 0; (i < 4i); i++) { - var offset = neighborOffsets[i]; - var neighborDensity = getCell_8((x + offset.x), (y + offset.y)); - var cost = (neighborDensity.z + (f32(offset.y) * gravityCost)); - if (!isValidFlowOut_11((x + offset.x), (y + offset.y))) { + let offset = (&neighborOffsets[i]); + var neighborDensity = getCell_8((x + (*offset).x), (y + (*offset).y)); + let cost = (neighborDensity.z + (f32((*offset).y) * gravityCost)); + if (!isValidFlowOut_11((x + (*offset).x), (y + (*offset).y))) { continue; } if ((cost == leastCost)) { - dirChoices[dirChoiceCount] = vec2f(f32(offset.x), f32(offset.y)); + dirChoices[dirChoiceCount] = vec2f(f32((*offset).x), f32((*offset).y)); dirChoiceCount++; } else { if ((cost < leastCost)) { leastCost = cost; - dirChoices[0] = vec2f(f32(offset.x), f32(offset.y)); + dirChoices[0i] = vec2f(f32((*offset).x), f32((*offset).y)); dirChoiceCount = 1i; } } } - var leastCostDir = dirChoices[u32((randFloat01_16() * f32(dirChoiceCount)))]; - return leastCostDir; + let leastCostDir = (&dirChoices[u32((randFloat01_16() * f32(dirChoiceCount)))]); + return (*leastCostDir); } fn flowFromCell_18(myX: i32, myY: i32, x: i32, y: i32) -> f32 { @@ -204,7 +204,7 @@ describe('fluid double buffering example', () => { var src = getCell_8(x, y); var destPos = vec2i((x + i32(src.x)), (y + i32(src.y))); var dest = getCell_8(destPos.x, destPos.y); - var diff = (src.z - dest.z); + let diff = (src.z - dest.z); var outFlow = min(max(0.01f, (0.3f + (diff * 0.1f))), src.z); if ((length(src.xy) < 0.5f)) { outFlow = 0f; @@ -227,8 +227,8 @@ describe('fluid double buffering example', () => { @group(0) @binding(4) var sourceParams_20: item_21; fn getMinimumInFlow_19(x: i32, y: i32) -> f32 { - var gridSizeF = 256f; - var sourceRadius2 = max(1f, (sourceParams_20.radius * gridSizeF)); + const gridSizeF = 256f; + let sourceRadius2 = max(1f, (sourceParams_20.radius * gridSizeF)); var sourcePos = vec2f((sourceParams_20.center.x * gridSizeF), (sourceParams_20.center.y * gridSizeF)); if ((distance(vec2f(f32(x), f32(y)), sourcePos) < sourceRadius2)) { return sourceParams_20.intensity; @@ -239,9 +239,9 @@ describe('fluid double buffering example', () => { @group(0) @binding(5) var gridAlphaBuffer_22: array; fn simulate_2(xu: u32, yu: u32, _arg_2: u32) { - var x = i32(xu); - var y = i32(yu); - var index = coordsToIndex_3(x, y); + let x = i32(xu); + let y = i32(yu); + let index = coordsToIndex_3(x, y); randSeed2_5(vec2f(f32(index), time_4)); var next = getCell_8(x, y); var nextVelocity = computeVelocity_10(x, y); @@ -252,7 +252,7 @@ describe('fluid double buffering example', () => { next.z += flowFromCell_18(x, y, x, (y - 1i)); next.z += flowFromCell_18(x, y, (x + 1i), y); next.z += flowFromCell_18(x, y, (x - 1i), y); - var minInflow = getMinimumInFlow_19(x, y); + let minInflow = getMinimumInFlow_19(x, y); next.z = max(minInflow, next.z); gridAlphaBuffer_22[index] = next; } @@ -306,14 +306,14 @@ describe('fluid double buffering example', () => { fn isInsideObstacle_13(x: i32, y: i32) -> bool { for (var obsIdx = 0; (obsIdx < 4i); obsIdx++) { - var obs = obstacles_14[obsIdx]; - if ((obs.enabled == 0u)) { + let obs = (&obstacles_14[obsIdx]); + if (((*obs).enabled == 0u)) { continue; } - var minX = max(0i, (obs.center.x - i32((f32(obs.size.x) / 2f)))); - var maxX = min(256i, (obs.center.x + i32((f32(obs.size.x) / 2f)))); - var minY = max(0i, (obs.center.y - i32((f32(obs.size.y) / 2f)))); - var maxY = min(256i, (obs.center.y + i32((f32(obs.size.y) / 2f)))); + let minX = max(0i, ((*obs).center.x - i32((f32((*obs).size.x) / 2f)))); + let maxX = min(256i, ((*obs).center.x + i32((f32((*obs).size.x) / 2f)))); + let minY = max(0i, ((*obs).center.y - i32((f32((*obs).size.y) / 2f)))); + let maxY = min(256i, ((*obs).center.y + i32((f32((*obs).size.y) / 2f)))); if (((((x >= minX) && (x <= maxX)) && (y >= minY)) && (y <= maxY))) { return true; } @@ -332,8 +332,8 @@ describe('fluid double buffering example', () => { } fn item_17() -> f32 { - var a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); seed_7.x = fract((cos(a) * 136.8168f)); seed_7.y = fract((cos(b) * 534.7645f)); return seed_7.y; @@ -344,33 +344,33 @@ describe('fluid double buffering example', () => { } fn computeVelocity_10(x: i32, y: i32) -> vec2f { - var gravityCost = 0.5; - var neighborOffsets = array(vec2i(0, 1), vec2i(0i, -1), vec2i(1, 0), vec2i(-1, 0i)); + const gravityCost = 0.5; + var neighborOffsets = array(vec2i(0, 1), vec2i(0, -1), vec2i(1, 0), vec2i(-1, 0)); var cell = getCell_8(x, y); var leastCost = cell.z; var dirChoices = array(vec2f(), vec2f(), vec2f(), vec2f()); var dirChoiceCount = 1; for (var i = 0; (i < 4i); i++) { - var offset = neighborOffsets[i]; - var neighborDensity = getCell_8((x + offset.x), (y + offset.y)); - var cost = (neighborDensity.z + (f32(offset.y) * gravityCost)); - if (!isValidFlowOut_11((x + offset.x), (y + offset.y))) { + let offset = (&neighborOffsets[i]); + var neighborDensity = getCell_8((x + (*offset).x), (y + (*offset).y)); + let cost = (neighborDensity.z + (f32((*offset).y) * gravityCost)); + if (!isValidFlowOut_11((x + (*offset).x), (y + (*offset).y))) { continue; } if ((cost == leastCost)) { - dirChoices[dirChoiceCount] = vec2f(f32(offset.x), f32(offset.y)); + dirChoices[dirChoiceCount] = vec2f(f32((*offset).x), f32((*offset).y)); dirChoiceCount++; } else { if ((cost < leastCost)) { leastCost = cost; - dirChoices[0] = vec2f(f32(offset.x), f32(offset.y)); + dirChoices[0i] = vec2f(f32((*offset).x), f32((*offset).y)); dirChoiceCount = 1i; } } } - var leastCostDir = dirChoices[u32((randFloat01_16() * f32(dirChoiceCount)))]; - return leastCostDir; + let leastCostDir = (&dirChoices[u32((randFloat01_16() * f32(dirChoiceCount)))]); + return (*leastCostDir); } fn flowFromCell_18(myX: i32, myY: i32, x: i32, y: i32) -> f32 { @@ -380,7 +380,7 @@ describe('fluid double buffering example', () => { var src = getCell_8(x, y); var destPos = vec2i((x + i32(src.x)), (y + i32(src.y))); var dest = getCell_8(destPos.x, destPos.y); - var diff = (src.z - dest.z); + let diff = (src.z - dest.z); var outFlow = min(max(0.01f, (0.3f + (diff * 0.1f))), src.z); if ((length(src.xy) < 0.5f)) { outFlow = 0f; @@ -403,8 +403,8 @@ describe('fluid double buffering example', () => { @group(0) @binding(4) var sourceParams_20: item_21; fn getMinimumInFlow_19(x: i32, y: i32) -> f32 { - var gridSizeF = 256f; - var sourceRadius2 = max(1f, (sourceParams_20.radius * gridSizeF)); + const gridSizeF = 256f; + let sourceRadius2 = max(1f, (sourceParams_20.radius * gridSizeF)); var sourcePos = vec2f((sourceParams_20.center.x * gridSizeF), (sourceParams_20.center.y * gridSizeF)); if ((distance(vec2f(f32(x), f32(y)), sourcePos) < sourceRadius2)) { return sourceParams_20.intensity; @@ -415,9 +415,9 @@ describe('fluid double buffering example', () => { @group(0) @binding(5) var gridBetaBuffer_22: array; fn simulate_2(xu: u32, yu: u32, _arg_2: u32) { - var x = i32(xu); - var y = i32(yu); - var index = coordsToIndex_3(x, y); + let x = i32(xu); + let y = i32(yu); + let index = coordsToIndex_3(x, y); randSeed2_5(vec2f(f32(index), time_4)); var next = getCell_8(x, y); var nextVelocity = computeVelocity_10(x, y); @@ -428,7 +428,7 @@ describe('fluid double buffering example', () => { next.z += flowFromCell_18(x, y, x, (y - 1i)); next.z += flowFromCell_18(x, y, (x + 1i), y); next.z += flowFromCell_18(x, y, (x - 1i), y); - var minInflow = getMinimumInFlow_19(x, y); + let minInflow = getMinimumInFlow_19(x, y); next.z = max(minInflow, next.z); gridBetaBuffer_22[index] = next; } @@ -454,7 +454,7 @@ describe('fluid double buffering example', () => { } @vertex fn vertexMain_0(input: vertexMain_Input_2) -> vertexMain_Output_1 { - var pos = array(vec2f(1), vec2f(-1, 1f), vec2f(1f, -1), vec2f(-1, -1)); + var pos = array(vec2f(1), vec2f(-1, 1), vec2f(1, -1), vec2f(-1)); var uv = array(vec2f(1), vec2f(0, 1), vec2f(1, 0), vec2f()); return vertexMain_Output_1(vec4f(pos[input.idx].x, pos[input.idx].y, 0f, 1f), uv[input.idx]); } @@ -475,14 +475,14 @@ describe('fluid double buffering example', () => { fn isInsideObstacle_6(x: i32, y: i32) -> bool { for (var obsIdx = 0; (obsIdx < 4i); obsIdx++) { - var obs = obstacles_7[obsIdx]; - if ((obs.enabled == 0u)) { + let obs = (&obstacles_7[obsIdx]); + if (((*obs).enabled == 0u)) { continue; } - var minX = max(0i, (obs.center.x - i32((f32(obs.size.x) / 2f)))); - var maxX = min(256i, (obs.center.x + i32((f32(obs.size.x) / 2f)))); - var minY = max(0i, (obs.center.y - i32((f32(obs.size.y) / 2f)))); - var maxY = min(256i, (obs.center.y + i32((f32(obs.size.y) / 2f)))); + let minX = max(0i, ((*obs).center.x - i32((f32((*obs).size.x) / 2f)))); + let maxX = min(256i, ((*obs).center.x + i32((f32((*obs).size.x) / 2f)))); + let minY = max(0i, ((*obs).center.y - i32((f32((*obs).size.y) / 2f)))); + let maxY = min(256i, ((*obs).center.y + i32((f32((*obs).size.y) / 2f)))); if (((((x >= minX) && (x <= maxX)) && (y >= minY)) && (y <= maxY))) { return true; } @@ -495,19 +495,19 @@ describe('fluid double buffering example', () => { } @fragment fn fragmentMain_3(input: fragmentMain_Input_9) -> @location(0) vec4f { - var x = i32((input.uv.x * 256f)); - var y = i32((input.uv.y * 256f)); - var index = coordsToIndex_4(x, y); - var cell = gridAlphaBuffer_5[index]; - var density = max(0f, cell.z); + let x = i32((input.uv.x * 256f)); + let y = i32((input.uv.y * 256f)); + let index = coordsToIndex_4(x, y); + let cell = (&gridAlphaBuffer_5[index]); + let density = max(0f, (*cell).z); var obstacleColor = vec4f(0.10000000149011612, 0.10000000149011612, 0.10000000149011612, 1); var background = vec4f(0.8999999761581421, 0.8999999761581421, 0.8999999761581421, 1); var firstColor = vec4f(0.20000000298023224, 0.6000000238418579, 1, 1); var secondColor = vec4f(0.20000000298023224, 0.30000001192092896, 0.6000000238418579, 1); var thirdColor = vec4f(0.10000000149011612, 0.20000000298023224, 0.4000000059604645, 1); - var firstThreshold = 2f; - var secondThreshold = 10f; - var thirdThreshold = 20f; + const firstThreshold = 2f; + const secondThreshold = 10f; + const thirdThreshold = 20f; if (isInsideObstacle_6(x, y)) { return obstacleColor; } @@ -515,7 +515,7 @@ describe('fluid double buffering example', () => { return background; } if ((density <= firstThreshold)) { - var t = (1f - pow((1f - (density / firstThreshold)), 2f)); + let t = (1f - pow((1f - (density / firstThreshold)), 2f)); return mix(background, firstColor, t); } if ((density <= secondThreshold)) { diff --git a/packages/typegpu/tests/examples/individual/fluid-with-atomics.test.ts b/packages/typegpu/tests/examples/individual/fluid-with-atomics.test.ts index eb32e0fe8e..e8830a5bba 100644 --- a/packages/typegpu/tests/examples/individual/fluid-with-atomics.test.ts +++ b/packages/typegpu/tests/examples/individual/fluid-with-atomics.test.ts @@ -17,94 +17,94 @@ describe('fluid with atomics example', () => { }, device); expect(shaderCodes).toMatchInlineSnapshot(` - "@group(0) @binding(0) var size_5: vec2u; + "@group(0) @binding(0) var size_6: vec2u; - fn getIndex_4(x: u32, y: u32) -> u32 { - var h = size_5.y; - var w = size_5.x; + fn getIndex_5(x: u32, y: u32) -> u32 { + let h = size_6.y; + let w = size_6.x; return (((y % h) * w) + (x % w)); } - @group(0) @binding(1) var nextState_6: array, 1048576>; + @group(0) @binding(1) var currentStateBuffer_7: array; - fn updateCell_3(x: u32, y: u32, value: u32) { - atomicStore(&nextState_6[getIndex_4(x, y)], value); + fn getCell_4(x: u32, y: u32) -> u32 { + return currentStateBuffer_7[getIndex_5(x, y)]; } - @group(0) @binding(2) var currentStateBuffer_9: array; - - fn getCell_8(x: u32, y: u32) -> u32 { - return currentStateBuffer_9[getIndex_4(x, y)]; + fn isClearCell_3(x: u32, y: u32) -> bool { + return ((getCell_4(x, y) >> 24u) == 4u); } - fn isClearCell_7(x: u32, y: u32) -> bool { - return ((getCell_8(x, y) >> 24u) == 4u); - } + @group(0) @binding(2) var nextState_9: array, 1048576>; - const MAX_WATER_LEVEL_11: u32 = 16777215u; + fn updateCell_8(x: u32, y: u32, value: u32) { + atomicStore(&nextState_9[getIndex_5(x, y)], value); + } - fn persistFlags_10(x: u32, y: u32) { - var cell = getCell_8(x, y); - var waterLevel = (cell & MAX_WATER_LEVEL_11); - var flags = (cell >> 24u); - updateCell_3(x, y, ((flags << 24u) | waterLevel)); + fn isWall_10(x: u32, y: u32) -> bool { + return ((getCell_4(x, y) >> 24u) == 1u); } - fn isWall_12(x: u32, y: u32) -> bool { - return ((getCell_8(x, y) >> 24u) == 1u); + const MAX_WATER_LEVEL_12: u32 = 16777215u; + + fn persistFlags_11(x: u32, y: u32) { + let cell = getCell_4(x, y); + let waterLevel = (cell & MAX_WATER_LEVEL_12); + let flags = (cell >> 24u); + updateCell_8(x, y, ((flags << 24u) | waterLevel)); } - fn getCellNext_14(x: u32, y: u32) -> u32 { - return atomicLoad(&nextState_6[getIndex_4(x, y)]); + fn isWaterSource_13(x: u32, y: u32) -> bool { + return ((getCell_4(x, y) >> 24u) == 2u); } - fn addToCell_13(x: u32, y: u32, value: u32) { - var cell = getCellNext_14(x, y); - var waterLevel = (cell & MAX_WATER_LEVEL_11); - var newWaterLevel = min((waterLevel + value), MAX_WATER_LEVEL_11); - atomicAdd(&nextState_6[getIndex_4(x, y)], (newWaterLevel - waterLevel)); + fn getCellNext_15(x: u32, y: u32) -> u32 { + return atomicLoad(&nextState_9[getIndex_5(x, y)]); } - fn isWaterSource_15(x: u32, y: u32) -> bool { - return ((getCell_8(x, y) >> 24u) == 2u); + fn addToCell_14(x: u32, y: u32, value: u32) { + let cell = getCellNext_15(x, y); + let waterLevel = (cell & MAX_WATER_LEVEL_12); + let newWaterLevel = min((waterLevel + value), MAX_WATER_LEVEL_12); + atomicAdd(&nextState_9[getIndex_5(x, y)], (newWaterLevel - waterLevel)); } fn isWaterDrain_16(x: u32, y: u32) -> bool { - return ((getCell_8(x, y) >> 24u) == 3u); + return ((getCell_4(x, y) >> 24u) == 3u); } - fn subtractFromCell_17(x: u32, y: u32, value: u32) { - var cell = getCellNext_14(x, y); - var waterLevel = (cell & MAX_WATER_LEVEL_11); - var newWaterLevel = max((waterLevel - min(value, waterLevel)), 0u); - atomicSub(&nextState_6[getIndex_4(x, y)], (waterLevel - newWaterLevel)); + fn getWaterLevel_17(x: u32, y: u32) -> u32 { + return (getCell_4(x, y) & MAX_WATER_LEVEL_12); } - fn getWaterLevel_18(x: u32, y: u32) -> u32 { - return (getCell_8(x, y) & MAX_WATER_LEVEL_11); + fn subtractFromCell_18(x: u32, y: u32, value: u32) { + let cell = getCellNext_15(x, y); + let waterLevel = (cell & MAX_WATER_LEVEL_12); + let newWaterLevel = max((waterLevel - min(value, waterLevel)), 0u); + atomicSub(&nextState_9[getIndex_5(x, y)], (waterLevel - newWaterLevel)); } fn checkForFlagsAndBounds_2(x: u32, y: u32) -> bool { - if (isClearCell_7(x, y)) { - updateCell_3(x, y, 0u); + if (isClearCell_3(x, y)) { + updateCell_8(x, y, 0u); return true; } - if (isWall_12(x, y)) { - persistFlags_10(x, y); + if (isWall_10(x, y)) { + persistFlags_11(x, y); return true; } - if (isWaterSource_15(x, y)) { - persistFlags_10(x, y); - addToCell_13(x, y, 20u); + if (isWaterSource_13(x, y)) { + persistFlags_11(x, y); + addToCell_14(x, y, 20u); return false; } if (isWaterDrain_16(x, y)) { - persistFlags_10(x, y); - updateCell_3(x, y, (3 << 24)); + persistFlags_11(x, y); + updateCell_8(x, y, (3 << 24)); return true; } - if (((((y == 0u) || (y == (size_5.y - 1u))) || (x == 0u)) || (x == (size_5.x - 1u)))) { - subtractFromCell_17(x, y, getWaterLevel_18(x, y)); + if (((((y == 0u) || (y == (size_6.y - 1u))) || (x == 0u)) || (x == (size_6.x - 1u)))) { + subtractFromCell_18(x, y, getWaterLevel_17(x, y)); return true; } return false; @@ -115,7 +115,7 @@ describe('fluid with atomics example', () => { const MAX_PRESSURE_21: u32 = 12u; fn getStableStateBelow_19(upper: u32, lower: u32) -> u32 { - var totalMass = (upper + lower); + let totalMass = (upper + lower); if ((totalMass <= MAX_WATER_LEVEL_UNPRESSURIZED_20)) { return totalMass; } @@ -131,57 +131,57 @@ describe('fluid with atomics example', () => { if (checkForFlagsAndBounds_2(x, y)) { return; } - var remainingWater = getWaterLevel_18(x, y); + var remainingWater = getWaterLevel_17(x, y); if ((remainingWater == 0u)) { return; } - if (!isWall_12(x, (y - 1u))) { - var waterLevelBelow = getWaterLevel_18(x, (y - 1u)); - var stable = getStableStateBelow_19(remainingWater, waterLevelBelow); + if (!isWall_10(x, (y - 1u))) { + let waterLevelBelow = getWaterLevel_17(x, (y - 1u)); + let stable = getStableStateBelow_19(remainingWater, waterLevelBelow); if ((waterLevelBelow < stable)) { - var change = (stable - waterLevelBelow); - var flow = min(change, viscosity_22); - subtractFromCell_17(x, y, flow); - addToCell_13(x, (y - 1u), flow); + let change = (stable - waterLevelBelow); + let flow = min(change, viscosity_22); + subtractFromCell_18(x, y, flow); + addToCell_14(x, (y - 1u), flow); remainingWater -= flow; } } if ((remainingWater == 0u)) { return; } - var waterLevelBefore = remainingWater; - if (!isWall_12((x - 1u), y)) { - var flowRaw = (i32(waterLevelBefore) - i32(getWaterLevel_18((x - 1u), y))); + let waterLevelBefore = remainingWater; + if (!isWall_10((x - 1u), y)) { + let flowRaw = (i32(waterLevelBefore) - i32(getWaterLevel_17((x - 1u), y))); if ((flowRaw > 0i)) { - var change = max(min(4u, remainingWater), u32((f32(flowRaw) / 4f))); - var flow = min(change, viscosity_22); - subtractFromCell_17(x, y, flow); - addToCell_13((x - 1u), y, flow); + let change = max(min(4u, remainingWater), u32((f32(flowRaw) / 4f))); + let flow = min(change, viscosity_22); + subtractFromCell_18(x, y, flow); + addToCell_14((x - 1u), y, flow); remainingWater -= flow; } } if ((remainingWater == 0u)) { return; } - if (!isWall_12((x + 1u), y)) { - var flowRaw = (i32(waterLevelBefore) - i32(getWaterLevel_18((x + 1u), y))); + if (!isWall_10((x + 1u), y)) { + let flowRaw = (i32(waterLevelBefore) - i32(getWaterLevel_17((x + 1u), y))); if ((flowRaw > 0i)) { - var change = max(min(4u, remainingWater), u32((f32(flowRaw) / 4f))); - var flow = min(change, viscosity_22); - subtractFromCell_17(x, y, flow); - addToCell_13((x + 1u), y, flow); + let change = max(min(4u, remainingWater), u32((f32(flowRaw) / 4f))); + let flow = min(change, viscosity_22); + subtractFromCell_18(x, y, flow); + addToCell_14((x + 1u), y, flow); remainingWater -= flow; } } if ((remainingWater == 0u)) { return; } - if (!isWall_12(x, (y + 1u))) { - var stable = getStableStateBelow_19(getWaterLevel_18(x, (y + 1u)), remainingWater); + if (!isWall_10(x, (y + 1u))) { + let stable = getStableStateBelow_19(getWaterLevel_17(x, (y + 1u)), remainingWater); if ((stable < remainingWater)) { - var flow = min((remainingWater - stable), viscosity_22); - subtractFromCell_17(x, y, flow); - addToCell_13(x, (y + 1u), flow); + let flow = min((remainingWater - stable), viscosity_22); + subtractFromCell_18(x, y, flow); + addToCell_14(x, (y + 1u), flow); remainingWater -= flow; } } @@ -209,23 +209,23 @@ describe('fluid with atomics example', () => { } @vertex fn vertex_0(input: vertex_Input_3) -> vertex_Output_2 { - var w = size_1.x; - var h = size_1.y; - var gridX = (input.idx % w); - var gridY = u32((f32(input.idx) / f32(w))); - var maxDim = max(w, h); - var x = (((2f * (f32(gridX) + input.squareData.x)) - f32(w)) / f32(maxDim)); - var y = (((2f * (f32(gridY) + input.squareData.y)) - f32(h)) / f32(maxDim)); - var cellFlags = (input.currentStateData >> 24u); + let w = size_1.x; + let h = size_1.y; + let gridX = (input.idx % w); + let gridY = u32((f32(input.idx) / f32(w))); + let maxDim = max(w, h); + let x = (((2f * (f32(gridX) + input.squareData.x)) - f32(w)) / f32(maxDim)); + let y = (((2f * (f32(gridY) + input.squareData.y)) - f32(h)) / f32(maxDim)); + let cellFlags = (input.currentStateData >> 24u); var cell = f32((input.currentStateData & 16777215u)); if ((cellFlags == 1u)) { - cell = -1; + cell = -1f; } if ((cellFlags == 2u)) { - cell = -2; + cell = -2f; } if ((cellFlags == 3u)) { - cell = -3; + cell = -3f; } return vertex_Output_2(vec4f(x, y, 0f, 1f), cell); } @@ -235,20 +235,20 @@ describe('fluid with atomics example', () => { } @fragment fn fragment_4(input: fragment_Input_5) -> @location(0) vec4f { - if ((input.cell == -1)) { + if ((input.cell == -1f)) { return vec4f(0.5, 0.5, 0.5, 1); } - if ((input.cell == -2)) { + if ((input.cell == -2f)) { return vec4f(0, 1, 0, 1); } - if ((input.cell == -3)) { + if ((input.cell == -3f)) { return vec4f(1, 0, 0, 1); } - var normalized = min((input.cell / 255f), 1f); + let normalized = min((input.cell / 255f), 1f); if ((normalized == 0f)) { return vec4f(); } - var res = (1f / (1f + exp((-(normalized - 0.2f) * 10f)))); + let res = (1f / (1f + exp((-((normalized - 0.2f)) * 10f)))); return vec4f(0f, 0f, res, res); }" `); diff --git a/packages/typegpu/tests/examples/individual/gravity.test.ts b/packages/typegpu/tests/examples/individual/gravity.test.ts index f68ca44de7..d6b46c02dc 100644 --- a/packages/typegpu/tests/examples/individual/gravity.test.ts +++ b/packages/typegpu/tests/examples/individual/gravity.test.ts @@ -47,10 +47,12 @@ describe('gravity example', () => { } fn isSmaller_5(currentId: u32, otherId: u32) -> bool { - if ((inState_1[currentId].mass < inState_1[otherId].mass)) { + let current = (&inState_1[currentId]); + let other = (&inState_1[otherId]); + if (((*current).mass < (*other).mass)) { return true; } - if ((inState_1[currentId].mass == inState_1[otherId].mass)) { + if (((*current).mass == (*other).mass)) { return (currentId < otherId); } return false; @@ -63,40 +65,49 @@ describe('gravity example', () => { } @compute @workgroup_size(1) fn computeCollisionsShader_0(input: computeCollisionsShader_Input_7) { - var currentId = input.gid.x; - var current = CelestialBody_2(inState_1[currentId].destroyed, inState_1[currentId].position, inState_1[currentId].velocity, inState_1[currentId].mass, inState_1[currentId].radiusMultiplier, inState_1[currentId].collisionBehavior, inState_1[currentId].textureIndex, inState_1[currentId].ambientLightFactor); - var updatedCurrent = current; + let currentId = input.gid.x; + var current = inState_1[currentId]; if ((current.destroyed == 0u)) { - for (var i = 0; (i < celestialBodiesCount_3); i++) { - var otherId = u32(i); - var other = CelestialBody_2(inState_1[otherId].destroyed, inState_1[otherId].position, inState_1[otherId].velocity, inState_1[otherId].mass, inState_1[otherId].radiusMultiplier, inState_1[otherId].collisionBehavior, inState_1[otherId].textureIndex, inState_1[otherId].ambientLightFactor); - if ((((((u32(i) == input.gid.x) || (other.destroyed == 1u)) || (current.collisionBehavior == 0u)) || (other.collisionBehavior == 0u)) || (distance(current.position, other.position) >= (radiusOf_4(current) + radiusOf_4(other))))) { + for (var otherId = 0u; (otherId < u32(celestialBodiesCount_3)); otherId++) { + let other = (&inState_1[otherId]); + if ((((((otherId == currentId) || ((*other).destroyed == 1u)) || (current.collisionBehavior == 0u)) || ((*other).collisionBehavior == 0u)) || (distance(current.position, (*other).position) >= (radiusOf_4(current) + radiusOf_4((*other)))))) { continue; } - if (((current.collisionBehavior == 1u) && (other.collisionBehavior == 1u))) { + if (((current.collisionBehavior == 1u) && ((*other).collisionBehavior == 1u))) { if (isSmaller_5(currentId, otherId)) { - updatedCurrent.position = (other.position + ((radiusOf_4(current) + radiusOf_4(other)) * normalize((current.position - other.position)))); + var dir = normalize((current.position - (*other).position)); + current.position = ((*other).position + (dir * (radiusOf_4(current) + radiusOf_4((*other))))); } - updatedCurrent.velocity = (0.99 * (updatedCurrent.velocity - (((((2f * other.mass) / (current.mass + other.mass)) * dot((current.velocity - other.velocity), (current.position - other.position))) / pow(distance(current.position, other.position), 2f)) * (current.position - other.position)))); + var posDiff = (current.position - (*other).position); + var velDiff = (current.velocity - (*other).velocity); + let posDiffFactor = ((((2f * (*other).mass) / (current.mass + (*other).mass)) * dot(velDiff, posDiff)) / dot(posDiff, posDiff)); + current.velocity = ((current.velocity - (posDiff * posDiffFactor)) * 0.99); } else { - var isCurrentAbsorbed = ((current.collisionBehavior == 1u) || ((current.collisionBehavior == 2u) && isSmaller_5(currentId, otherId))); + let isCurrentAbsorbed = ((current.collisionBehavior == 1u) || ((current.collisionBehavior == 2u) && isSmaller_5(currentId, otherId))); if (isCurrentAbsorbed) { - updatedCurrent.destroyed = 1u; + current.destroyed = 1u; } else { - var m1 = updatedCurrent.mass; - var m2 = other.mass; - updatedCurrent.velocity = (((m1 / (m1 + m2)) * updatedCurrent.velocity) + ((m2 / (m1 + m2)) * other.velocity)); - updatedCurrent.mass = (m1 + m2); + let m1 = current.mass; + let m2 = (*other).mass; + current.velocity = ((current.velocity * (m1 / (m1 + m2))) + ((*other).velocity * (m2 / (m1 + m2)))); + current.mass = (m1 + m2); } } } } - outState_6[input.gid.x] = updatedCurrent; + outState_6[currentId] = current; } - struct CelestialBody_2 { + struct Time_2 { + passed: f32, + multiplier: f32, + } + + @group(0) @binding(0) var time_1: Time_2; + + struct CelestialBody_4 { destroyed: u32, position: vec3f, velocity: vec3f, @@ -107,45 +118,38 @@ describe('gravity example', () => { ambientLightFactor: f32, } - @group(1) @binding(1) var inState_1: array; - - struct Time_4 { - passed: f32, - multiplier: f32, - } - - @group(0) @binding(0) var time_3: Time_4; + @group(1) @binding(1) var inState_3: array; @group(1) @binding(0) var celestialBodiesCount_5: i32; - fn radiusOf_6(body: CelestialBody_2) -> f32 { + fn radiusOf_6(body: CelestialBody_4) -> f32 { return (pow(((body.mass * 0.75f) / 3.141592653589793f), 0.333f) * body.radiusMultiplier); } - @group(1) @binding(2) var outState_7: array; + @group(1) @binding(2) var outState_7: array; struct computeGravityShader_Input_8 { @builtin(global_invocation_id) gid: vec3u, } @compute @workgroup_size(1) fn computeGravityShader_0(input: computeGravityShader_Input_8) { - var current = CelestialBody_2(inState_1[input.gid.x].destroyed, inState_1[input.gid.x].position, inState_1[input.gid.x].velocity, inState_1[input.gid.x].mass, inState_1[input.gid.x].radiusMultiplier, inState_1[input.gid.x].collisionBehavior, inState_1[input.gid.x].textureIndex, inState_1[input.gid.x].ambientLightFactor); - var dt = (time_3.passed * time_3.multiplier); - var updatedCurrent = current; + let dt = (time_1.passed * time_1.multiplier); + let currentId = input.gid.x; + var current = inState_3[currentId]; if ((current.destroyed == 0u)) { - for (var i = 0; (i < celestialBodiesCount_5); i++) { - var other = CelestialBody_2(inState_1[i].destroyed, inState_1[i].position, inState_1[i].velocity, inState_1[i].mass, inState_1[i].radiusMultiplier, inState_1[i].collisionBehavior, inState_1[i].textureIndex, inState_1[i].ambientLightFactor); - if (((u32(i) == input.gid.x) || (other.destroyed == 1u))) { + for (var otherId = 0u; (otherId < u32(celestialBodiesCount_5)); otherId++) { + let other = (&inState_3[otherId]); + if (((otherId == currentId) || ((*other).destroyed == 1u))) { continue; } - var dist = max((radiusOf_6(current) + radiusOf_6(other)), distance(current.position, other.position)); - var gravityForce = (((current.mass * other.mass) / dist) / dist); - var direction = normalize((other.position - current.position)); - updatedCurrent.velocity = (updatedCurrent.velocity + (((gravityForce / current.mass) * dt) * direction)); + let dist = max((radiusOf_6(current) + radiusOf_6((*other))), distance(current.position, (*other).position)); + let gravityForce = (((current.mass * (*other).mass) / dist) / dist); + var direction = normalize(((*other).position - current.position)); + current.velocity = (current.velocity + (direction * ((gravityForce / current.mass) * dt))); } - updatedCurrent.position = (updatedCurrent.position + (dt * updatedCurrent.velocity)); + current.position = (current.position + (current.velocity * dt)); } - outState_7[input.gid.x] = updatedCurrent; + outState_7[currentId] = current; } struct Camera_2 { @@ -228,11 +232,11 @@ describe('gravity example', () => { } @vertex fn mainVertex_0(input: mainVertex_Input_7) -> mainVertex_Output_6 { - var currentBody = CelestialBody_2(celestialBodies_1[input.instanceIndex].destroyed, celestialBodies_1[input.instanceIndex].position, celestialBodies_1[input.instanceIndex].velocity, celestialBodies_1[input.instanceIndex].mass, celestialBodies_1[input.instanceIndex].radiusMultiplier, celestialBodies_1[input.instanceIndex].collisionBehavior, celestialBodies_1[input.instanceIndex].textureIndex, celestialBodies_1[input.instanceIndex].ambientLightFactor); - var worldPosition = ((radiusOf_3(currentBody) * input.position.xyz) + currentBody.position); - var camera = camera_4; - var positionOnCanvas = (camera.projection * (camera.view * vec4f(worldPosition, 1f))); - return mainVertex_Output_6(positionOnCanvas, input.uv, input.normal, worldPosition, currentBody.textureIndex, currentBody.destroyed, currentBody.ambientLightFactor); + let currentBody = (&celestialBodies_1[input.instanceIndex]); + var worldPosition = ((*currentBody).position + (input.position.xyz * radiusOf_3((*currentBody)))); + let camera = (&camera_4); + var positionOnCanvas = (((*camera).projection * (*camera).view) * vec4f(worldPosition, 1f)); + return mainVertex_Output_6(positionOnCanvas, input.uv, input.normal, worldPosition, (*currentBody).textureIndex, (*currentBody).destroyed, (*currentBody).ambientLightFactor); } @group(1) @binding(0) var celestialBodyTextures_9: texture_2d_array; @@ -257,11 +261,11 @@ describe('gravity example', () => { } var lightColor = vec3f(1, 0.8999999761581421, 0.8999999761581421); var textureColor = textureSample(celestialBodyTextures_9, sampler_10, input.uv, input.sphereTextureIndex).xyz; - var ambient = (input.ambientLightFactor * (textureColor * lightColor)); - var normal = input.normals; + var ambient = ((textureColor * lightColor) * input.ambientLightFactor); + let normal = input.normals; var lightDirection = normalize((lightSource_11 - input.worldPosition)); - var cosTheta = dot(normal, lightDirection); - var diffuse = (max(0f, cosTheta) * (textureColor * lightColor)); + let cosTheta = dot(normal, lightDirection); + var diffuse = ((textureColor * lightColor) * max(0f, cosTheta)); var litColor = (ambient + diffuse); return vec4f(litColor.xyz, 1f); }" diff --git a/packages/typegpu/tests/examples/individual/jelly-slider.test.ts b/packages/typegpu/tests/examples/individual/jelly-slider.test.ts index a4fd60a826..bbd4ce31ce 100644 --- a/packages/typegpu/tests/examples/individual/jelly-slider.test.ts +++ b/packages/typegpu/tests/examples/individual/jelly-slider.test.ts @@ -98,44 +98,44 @@ describe('jelly-slider example', () => { } fn getRay_8(ndc: vec2f) -> Ray_11 { - var clipPos = vec4f(ndc.x, ndc.y, -1, 1f); - var invView = cameraUniform_9.viewInv; - var invProj = cameraUniform_9.projInv; - var viewPos = (invProj * clipPos); + var clipPos = vec4f(ndc.x, ndc.y, -1f, 1f); + let invView = (&cameraUniform_9.viewInv); + let invProj = (&cameraUniform_9.projInv); + var viewPos = ((*invProj) * clipPos); var viewPosNormalized = vec4f((viewPos.xyz / viewPos.w), 1f); - var worldPos = (invView * viewPosNormalized); - var rayOrigin = invView[3].xyz; + var worldPos = ((*invView) * viewPosNormalized); + var rayOrigin = (*invView)[3i].xyz; var rayDir = normalize((worldPos.xyz - rayOrigin)); return Ray_11(rayOrigin, rayDir); } - fn sdRoundedBox2d_15(p: vec2f, size: vec2f, cornerRadius: f32) -> f32 { + fn sdPlane_14(p: vec3f, n: vec3f, h: f32) -> f32 { + return (dot(p, n) + h); + } + + fn sdRoundedBox2d_16(p: vec2f, size: vec2f, cornerRadius: f32) -> f32 { var d = ((abs(p) - size) + vec2f(cornerRadius)); return ((length(max(d, vec2f())) + min(max(d.x, d.y), 0f)) - cornerRadius); } - fn rectangleCutoutDist_14(position: vec2f) -> f32 { - var groundRoundness = 0.02; - return sdRoundedBox2d_15(position, vec2f((1f + groundRoundness), (0.2f + groundRoundness)), (0.2f + groundRoundness)); + fn rectangleCutoutDist_15(position: vec2f) -> f32 { + const groundRoundness = 0.02; + return sdRoundedBox2d_16(position, vec2f((1f + groundRoundness), (0.2f + groundRoundness)), (0.2f + groundRoundness)); } - fn opExtrudeY_16(p: vec3f, dd: f32, h: f32) -> f32 { + fn opExtrudeY_17(p: vec3f, dd: f32, h: f32) -> f32 { var w = vec2f(dd, (abs(p.y) - h)); return (min(max(w.x, w.y), 0f) + length(max(w, vec2f()))); } - fn opUnion_17(d1: f32, d2: f32) -> f32 { + fn opUnion_18(d1: f32, d2: f32) -> f32 { return min(d1, d2); } - fn sdPlane_18(p: vec3f, n: vec3f, h: f32) -> f32 { - return (dot(p, n) + h); - } - fn getMainSceneDist_13(position: vec3f) -> f32 { - var groundThickness = 0.03; - var groundRoundness = 0.02; - return opUnion_17(sdPlane_18(position, vec3f(0, 1, 0), 0.06f), (opExtrudeY_16(position, -rectangleCutoutDist_14(position.xz), (groundThickness - groundRoundness)) - groundRoundness)); + const groundThickness = 0.03; + const groundRoundness = 0.02; + return opUnion_18(sdPlane_14(position, vec3f(0, 1, 0), 0.06f), (opExtrudeY_17(position, -(rectangleCutoutDist_15(position.xz)), (groundThickness - groundRoundness)) - groundRoundness)); } @group(0) @binding(2) var item_20: vec4f; @@ -145,15 +145,15 @@ describe('jelly-slider example', () => { @group(0) @binding(4) var filteringSampler_23: sampler; fn renderPercentageOnGround_21(hitPosition: vec3f, center: vec3f, percentage: u32) -> vec4f { - var textWidth = 0.38; - var textHeight = 0.33; + const textWidth = 0.38; + const textHeight = 0.33; if (((abs((hitPosition.x - center.x)) > (textWidth * 0.5f)) || (abs((hitPosition.z - center.z)) > (textHeight * 0.5f)))) { return vec4f(); } - var localX = (hitPosition.x - center.x); - var localZ = (hitPosition.z - center.z); - var uvX = ((localX + (textWidth * 0.5f)) / textWidth); - var uvZ = ((localZ + (textHeight * 0.5f)) / textHeight); + let localX = (hitPosition.x - center.x); + let localZ = (hitPosition.z - center.z); + let uvX = ((localX + (textWidth * 0.5f)) / textWidth); + let uvZ = ((localZ + (textHeight * 0.5f)) / textHeight); if (((((uvX < 0f) || (uvX > 1f)) || (uvZ < 0f)) || (uvZ > 1f))) { return vec4f(); } @@ -170,8 +170,8 @@ describe('jelly-slider example', () => { @group(0) @binding(6) var bezierTexture_26: texture_2d; fn item_28() -> f32 { - var a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); seed_7.x = fract((cos(a) * 136.8168f)); seed_7.y = fract((cos(b) * 534.7645f)); return seed_7.y; @@ -182,7 +182,7 @@ describe('jelly-slider example', () => { } fn getNormalFromSdf_30(position: vec3f, epsilon: f32) -> vec3f { - var k = vec3f(1f, -1, 0f); + var k = vec3f(1, -1, 0); var offset1 = (k.xyy * epsilon); var offset2 = (k.yyx * epsilon); var offset3 = (k.yxy * epsilon); @@ -209,26 +209,26 @@ describe('jelly-slider example', () => { } fn getFakeShadow_34(position: vec3f, lightDir: vec3f) -> vec3f { - var jellyColor = jellyColorUniform_31; - var endCapX = item_20.x; - if ((position.y < -0.03)) { - var fadeSharpness = 30; - var inset = 0.02; - var cutout = (rectangleCutoutDist_14(position.xz) + inset); - var edgeDarkening = saturate((1f - (cutout * f32(fadeSharpness)))); - var lightGradient = saturate((((-position.z * 4f) * lightDir.z) + 1f)); + let jellyColor = (&jellyColorUniform_31); + let endCapX = item_20.x; + if ((position.y < -0.03f)) { + const fadeSharpness = 30; + const inset = 0.02; + let cutout = (rectangleCutoutDist_15(position.xz) + inset); + let edgeDarkening = saturate((1f - (cutout * f32(fadeSharpness)))); + let lightGradient = saturate((((-(position.z) * 4f) * lightDir.z) + 1f)); return ((vec3f(1) * edgeDarkening) * (lightGradient * 0.5f)); } else { - var finalUV = vec2f((((position.x - ((position.z * lightDir.x) * sign(lightDir.z))) * 0.5f) + 0.5f), ((1f - ((-position.z / lightDir.z) * 0.5f)) - 0.2f)); + var finalUV = vec2f((((position.x - ((position.z * lightDir.x) * sign(lightDir.z))) * 0.5f) + 0.5f), ((1f - ((-(position.z) / lightDir.z) * 0.5f)) - 0.2f)); var data = textureSampleLevel(bezierTexture_26, filteringSampler_23, finalUV, 0); - var jellySaturation = mix(0, data.y, saturate(((position.x * 1.5f) + 1.1f))); - var shadowColor = mix(vec3f(), jellyColor.xyz, jellySaturation); - var contrast = ((20f * saturate(finalUV.y)) * (0.8f + (endCapX * 0.2f))); - var shadowOffset = -0.3; - var featherSharpness = 10; - var uvEdgeFeather = (((saturate((finalUV.x * f32(featherSharpness))) * saturate(((1f - finalUV.x) * f32(featherSharpness)))) * saturate(((1f - finalUV.y) * f32(featherSharpness)))) * saturate(finalUV.y)); - var influence = (saturate(((1f - lightDir.y) * 2f)) * uvEdgeFeather); + let jellySaturation = mix(0f, data.y, saturate(((position.x * 1.5f) + 1.1f))); + var shadowColor = mix(vec3f(), (*jellyColor).xyz, jellySaturation); + let contrast = ((20f * saturate(finalUV.y)) * (0.8f + (endCapX * 0.2f))); + const shadowOffset = -0.3; + const featherSharpness = 10; + let uvEdgeFeather = (((saturate((finalUV.x * f32(featherSharpness))) * saturate(((1f - finalUV.x) * f32(featherSharpness)))) * saturate(((1f - finalUV.y) * f32(featherSharpness)))) * saturate(finalUV.y)); + let influence = (saturate(((1f - lightDir.y) * 2f)) * uvEdgeFeather); return mix(vec3f(1), mix(shadowColor, vec3f(1), saturate(((data.x * contrast) + shadowOffset))), influence); } } @@ -236,10 +236,10 @@ describe('jelly-slider example', () => { fn calculateLighting_33(hitPosition: vec3f, normal: vec3f, rayOrigin: vec3f) -> vec3f { var lightDir = -(lightUniform_24.direction); var fakeShadow = getFakeShadow_34(hitPosition, lightDir); - var diffuse = max(dot(normal, lightDir), 0f); + let diffuse = max(dot(normal, lightDir), 0f); var viewDir = normalize((rayOrigin - hitPosition)); var reflectDir = reflect(-(lightDir), normal); - var specularFactor = pow(max(dot(viewDir, reflectDir), 0f), 10f); + let specularFactor = pow(max(dot(viewDir, reflectDir), 0f), 10f); var specular = (lightUniform_24.color * (specularFactor * 0.6f)); var baseColor = vec3f(0.8999999761581421); var directionalLight = (((baseColor * lightUniform_24.color) * diffuse) * fakeShadow); @@ -270,8 +270,8 @@ describe('jelly-slider example', () => { var uv = vec2f(((p.x - bbox.left) / (bbox.right - bbox.left)), ((bbox.top - p.y) / (bbox.top - bbox.bottom))); var clampedUV = saturate(uv); var sampledColor = textureSampleLevel(bezierTexture_26, filteringSampler_23, clampedUV, 0); - var segUnsigned = sampledColor.x; - var progress = sampledColor.y; + let segUnsigned = sampledColor.x; + let progress = sampledColor.y; var normal = sampledColor.zw; return LineInfo_42(progress, segUnsigned, normal); } @@ -288,37 +288,37 @@ describe('jelly-slider example', () => { return 1000000000; } var poly2D = sdInflatedPolyline2D_41(p); - var dist3D = (opExtrudeZ_43(position, poly2D.distance, 0.17f) - 0.024f); + let dist3D = (opExtrudeZ_43(position, poly2D.distance, 0.17f) - 0.024f); return dist3D; } fn getSceneDistForAO_37(position: vec3f) -> f32 { - var mainScene = getMainSceneDist_13(position); - var sliderApprox = sliderApproxDist_38(position); + let mainScene = getMainSceneDist_13(position); + let sliderApprox = sliderApproxDist_38(position); return min(mainScene, sliderApprox); } fn calculateAO_36(position: vec3f, normal: vec3f) -> f32 { var totalOcclusion = 0f; var sampleWeight = 1f; - var stepDistance = 0.03333333333333333; + const stepDistance = 0.03333333333333333; for (var i = 1; (i <= 3i); i++) { - var sampleHeight = (stepDistance * f32(i)); + let sampleHeight = (stepDistance * f32(i)); var samplePosition = (position + (normal * sampleHeight)); - var distanceToSurface = (getSceneDistForAO_37(samplePosition) - 5e-3f); - var occlusionContribution = max(0f, (sampleHeight - distanceToSurface)); + let distanceToSurface = (getSceneDistForAO_37(samplePosition) - 5e-3f); + let occlusionContribution = max(0f, (sampleHeight - distanceToSurface)); totalOcclusion += (occlusionContribution * sampleWeight); sampleWeight *= 0.5f; if ((totalOcclusion > 0.2f)) { break; } } - var rawAO = (1f - ((0.5f * totalOcclusion) / 0.1f)); + let rawAO = (1f - ((0.5f * totalOcclusion) / 0.1f)); return saturate(rawAO); } fn applyAO_35(litColor: vec3f, hitPosition: vec3f, normal: vec3f) -> vec4f { - var ao = calculateAO_36(hitPosition, normal); + let ao = calculateAO_36(hitPosition, normal); var finalColor = (litColor * ao); return vec4f(finalColor, 1f); } @@ -327,35 +327,35 @@ describe('jelly-slider example', () => { var hitPosition = (rayOrigin + (rayDirection * backgroundHitDist)); var percentageSample = renderPercentageOnGround_21(hitPosition, vec3f(0.7200000286102295, 0, 0), u32(((item_20.x + 0.43f) * 84f))); var highlights = 0f; - var highlightWidth = 1f; - var highlightHeight = 0.2; + const highlightWidth = 1f; + const highlightHeight = 0.2; var offsetX = 0f; var offsetZ = 0.05000000074505806f; - var lightDir = lightUniform_24.direction; - var causticScale = 0.2; - offsetX -= (lightDir.x * causticScale); - offsetZ += (lightDir.z * causticScale); - var endCapX = item_20.x; - var sliderStretch = ((endCapX + 1f) * 0.5f); + let lightDir = (&lightUniform_24.direction); + const causticScale = 0.2; + offsetX -= ((*lightDir).x * causticScale); + offsetZ += ((*lightDir).z * causticScale); + let endCapX = item_20.x; + let sliderStretch = ((endCapX + 1f) * 0.5f); if (((abs((hitPosition.x + offsetX)) < highlightWidth) && (abs((hitPosition.z + offsetZ)) < highlightHeight))) { - var uvX_orig = ((((hitPosition.x + offsetX) + (highlightWidth * 2f)) / highlightWidth) * 0.5f); - var uvZ_orig = ((((hitPosition.z + offsetZ) + (highlightHeight * 2f)) / highlightHeight) * 0.5f); + let uvX_orig = ((((hitPosition.x + offsetX) + (highlightWidth * 2f)) / highlightWidth) * 0.5f); + let uvZ_orig = ((((hitPosition.z + offsetZ) + (highlightHeight * 2f)) / highlightHeight) * 0.5f); var centeredUV = vec2f((uvX_orig - 0.5f), (uvZ_orig - 0.5f)); var finalUV = vec2f(centeredUV.x, (1f - (pow((abs((centeredUV.y - 0.5f)) * 2f), 2f) * 0.3f))); - var density = max(0f, ((textureSampleLevel(bezierTexture_26, filteringSampler_23, finalUV, 0).x - 0.25f) * 8f)); - var fadeX = smoothstep(0, -0.2, (hitPosition.x - endCapX)); - var fadeZ = (1f - pow((abs((centeredUV.y - 0.5f)) * 2f), 3f)); - var fadeStretch = saturate((1f - sliderStretch)); - var edgeFade = ((saturate(fadeX) * saturate(fadeZ)) * fadeStretch); - highlights = ((((pow(density, 3f) * edgeFade) * 3f) * (1f + lightDir.z)) / 1.5f); + let density = max(0f, ((textureSampleLevel(bezierTexture_26, filteringSampler_23, finalUV, 0).x - 0.25f) * 8f)); + let fadeX = smoothstep(0, -0.2, (hitPosition.x - endCapX)); + let fadeZ = (1f - pow((abs((centeredUV.y - 0.5f)) * 2f), 3f)); + let fadeStretch = saturate((1f - sliderStretch)); + let edgeFade = ((saturate(fadeX) * saturate(fadeZ)) * fadeStretch); + highlights = ((((pow(density, 3f) * edgeFade) * 3f) * (1f + (*lightDir).z)) / 1.5f); } - var originYBound = saturate((rayOrigin.y + 0.01f)); + let originYBound = saturate((rayOrigin.y + 0.01f)); var posOffset = (hitPosition + (vec3f(0, 1, 0) * ((offset * (originYBound / (1f + originYBound))) * (1f + (randFloat01_27() / 2f))))); var newNormal = getNormalMain_29(posOffset); - var jellyColor = jellyColorUniform_31; - var sqDist = sqLength_32((hitPosition - vec3f(endCapX, 0f, 0f))); - var bounceLight = (jellyColor.xyz * ((1f / ((sqDist * 15f) + 1f)) * 0.4f)); - var sideBounceLight = ((jellyColor.xyz * ((1f / ((sqDist * 40f) + 1f)) * 0.3f)) * abs(newNormal.z)); + let jellyColor = (&jellyColorUniform_31); + let sqDist = sqLength_32((hitPosition - vec3f(endCapX, 0f, 0f))); + var bounceLight = ((*jellyColor).xyz * ((1f / ((sqDist * 15f) + 1f)) * 0.4f)); + var sideBounceLight = (((*jellyColor).xyz * ((1f / ((sqDist * 40f) + 1f)) * 0.3f)) * abs(newNormal.z)); var litColor = calculateLighting_33(posOffset, newNormal, rayOrigin); var backgroundColor = ((applyAO_35((vec3f(1) * litColor), posOffset, newNormal) + vec4f(bounceLight, 0f)) + vec4f(sideBounceLight, 0f)); var textColor = saturate((backgroundColor.xyz * vec3f(0.5))); @@ -374,8 +374,8 @@ describe('jelly-slider example', () => { var t2 = ((boxMax - rayOrigin) * invDir); var tMinVec = min(t1, t2); var tMaxVec = max(t1, t2); - var tMin = max(max(tMinVec.x, tMinVec.y), tMinVec.z); - var tMax = min(min(tMaxVec.x, tMaxVec.y), tMaxVec.z); + let tMin = max(max(tMinVec.x, tMinVec.y), tMinVec.z); + let tMax = min(min(tMaxVec.x, tMaxVec.y), tMaxVec.z); var result = BoxIntersection_45(); result.hit = ((tMax >= tMin) && (tMax >= 0f)); result.tMin = tMin; @@ -386,21 +386,21 @@ describe('jelly-slider example', () => { fn sdPie_49(p: vec2f, c: vec2f, r: f32) -> f32 { var p_w = p; p_w.x = abs(p.x); - var l = (length(p_w) - r); - var m = length((p_w - (c * clamp(dot(p_w, c), 0f, r)))); + let l = (length(p_w) - r); + let m = length((p_w - (c * clamp(dot(p_w, c), 0f, r)))); return max(l, (m * sign(((c.y * p_w.x) - (c.x * p_w.y))))); } fn cap3D_48(position: vec3f) -> f32 { - var endCap = item_20; - var secondLastPoint = vec2f(endCap.x, endCap.y); - var lastPoint = vec2f(endCap.z, endCap.w); - var angle = atan2((lastPoint.y - secondLastPoint.y), (lastPoint.x - secondLastPoint.x)); - var rot = mat2x2f(cos(angle), -sin(angle), sin(angle), cos(angle)); + let endCap = (&item_20); + var secondLastPoint = vec2f((*endCap).x, (*endCap).y); + var lastPoint = vec2f((*endCap).z, (*endCap).w); + let angle = atan2((lastPoint.y - secondLastPoint.y), (lastPoint.x - secondLastPoint.x)); + var rot = mat2x2f(cos(angle), -(sin(angle)), sin(angle), cos(angle)); var pieP = (position - vec3f(secondLastPoint, 0f)); pieP = vec3f((rot * pieP.xy), pieP.z); - var hmm = sdPie_49(pieP.zx, vec2f(1, 0), 0.17f); - var extrudeEnd = (opExtrudeY_16(pieP, hmm, 1e-3f) - 0.024f); + let hmm = sdPie_49(pieP.zx, vec2f(1, 0), 0.17f); + let extrudeEnd = (opExtrudeY_17(pieP, hmm, 1e-3f) - 0.024f); return extrudeEnd; } @@ -411,7 +411,7 @@ describe('jelly-slider example', () => { finalDist = cap3D_48(position); } else { - var body = (opExtrudeZ_43(position, poly2D.distance, 0.17f) - 0.024f); + let body = (opExtrudeZ_43(position, poly2D.distance, 0.17f) - 0.024f); finalDist = body; } return LineInfo_42(poly2D.t, finalDist, poly2D.normal); @@ -424,7 +424,7 @@ describe('jelly-slider example', () => { } fn getSceneDist_46(position: vec3f) -> HitInfo_50 { - var mainScene = getMainSceneDist_13(position); + let mainScene = getMainSceneDist_13(position); var poly3D = sliderSdf3D_47(position); var hitInfo = HitInfo_50(); if ((poly3D.distance < mainScene)) { @@ -440,7 +440,7 @@ describe('jelly-slider example', () => { } fn getNormalFromSdf_54(position: vec3f, epsilon: f32) -> vec3f { - var k = vec3f(1f, -1, 0f); + var k = vec3f(1, -1, 0); var offset1 = (k.xyy * epsilon); var offset2 = (k.yyx * epsilon); var offset3 = (k.yxy * epsilon); @@ -459,22 +459,22 @@ describe('jelly-slider example', () => { fn getSliderNormal_52(position: vec3f, hitInfo: HitInfo_50) -> vec3f { var poly2D = sdInflatedPolyline2D_41(position.xy); - var gradient2D = poly2D.normal; - var threshold = 0.14450000000000002; - var absZ = abs(position.z); - var zDistance = max(0f, (((absZ - threshold) * 0.17f) / (0.17f - threshold))); - var edgeDistance = (0.024f - poly2D.distance); - var edgeContrib = 0.9; - var zContrib = (1f - edgeContrib); - var zDirection = sign(position.z); + let gradient2D = (&poly2D.normal); + const threshold = 0.14450000000000002; + let absZ = abs(position.z); + let zDistance = max(0f, (((absZ - threshold) * 0.17f) / (0.17f - threshold))); + let edgeDistance = (0.024f - poly2D.distance); + const edgeContrib = 0.9; + let zContrib = (1f - edgeContrib); + let zDirection = sign(position.z); var zAxisVector = vec3f(0f, 0f, zDirection); - var edgeBlendDistance = ((edgeContrib * 0.024f) + (zContrib * 0.17f)); - var blendFactor = smoothstep(edgeBlendDistance, 0, ((zDistance * zContrib) + (edgeDistance * edgeContrib))); - var normal2D = vec3f(gradient2D.xy, 0f); + let edgeBlendDistance = ((edgeContrib * 0.024f) + (zContrib * 0.17f)); + let blendFactor = smoothstep(edgeBlendDistance, 0, ((zDistance * zContrib) + (edgeDistance * edgeContrib))); + var normal2D = vec3f((*gradient2D).xy, 0f); var blendedNormal = mix(zAxisVector, normal2D, ((blendFactor * 0.5f) + 0.5f)); var normal = normalize(blendedNormal); if ((hitInfo.t > 0.94f)) { - var ratio = ((hitInfo.t - 0.94f) / 0.02f); + let ratio = ((hitInfo.t - 0.94f) / 0.02f); var fullNormal = getNormalCap_53(position); normal = normalize(mix(normal, fullNormal, ratio)); } @@ -489,7 +489,7 @@ describe('jelly-slider example', () => { } fn fresnelSchlick_55(cosTheta: f32, ior1: f32, ior2: f32) -> f32 { - var r0 = pow(((ior1 - ior2) / (ior1 + ior2)), 2f); + let r0 = pow(((ior1 - ior2) / (ior1 + ior2)), 2f); return (r0 + ((1f - r0) * pow((1f - cosTheta), 5f))); } @@ -513,7 +513,7 @@ describe('jelly-slider example', () => { } fn beerLambert_58(sigma: vec3f, dist: f32) -> vec3f { - return exp((sigma * -dist)); + return exp((sigma * -(dist))); } fn rayMarch_12(rayOrigin: vec3f, rayDirection: vec3f, uv: vec2f) -> vec4f { @@ -521,7 +521,7 @@ describe('jelly-slider example', () => { var backgroundDist = 0f; for (var i = 0; (i < 64i); i++) { var p = (rayOrigin + (rayDirection * backgroundDist)); - var hit = getMainSceneDist_13(p); + let hit = getMainSceneDist_13(p); backgroundDist += hit; if ((hit < 1e-3f)) { break; @@ -529,8 +529,8 @@ describe('jelly-slider example', () => { } var background = renderBackground_19(rayOrigin, rayDirection, backgroundDist, 0f); var bbox = getSliderBbox_39(); - var zDepth = 0.25f; - var sliderMin = vec3f(bbox.left, bbox.bottom, -zDepth); + const zDepth = 0.25f; + var sliderMin = vec3f(bbox.left, bbox.bottom, -(zDepth)); var sliderMax = vec3f(bbox.right, bbox.top, zDepth); var intersection = intersectBox_44(rayOrigin, rayDirection, sliderMin, sliderMax); if (!intersection.hit) { @@ -551,26 +551,26 @@ describe('jelly-slider example', () => { break; } var N = getNormal_51(hitPosition, hitInfo); - var I = rayDirection; - var cosi = min(1f, max(0f, dot(-(I), N))); - var F = fresnelSchlick_55(cosi, 1f, 1.4199999570846558f); + let I = rayDirection; + let cosi = min(1f, max(0f, dot(-(I), N))); + let F = fresnelSchlick_55(cosi, 1f, 1.4199999570846558f); var reflection = saturate(vec3f((hitPosition.y + 0.2f))); - var eta = 0.7042253521126761; - var k = (1f - ((eta * eta) * (1f - (cosi * cosi)))); + const eta = 0.7042253521126761; + let k = (1f - ((eta * eta) * (1f - (cosi * cosi)))); var refractedColor = vec3f(); if ((k > 0f)) { var refrDir = normalize(((I * eta) + (N * ((eta * cosi) - sqrt(k))))); var p = (hitPosition + (refrDir * 2e-3)); var exitPos = (p + (refrDir * 2e-3)); var env = rayMarchNoJelly_56(exitPos, refrDir); - var progress = hitInfo.t; - var jellyColor = jellyColorUniform_31; - var scatterTint = (jellyColor.xyz * 1.5); - var density = 20f; - var absorb = ((vec3f(1) - jellyColor.xyz) * density); - var T = beerLambert_58((absorb * pow(progress, 2f)), 0.08); + let progress = hitInfo.t; + let jellyColor = (&jellyColorUniform_31); + var scatterTint = ((*jellyColor).xyz * 1.5); + const density = 20f; + var absorb = ((vec3f(1) - (*jellyColor).xyz) * density); + var T = beerLambert_58((absorb * pow(progress, 2f)), 0.08f); var lightDir = -(lightUniform_24.direction); - var forward = max(0f, dot(lightDir, refrDir)); + let forward = max(0f, dot(lightDir, refrDir)); var scatter = (scatterTint * ((3f * forward) * pow(progress, 3f))); refractedColor = ((env * T) + scatter); } @@ -590,7 +590,7 @@ describe('jelly-slider example', () => { @fragment fn raymarchFn_3(_arg_0: raymarchFn_Input_59) -> @location(0) vec4f { randSeed2_5((randomUniform_4 * _arg_0.uv)); - var ndc = vec2f(((_arg_0.uv.x * 2f) - 1f), -((_arg_0.uv.y * 2f) - 1f)); + var ndc = vec2f(((_arg_0.uv.x * 2f) - 1f), -(((_arg_0.uv.y * 2f) - 1f))); var ray = getRay_8(ndc); var color = rayMarch_12(ray.origin, ray.direction, _arg_0.uv); return vec4f(tanh((color.xyz * 1.3)), 1f); @@ -607,8 +607,8 @@ describe('jelly-slider example', () => { } @compute @workgroup_size(16, 16) fn taaResolveFn_0(_arg_0: taaResolveFn_Input_4) { - var currentColor = textureLoad(currentTexture_1, vec2u(_arg_0.gid.xy), 0); - var historyColor = textureLoad(historyTexture_2, vec2u(_arg_0.gid.xy), 0); + var currentColor = textureLoad(currentTexture_1, _arg_0.gid.xy, 0); + var historyColor = textureLoad(historyTexture_2, _arg_0.gid.xy, 0); var minColor = vec3f(9999); var maxColor = vec3f(-9999); var dimensions = textureDimensions(currentTexture_1); @@ -623,17 +623,17 @@ describe('jelly-slider example', () => { } var historyColorClamped = clamp(historyColor.xyz, minColor, maxColor); var uv = (vec2f(_arg_0.gid.xy) / vec2f(dimensions.xy)); - var textRegionMinX = 0.7099999785423279f; - var textRegionMaxX = 0.8500000238418579f; - var textRegionMinY = 0.4699999988079071f; - var textRegionMaxY = 0.550000011920929f; - var borderSize = 0.019999999552965164f; - var fadeInX = smoothstep((textRegionMinX - borderSize), (textRegionMinX + borderSize), uv.x); - var fadeOutX = (1f - smoothstep((textRegionMaxX - borderSize), (textRegionMaxX + borderSize), uv.x)); - var fadeInY = smoothstep((textRegionMinY - borderSize), (textRegionMinY + borderSize), uv.y); - var fadeOutY = (1f - smoothstep((textRegionMaxY - borderSize), (textRegionMaxY + borderSize), uv.y)); - var inTextRegion = (((fadeInX * fadeOutX) * fadeInY) * fadeOutY); - var blendFactor = mix(0.8999999761581421f, 0.699999988079071f, inTextRegion); + const textRegionMinX = 0.7099999785423279f; + const textRegionMaxX = 0.8500000238418579f; + const textRegionMinY = 0.4699999988079071f; + const textRegionMaxY = 0.550000011920929f; + const borderSize = 0.019999999552965164f; + let fadeInX = smoothstep((textRegionMinX - borderSize), (textRegionMinX + borderSize), uv.x); + let fadeOutX = (1f - smoothstep((textRegionMaxX - borderSize), (textRegionMaxX + borderSize), uv.x)); + let fadeInY = smoothstep((textRegionMinY - borderSize), (textRegionMinY + borderSize), uv.y); + let fadeOutY = (1f - smoothstep((textRegionMaxY - borderSize), (textRegionMaxY + borderSize), uv.y)); + let inTextRegion = (((fadeInX * fadeOutX) * fadeInY) * fadeOutY); + let blendFactor = mix(0.8999999761581421f, 0.699999988079071f, inTextRegion); var resolvedColor = vec4f(mix(currentColor.xyz, historyColorClamped, blendFactor), 1f); textureStore(outputTexture_3, vec2u(_arg_0.gid.x, _arg_0.gid.y), resolvedColor); } @@ -683,29 +683,29 @@ describe('jelly-slider example', () => { var b = ((A - (B * 2)) + C); var c = (a * 2f); var d = (A - pos); - var dotB = max(dot(b, b), 1e-4f); - var kk = (1f / dotB); - var kx = (kk * dot(a, b)); - var ky = ((kk * ((2f * dot(a, a)) + dot(d, b))) / 3f); - var kz = (kk * dot(d, a)); + let dotB = max(dot(b, b), 1e-4f); + let kk = (1f / dotB); + let kx = (kk * dot(a, b)); + let ky = ((kk * ((2f * dot(a, a)) + dot(d, b))) / 3f); + let kz = (kk * dot(d, a)); var res = 0f; - var p = (ky - (kx * kx)); - var p3 = ((p * p) * p); - var q = ((kx * (((2f * kx) * kx) - (3f * ky))) + kz); + let p = (ky - (kx * kx)); + let p3 = ((p * p) * p); + let q = ((kx * (((2f * kx) * kx) - (3f * ky))) + kz); var h = ((q * q) + (4f * p3)); if ((h >= 0f)) { h = sqrt(h); - var x = ((vec2f(h, -h) - q) * 0.5); + var x = ((vec2f(h, -(h)) - q) * 0.5); var uv = (sign(x) * pow(abs(x), vec2f(0.3333333432674408))); - var t = clamp(((uv.x + uv.y) - kx), 0f, 1f); + let t = clamp(((uv.x + uv.y) - kx), 0f, 1f); res = dot2_7((d + ((c + (b * t)) * t))); } else { - var z = sqrt(-p); - var v = (acos((q / ((p * z) * 2f))) / 3f); - var m = cos(v); - var n = (sin(v) * 1.732050808f); - var t = saturate(((vec3f((m + m), (-n - m), (n - m)) * z) - kx)); + let z = sqrt(-(p)); + let v = (acos((q / ((p * z) * 2f))) / 3f); + let m = cos(v); + let n = (sin(v) * 1.732050808f); + var t = saturate(((vec3f((m + m), (-(n) - m), (n - m)) * z) - kx)); res = min(dot2_7((d + ((c + (b * t.x)) * t.x))), dot2_7((d + ((c + (b * t.y)) * t.y)))); } return sqrt(res); @@ -718,24 +718,24 @@ describe('jelly-slider example', () => { var minDist = 1e+10f; var closestSegment = 0i; var closestT = 0f; - var epsilon = 0.029999999329447746f; + const epsilon = 0.029999999329447746f; var xOffset = vec2f(epsilon, 0f); var yOffset2 = vec2f(0f, epsilon); var xPlusDist = 1e+10f; var xMinusDist = 1e+10f; var yPlusDist = 1e+10f; var yMinusDist = 1e+10f; - for (var i = 0; (i < (17 - 1)); i++) { - var A = pointsView_4[i]; - var B = pointsView_4[(i + 1i)]; - var C = controlPointsView_5[i]; - var dist = sdBezier_6(sliderPos, A, C, B); + for (var i = 0; (i < 16i); i++) { + let A = (&pointsView_4[i]); + let B = (&pointsView_4[(i + 1i)]); + let C = (&controlPointsView_5[i]); + let dist = sdBezier_6(sliderPos, (*A), (*C), (*B)); if ((dist < minDist)) { minDist = dist; closestSegment = i; - var AB = (B - A); - var AP = (sliderPos - A); - var ABLength = length(AB); + var AB = ((*B) - (*A)); + var AP = (sliderPos - (*A)); + let ABLength = length(AB); if ((ABLength > 0f)) { closestT = clamp((dot(AP, AB) / (ABLength * ABLength)), 0f, 1f); } @@ -743,14 +743,14 @@ describe('jelly-slider example', () => { closestT = 0f; } } - xPlusDist = min(xPlusDist, sdBezier_6((sliderPos + xOffset), A, C, B)); - xMinusDist = min(xMinusDist, sdBezier_6((sliderPos - xOffset), A, C, B)); - yPlusDist = min(yPlusDist, sdBezier_6((sliderPos + yOffset2), A, C, B)); - yMinusDist = min(yMinusDist, sdBezier_6((sliderPos - yOffset2), A, C, B)); + xPlusDist = min(xPlusDist, sdBezier_6((sliderPos + xOffset), (*A), (*C), (*B))); + xMinusDist = min(xMinusDist, sdBezier_6((sliderPos - xOffset), (*A), (*C), (*B))); + yPlusDist = min(yPlusDist, sdBezier_6((sliderPos + yOffset2), (*A), (*C), (*B))); + yMinusDist = min(yMinusDist, sdBezier_6((sliderPos - yOffset2), (*A), (*C), (*B))); } - var overallProgress = ((f32(closestSegment) + closestT) / f32((17 - 1))); - var normalX = ((xPlusDist - xMinusDist) / (2f * epsilon)); - var normalY = ((yPlusDist - yMinusDist) / (2f * epsilon)); + let overallProgress = ((f32(closestSegment) + closestT) / 16f); + let normalX = ((xPlusDist - xMinusDist) / (2f * epsilon)); + let normalY = ((yPlusDist - yMinusDist) / (2f * epsilon)); textureStore(bezierWriteView_3, vec2u(x, y), vec4f(minDist, overallProgress, normalX, normalY)); } diff --git a/packages/typegpu/tests/examples/individual/liquid-glass.test.ts b/packages/typegpu/tests/examples/individual/liquid-glass.test.ts index d1b8978c0e..ba64bb6354 100644 --- a/packages/typegpu/tests/examples/individual/liquid-glass.test.ts +++ b/packages/typegpu/tests/examples/individual/liquid-glass.test.ts @@ -98,9 +98,9 @@ describe('liquid-glass example', () => { } fn calculateWeights_9(sdfDist: f32, start: f32, end: f32, featherUV: f32) -> Weights_10 { - var inside = (1f - smoothstep((start - featherUV), (start + featherUV), sdfDist)); - var outside = smoothstep((end - featherUV), (end + featherUV), sdfDist); - var ring = max(0f, ((1f - inside) - outside)); + let inside = (1f - smoothstep((start - featherUV), (start + featherUV), sdfDist)); + let outside = smoothstep((end - featherUV), (end + featherUV), sdfDist); + let ring = max(0f, ((1f - inside) - outside)); return Weights_10(inside, ring, outside); } @@ -112,7 +112,7 @@ describe('liquid-glass example', () => { var channelOffset = (dir * ((f32(i) - 1f) * offset)); samples[i] = textureSampleBias(tex, sampler2, (uv - channelOffset), blur).xyz; } - return vec3f(samples[0].x, samples[1].y, samples[2].z); + return vec3f(samples[0i].x, samples[1i].y, samples[2i].z); } struct TintParams_13 { @@ -130,11 +130,11 @@ describe('liquid-glass example', () => { @fragment fn fragmentShader_3(_arg_0: fragmentShader_Input_15) -> @location(0) vec4f { var posInBoxSpace = (_arg_0.uv - mousePosUniform_4); - var sdfDist = sdRoundedBox2d_7(posInBoxSpace, paramsUniform_5.rectDims, paramsUniform_5.radius); + let sdfDist = sdRoundedBox2d_7(posInBoxSpace, paramsUniform_5.rectDims, paramsUniform_5.radius); var dir = normalize((posInBoxSpace * paramsUniform_5.rectDims.yx)); - var normalizedDist = ((sdfDist - paramsUniform_5.start) / (paramsUniform_5.end - paramsUniform_5.start)); + let normalizedDist = ((sdfDist - paramsUniform_5.start) / (paramsUniform_5.end - paramsUniform_5.start)); var texDim = textureDimensions(sampledView_8, 0); - var featherUV = (paramsUniform_5.edgeFeather / f32(max(texDim.x, texDim.y))); + let featherUV = (paramsUniform_5.edgeFeather / f32(max(texDim.x, texDim.y))); var weights = calculateWeights_9(sdfDist, paramsUniform_5.start, paramsUniform_5.end, featherUV); var blurSample = textureSampleBias(sampledView_8, sampler_11, _arg_0.uv, paramsUniform_5.blur); var refractedSample = sampleWithChromaticAberration_12(sampledView_8, sampler_11, (_arg_0.uv + (dir * (paramsUniform_5.refractionStrength * normalizedDist))), (paramsUniform_5.chromaticStrength * normalizedDist), dir, (paramsUniform_5.blur * paramsUniform_5.edgeBlurMultiplier)); diff --git a/packages/typegpu/tests/examples/individual/log-test.test.ts b/packages/typegpu/tests/examples/individual/log-test.test.ts index 7f05f9522a..7f1e839ba3 100644 --- a/packages/typegpu/tests/examples/individual/log-test.test.ts +++ b/packages/typegpu/tests/examples/individual/log-test.test.ts @@ -798,14 +798,14 @@ describe('console log example', () => { fn wrappedCallback_2(_arg_0: u32, _arg_1: u32, _arg_2: u32) { log1_3(); log2_10(3.140000104904175f); - log3_14(i32(-2000000000)); + log3_14(-2000000000i); log4_17(3000000000u); log5_20(true); log6_23(); log7_25(); - log8_27(vec2f(1.1f, -2.2)); - log9_30(vec3f(10.1f, -20.2, 30.3f)); - log10_33(vec4f(100.1f, -200.2, 300.3f, -400.4)); + log8_27(vec2f(1.100000023841858, -2.200000047683716)); + log9_30(vec3f(10.100000381469727, -20.200000762939453, 30.299999237060547)); + log10_33(vec4f(100.0999984741211, -200.1999969482422, 300.29998779296875, -400.3999938964844)); log11_36(); log12_38(vec2i(-1, -2)); log13_41(vec3i(-1, -2, -3)); @@ -1494,7 +1494,7 @@ describe('console log example', () => { } @vertex fn mainVertex_0(input: mainVertex_Input_2) -> mainVertex_Output_1 { - var positions = array(vec2f(0, 0.5), vec2f(-0.5, -0.5), vec2f(0.5f, -0.5)); + var positions = array(vec2f(0, 0.5), vec2f(-0.5), vec2f(0.5, -0.5)); return mainVertex_Output_1(vec4f(positions[input.vertexIndex], 0f, 1f)); } @@ -1555,7 +1555,7 @@ describe('console log example', () => { } @vertex fn mainVertex_0(input: mainVertex_Input_2) -> mainVertex_Output_1 { - var positions = array(vec2f(0, 0.5), vec2f(-0.5, -0.5), vec2f(0.5f, -0.5)); + var positions = array(vec2f(0, 0.5), vec2f(-0.5), vec2f(0.5, -0.5)); return mainVertex_Output_1(vec4f(positions[input.vertexIndex], 0f, 1f)); } diff --git a/packages/typegpu/tests/examples/individual/matrix-next.test.ts b/packages/typegpu/tests/examples/individual/matrix-next.test.ts index 3a67d985df..4ac22eaf50 100644 --- a/packages/typegpu/tests/examples/individual/matrix-next.test.ts +++ b/packages/typegpu/tests/examples/individual/matrix-next.test.ts @@ -51,40 +51,40 @@ describe('matrix(next) example', () => { } @compute @workgroup_size(16, 16) fn computeSharedMemory_0(input: computeSharedMemory_Input_10) { - var dimensions = dimensions_1; - var numTiles = u32((f32(((dimensions.firstColumnCount + 16u) - 1u)) / 16f)); - var globalRow = ((input.wid.x * 16u) + input.lid.x); - var globalCol = ((input.wid.y * 16u) + input.lid.y); - var localRow = input.lid.x; - var localCol = input.lid.y; - var tileIdx = getTileIndex_3(localRow, localCol); + let dimensions = (&dimensions_1); + let numTiles = u32((f32((((*dimensions).firstColumnCount + 16u) - 1u)) / 16f)); + let globalRow = ((input.wid.x * 16u) + input.lid.x); + let globalCol = ((input.wid.y * 16u) + input.lid.y); + let localRow = input.lid.x; + let localCol = input.lid.y; + let tileIdx = getTileIndex_3(localRow, localCol); var accumulatedResult = 0; for (var tileIndex = 0u; (tileIndex < numTiles); tileIndex++) { - var matrixACol = ((tileIndex * 16u) + localCol); + let matrixACol = ((tileIndex * 16u) + localCol); var valueA = 0; - if (((globalRow < dimensions.firstRowCount) && (matrixACol < dimensions.firstColumnCount))) { - var indexA = getIndex_4(globalRow, matrixACol, dimensions.firstColumnCount); + if (((globalRow < (*dimensions).firstRowCount) && (matrixACol < (*dimensions).firstColumnCount))) { + let indexA = getIndex_4(globalRow, matrixACol, (*dimensions).firstColumnCount); valueA = firstMatrix_5[indexA]; } tileA_6[tileIdx] = valueA; - var matrixBRow = ((tileIndex * 16u) + localRow); + let matrixBRow = ((tileIndex * 16u) + localRow); var valueB = 0; - if (((matrixBRow < dimensions.firstColumnCount) && (globalCol < dimensions.secondColumnCount))) { - var indexB = getIndex_4(matrixBRow, globalCol, dimensions.secondColumnCount); + if (((matrixBRow < (*dimensions).firstColumnCount) && (globalCol < (*dimensions).secondColumnCount))) { + let indexB = getIndex_4(matrixBRow, globalCol, (*dimensions).secondColumnCount); valueB = secondMatrix_7[indexB]; } tileB_8[tileIdx] = valueB; workgroupBarrier(); - var effectiveTileSize = min(16u, (dimensions.firstColumnCount - (tileIndex * 16u))); + let effectiveTileSize = min(16u, ((*dimensions).firstColumnCount - (tileIndex * 16u))); for (var k = 0u; (k < effectiveTileSize); k++) { - var tileA_element = tileA_6[getTileIndex_3(localRow, k)]; - var tileB_element = tileB_8[getTileIndex_3(k, localCol)]; + let tileA_element = tileA_6[getTileIndex_3(localRow, k)]; + let tileB_element = tileB_8[getTileIndex_3(k, localCol)]; accumulatedResult += (tileA_element * tileB_element); } workgroupBarrier(); } - if (((globalRow < dimensions.firstRowCount) && (globalCol < dimensions.secondColumnCount))) { - var outputIndex = getIndex_4(globalRow, globalCol, dimensions.secondColumnCount); + if (((globalRow < (*dimensions).firstRowCount) && (globalCol < (*dimensions).secondColumnCount))) { + let outputIndex = getIndex_4(globalRow, globalCol, (*dimensions).secondColumnCount); resultMatrix_9[outputIndex] = accumulatedResult; } }" diff --git a/packages/typegpu/tests/examples/individual/mnist-inference.test.ts b/packages/typegpu/tests/examples/individual/mnist-inference.test.ts index 24b0238d04..39b63c3164 100644 --- a/packages/typegpu/tests/examples/individual/mnist-inference.test.ts +++ b/packages/typegpu/tests/examples/individual/mnist-inference.test.ts @@ -39,14 +39,14 @@ describe('mnist inference example', () => { } @compute @workgroup_size(1) fn defaultCompute_0(_arg_0: defaultCompute_Input_6) { - var inputSize = arrayLength(&input_1); - var i = _arg_0.gid.x; - var weightsOffset = (i * inputSize); + let inputSize = arrayLength(&input_1); + let i = _arg_0.gid.x; + let weightsOffset = (i * inputSize); var sum = 0f; for (var j = 0u; (j < inputSize); j++) { sum = fma(input_1[j], weights_2[(weightsOffset + j)], sum); } - var total = (sum + biases_3[i]); + let total = (sum + biases_3[i]); output_4[i] = relu_5(total); } @@ -74,20 +74,20 @@ describe('mnist inference example', () => { } @compute @workgroup_size(128) fn subgroupCompute_0(_arg_0: subgroupCompute_Input_7) { - var subgroupId = u32((f32(_arg_0.lid.x) / f32(_arg_0.ssize))); - var outputsPerWG = u32((f32(workgroupSize_1) / f32(_arg_0.ssize))); - var neuronIndex = ((_arg_0.wid.x * outputsPerWG) + subgroupId); - var outLen = arrayLength(&output_2); - var valid = (neuronIndex < outLen); - var inputSize = arrayLength(&input_3); + let subgroupId = u32((f32(_arg_0.lid.x) / f32(_arg_0.ssize))); + let outputsPerWG = u32((f32(workgroupSize_1) / f32(_arg_0.ssize))); + let neuronIndex = ((_arg_0.wid.x * outputsPerWG) + subgroupId); + let outLen = arrayLength(&output_2); + let valid = (neuronIndex < outLen); + let inputSize = arrayLength(&input_3); var partial = 0f; if (valid) { - var weightsOffset = (neuronIndex * inputSize); + let weightsOffset = (neuronIndex * inputSize); for (var j = _arg_0.sid; (j < inputSize); j += _arg_0.ssize) { partial = fma(input_3[j], weights_4[(weightsOffset + j)], partial); } } - var sum = subgroupAdd(partial); + let sum = subgroupAdd(partial); if ((valid && (_arg_0.sid == 0u))) { output_2[neuronIndex] = relu_6((sum + biases_5[neuronIndex])); } diff --git a/packages/typegpu/tests/examples/individual/oklab.test.ts b/packages/typegpu/tests/examples/individual/oklab.test.ts index e560b67e7e..c7d740bc08 100644 --- a/packages/typegpu/tests/examples/individual/oklab.test.ts +++ b/packages/typegpu/tests/examples/individual/oklab.test.ts @@ -27,7 +27,7 @@ describe('oklab example', () => { } @vertex fn fullScreenTriangle_0(input: fullScreenTriangle_Input_2) -> fullScreenTriangle_Output_1 { - var pos = array(vec2f(-1, -1), vec2f(3f, -1), vec2f(-1, 3f)); + var pos = array(vec2f(-1), vec2f(3, -1), vec2f(-1, 3)); return fullScreenTriangle_Output_1(vec4f(pos[input.vertexIndex], 0f, 1f), pos[input.vertexIndex]); } @@ -43,13 +43,13 @@ describe('oklab example', () => { } fn oklabToLinearRgb_7(lab: vec3f) -> vec3f { - var l_ = ((lab.x + (0.3963377774f * lab.y)) + (0.2158037573f * lab.z)); - var m_ = ((lab.x - (0.1055613458f * lab.y)) - (0.0638541728f * lab.z)); - var s_ = ((lab.x - (0.0894841775f * lab.y)) - (1.291485548f * lab.z)); - var l = ((l_ * l_) * l_); - var m = ((m_ * m_) * m_); - var s = ((s_ * s_) * s_); - return vec3f((((4.0767416621f * l) - (3.3077115913f * m)) + (0.2309699292f * s)), (((-1.2684380046 * l) + (2.6097574011f * m)) - (0.3413193965f * s)), (((-0.0041960863 * l) - (0.7034186147f * m)) + (1.707614701f * s))); + let l_ = ((lab.x + (0.3963377774f * lab.y)) + (0.2158037573f * lab.z)); + let m_ = ((lab.x - (0.1055613458f * lab.y)) - (0.0638541728f * lab.z)); + let s_ = ((lab.x - (0.0894841775f * lab.y)) - (1.291485548f * lab.z)); + let l = ((l_ * l_) * l_); + let m = ((m_ * m_) * m_); + let s = ((s_ * s_) * s_); + return vec3f((((4.0767416621f * l) - (3.3077115913f * m)) + (0.2309699292f * s)), (((-1.2684380046f * l) + (2.6097574011f * m)) - (0.3413193965f * s)), (((-0.0041960863f * l) - (0.7034186147f * m)) + (1.707614701f * s))); } fn computeMaxSaturation_10(a: f32, b: f32) -> f32 { @@ -61,58 +61,58 @@ describe('oklab example', () => { var wl = 0f; var wm = 0f; var ws = 0f; - if ((((-1.88170328 * a) - (0.80936493f * b)) > 1f)) { + if ((((-1.88170328f * a) - (0.80936493f * b)) > 1f)) { k0 = 1.19086277f; k1 = 1.76576728f; k2 = 0.59662641f; k3 = 0.75515197f; k4 = 0.56771245f; wl = 4.0767416621f; - wm = -3.3077115913; + wm = -3.3077115913f; ws = 0.2309699292f; } else { if ((((1.81444104f * a) - (1.19445276f * b)) > 1f)) { k0 = 0.73956515f; - k1 = -0.45954404; + k1 = -0.45954404f; k2 = 0.08285427f; k3 = 0.1254107f; k4 = 0.14503204f; - wl = -1.2684380046; + wl = -1.2684380046f; wm = 2.6097574011f; - ws = -0.3413193965; + ws = -0.3413193965f; } else { k0 = 1.35733652f; - k1 = -0.00915799; - k2 = -1.1513021; - k3 = -0.50559606; + k1 = -0.00915799f; + k2 = -1.1513021f; + k3 = -0.50559606f; k4 = 0.00692167f; - wl = -0.0041960863; - wm = -0.7034186147; + wl = -0.0041960863f; + wm = -0.7034186147f; ws = 1.707614701f; } } - var k_l = ((0.3963377774f * a) + (0.2158037573f * b)); - var k_m = ((-0.1055613458 * a) - (0.0638541728f * b)); - var k_s = ((-0.0894841775 * a) - (1.291485548f * b)); + let k_l = ((0.3963377774f * a) + (0.2158037573f * b)); + let k_m = ((-0.1055613458f * a) - (0.0638541728f * b)); + let k_s = ((-0.0894841775f * a) - (1.291485548f * b)); var S = ((((k0 + (k1 * a)) + (k2 * b)) + ((k3 * a) * a)) + ((k4 * a) * b)); { - var l_ = (1f + (S * k_l)); - var m_ = (1f + (S * k_m)); - var s_ = (1f + (S * k_s)); - var l = ((l_ * l_) * l_); - var m = ((m_ * m_) * m_); - var s = ((s_ * s_) * s_); - var l_dS = (((3f * k_l) * l_) * l_); - var m_dS = (((3f * k_m) * m_) * m_); - var s_dS = (((3f * k_s) * s_) * s_); - var l_dS2 = (((6f * k_l) * k_l) * l_); - var m_dS2 = (((6f * k_m) * k_m) * m_); - var s_dS2 = (((6f * k_s) * k_s) * s_); - var f = (((wl * l) + (wm * m)) + (ws * s)); - var f1 = (((wl * l_dS) + (wm * m_dS)) + (ws * s_dS)); - var f2 = (((wl * l_dS2) + (wm * m_dS2)) + (ws * s_dS2)); + let l_ = (1f + (S * k_l)); + let m_ = (1f + (S * k_m)); + let s_ = (1f + (S * k_s)); + let l = ((l_ * l_) * l_); + let m = ((m_ * m_) * m_); + let s = ((s_ * s_) * s_); + let l_dS = (((3f * k_l) * l_) * l_); + let m_dS = (((3f * k_m) * m_) * m_); + let s_dS = (((3f * k_s) * s_) * s_); + let l_dS2 = (((6f * k_l) * k_l) * l_); + let m_dS2 = (((6f * k_m) * k_m) * m_); + let s_dS2 = (((6f * k_s) * k_s) * s_); + let f = (((wl * l) + (wm * m)) + (ws * s)); + let f1 = (((wl * l_dS) + (wm * m_dS)) + (ws * s_dS)); + let f2 = (((wl * l_dS2) + (wm * m_dS2)) + (ws * s_dS2)); S = (S - ((f * f1) / ((f1 * f1) - ((0.5f * f) * f2)))); } return S; @@ -128,15 +128,15 @@ describe('oklab example', () => { } fn findCusp_9(a: f32, b: f32) -> LC_12 { - var S_cusp = computeMaxSaturation_10(a, b); + let S_cusp = computeMaxSaturation_10(a, b); var rgb_at_max = oklabToLinearRgb_7(vec3f(1f, (S_cusp * a), (S_cusp * b))); - var L_cusp = cbrt_11((1f / max(max(rgb_at_max.x, rgb_at_max.y), rgb_at_max.z))); - var C_cusp = (L_cusp * S_cusp); + let L_cusp = cbrt_11((1f / max(max(rgb_at_max.x, rgb_at_max.y), rgb_at_max.z))); + let C_cusp = (L_cusp * S_cusp); return LC_12(L_cusp, C_cusp); } fn findGamutIntersection_13(a: f32, b: f32, L1: f32, C1: f32, L0: f32, cusp: LC_12) -> f32 { - var FLT_MAX = 3.40282346e+38; + const FLT_MAX = 3.40282346e+38; var t = 0f; if (((((L1 - L0) * cusp.C) - ((cusp.L - L0) * C1)) <= 0f)) { t = ((cusp.C * L0) / ((C1 * cusp.L) + (cusp.C * (L0 - L1)))); @@ -144,44 +144,44 @@ describe('oklab example', () => { else { t = ((cusp.C * (L0 - 1f)) / ((C1 * (cusp.L - 1f)) + (cusp.C * (L0 - L1)))); { - var dL = (L1 - L0); - var dC = C1; - var k_l = ((0.3963377774f * a) + (0.2158037573f * b)); - var k_m = ((-0.1055613458 * a) - (0.0638541728f * b)); - var k_s = ((-0.0894841775 * a) - (1.291485548f * b)); - var l_dt = (dL + (dC * k_l)); - var m_dt = (dL + (dC * k_m)); - var s_dt = (dL + (dC * k_s)); + let dL = (L1 - L0); + let dC = C1; + let k_l = ((0.3963377774f * a) + (0.2158037573f * b)); + let k_m = ((-0.1055613458f * a) - (0.0638541728f * b)); + let k_s = ((-0.0894841775f * a) - (1.291485548f * b)); + let l_dt = (dL + (dC * k_l)); + let m_dt = (dL + (dC * k_m)); + let s_dt = (dL + (dC * k_s)); { - var L = ((L0 * (1f - t)) + (t * L1)); - var C = (t * C1); - var l_ = (L + (C * k_l)); - var m_ = (L + (C * k_m)); - var s_ = (L + (C * k_s)); - var l = ((l_ * l_) * l_); - var m = ((m_ * m_) * m_); - var s = ((s_ * s_) * s_); - var ldt = (((3f * l_dt) * l_) * l_); - var mdt = (((3f * m_dt) * m_) * m_); - var sdt = (((3f * s_dt) * s_) * s_); - var ldt2 = (((6f * l_dt) * l_dt) * l_); - var mdt2 = (((6f * m_dt) * m_dt) * m_); - var sdt2 = (((6f * s_dt) * s_dt) * s_); - var r = ((((4.0767416621f * l) - (3.3077115913f * m)) + (0.2309699292f * s)) - 1f); - var r1 = (((4.0767416621f * ldt) - (3.3077115913f * mdt)) + (0.2309699292f * sdt)); - var r2 = (((4.0767416621f * ldt2) - (3.3077115913f * mdt2)) + (0.2309699292f * sdt2)); - var u_r = (r1 / ((r1 * r1) - ((0.5f * r) * r2))); - var t_r = (-r * u_r); - var g = ((((-1.2684380046 * l) + (2.6097574011f * m)) - (0.3413193965f * s)) - 1f); - var g1 = (((-1.2684380046 * ldt) + (2.6097574011f * mdt)) - (0.3413193965f * sdt)); - var g2 = (((-1.2684380046 * ldt2) + (2.6097574011f * mdt2)) - (0.3413193965f * sdt2)); - var u_g = (g1 / ((g1 * g1) - ((0.5f * g) * g2))); - var t_g = (-g * u_g); - var b2 = ((((-0.0041960863 * l) - (0.7034186147f * m)) + (1.707614701f * s)) - 1f); - var b1 = (((-0.0041960863 * ldt) - (0.7034186147f * mdt)) + (1.707614701f * sdt)); - var b22 = (((-0.0041960863 * ldt2) - (0.7034186147f * mdt2)) + (1.707614701f * sdt2)); - var u_b = (b1 / ((b1 * b1) - ((0.5f * b2) * b22))); - var t_b = (-b2 * u_b); + let L = ((L0 * (1f - t)) + (t * L1)); + let C = (t * C1); + let l_ = (L + (C * k_l)); + let m_ = (L + (C * k_m)); + let s_ = (L + (C * k_s)); + let l = ((l_ * l_) * l_); + let m = ((m_ * m_) * m_); + let s = ((s_ * s_) * s_); + let ldt = (((3f * l_dt) * l_) * l_); + let mdt = (((3f * m_dt) * m_) * m_); + let sdt = (((3f * s_dt) * s_) * s_); + let ldt2 = (((6f * l_dt) * l_dt) * l_); + let mdt2 = (((6f * m_dt) * m_dt) * m_); + let sdt2 = (((6f * s_dt) * s_dt) * s_); + let r = ((((4.0767416621f * l) - (3.3077115913f * m)) + (0.2309699292f * s)) - 1f); + let r1 = (((4.0767416621f * ldt) - (3.3077115913f * mdt)) + (0.2309699292f * sdt)); + let r2 = (((4.0767416621f * ldt2) - (3.3077115913f * mdt2)) + (0.2309699292f * sdt2)); + let u_r = (r1 / ((r1 * r1) - ((0.5f * r) * r2))); + var t_r = (-(r) * u_r); + let g = ((((-1.2684380046f * l) + (2.6097574011f * m)) - (0.3413193965f * s)) - 1f); + let g1 = (((-1.2684380046f * ldt) + (2.6097574011f * mdt)) - (0.3413193965f * sdt)); + let g2 = (((-1.2684380046f * ldt2) + (2.6097574011f * mdt2)) - (0.3413193965f * sdt2)); + let u_g = (g1 / ((g1 * g1) - ((0.5f * g) * g2))); + var t_g = (-(g) * u_g); + let b2 = ((((-0.0041960863f * l) - (0.7034186147f * m)) + (1.707614701f * s)) - 1f); + let b1 = (((-0.0041960863f * ldt) - (0.7034186147f * mdt)) + (1.707614701f * sdt)); + let b22 = (((-0.0041960863f * ldt2) - (0.7034186147f * mdt2)) + (1.707614701f * sdt2)); + let u_b = (b1 / ((b1 * b1) - ((0.5f * b2) * b22))); + var t_b = (-(b2) * u_b); t_r = select(FLT_MAX, t_r, (u_r >= 0f)); t_g = select(FLT_MAX, t_g, (u_g >= 0f)); t_b = select(FLT_MAX, t_b, (u_b >= 0f)); @@ -193,19 +193,19 @@ describe('oklab example', () => { } fn gamutClipAdaptiveL05_8(lab: vec3f) -> vec3f { - var alpha = 0.2f; - var L = lab.x; - var eps = 1e-5; - var C = max(eps, length(lab.yz)); - var a_ = (lab.y / C); - var b_ = (lab.z / C); - var Ld = (L - 0.5f); - var e1 = ((0.5f + abs(Ld)) + (alpha * C)); - var L0 = (0.5f * (1f + (sign(Ld) * (e1 - sqrt(max(0f, ((e1 * e1) - (2f * abs(Ld))))))))); + const alpha = 0.20000000298023224f; + let L = lab.x; + const eps = 1e-5; + let C = max(eps, length(lab.yz)); + let a_ = (lab.y / C); + let b_ = (lab.z / C); + let Ld = (L - 0.5f); + let e1 = ((0.5f + abs(Ld)) + (alpha * C)); + let L0 = (0.5f * (1f + (sign(Ld) * (e1 - sqrt(max(0f, ((e1 * e1) - (2f * abs(Ld))))))))); var cusp = findCusp_9(a_, b_); - var t = clamp(findGamutIntersection_13(a_, b_, L, C, L0, cusp), 0f, 1f); - var L_clipped = mix(L0, L, t); - var C_clipped = (t * C); + let t = clamp(findGamutIntersection_13(a_, b_, L, C, L0, cusp), 0f, 1f); + let L_clipped = mix(L0, L, t); + let C_clipped = (t * C); return vec3f(L_clipped, (C_clipped * a_), (C_clipped * b_)); } @@ -226,14 +226,14 @@ describe('oklab example', () => { } @fragment fn mainFragment_3(input: mainFragment_Input_17) -> @location(0) vec4f { - var hue = uniforms_4.hue; + let hue = uniforms_4.hue; var pos = scaleView_6(input.uv); var lab = vec3f(pos.y, (pos.x * vec2f(cos(hue), sin(hue)))); var rgb = oklabToLinearRgb_7(lab); - var outOfGamut = (any((rgb < vec3f())) || any((rgb > vec3f(1)))); + let outOfGamut = (any((rgb < vec3f())) || any((rgb > vec3f(1)))); var clipLab = gamutClipAdaptiveL05_8(lab); var color = oklabToRgb_14(lab); - var patternScaled = ((item_16(input.uv, clipLab) * 0.1f) + 0.9f); + let patternScaled = ((item_16(input.uv, clipLab) * 0.1f) + 0.9f); return vec4f(select(color, (patternScaled * color), outOfGamut), 1f); }" `); diff --git a/packages/typegpu/tests/examples/individual/perlin-noise.test.ts b/packages/typegpu/tests/examples/individual/perlin-noise.test.ts index 7eff6c1760..aa8e7c1730 100644 --- a/packages/typegpu/tests/examples/individual/perlin-noise.test.ts +++ b/packages/typegpu/tests/examples/individual/perlin-noise.test.ts @@ -34,19 +34,19 @@ describe('perlin noise example', () => { } fn item_10() -> f32 { - var a = dot(seed_8, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_8, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_8, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_8, vec2f(54.47856521606445, 345.8415222167969)); seed_8.x = fract((cos(a) * 136.8168f)); seed_8.y = fract((cos(b) * 534.7645f)); return seed_8.y; } fn randOnUnitSphere_9() -> vec3f { - var z = ((2f * item_10()) - 1f); - var oneMinusZSq = sqrt((1f - (z * z))); - var theta = (6.283185307179586f * item_10()); - var x = (cos(theta) * oneMinusZSq); - var y = (sin(theta) * oneMinusZSq); + let z = ((2f * item_10()) - 1f); + let oneMinusZSq = sqrt((1f - (z * z))); + let theta = (6.283185307179586f * item_10()); + let x = (cos(theta) * oneMinusZSq); + let y = (sin(theta) * oneMinusZSq); return vec3f(x, y, z); } @@ -56,8 +56,8 @@ describe('perlin noise example', () => { } fn mainCompute_2(x: u32, y: u32, z: u32) { - var size = size_3; - var idx = ((x + (y * size.x)) + ((z * size.x) * size.y)); + let size = (&size_3); + let idx = ((x + (y * (*size).x)) + ((z * (*size).x) * (*size).y)); memory_4[idx] = computeJunctionGradient_5(vec3i(i32(x), i32(y), i32(z))); } @@ -98,9 +98,9 @@ describe('perlin noise example', () => { fn getJunctionGradient_8(pos: vec3i) -> vec3f { var size = vec3i(perlin3dCache__size_9.xyz); - var x = (((pos.x % size.x) + size.x) % size.x); - var y = (((pos.y % size.y) + size.y) % size.y); - var z = (((pos.z % size.z) + size.z) % size.z); + let x = (((pos.x % size.x) + size.x) % size.x); + let y = (((pos.y % size.y) + size.y) % size.y); + let z = (((pos.z % size.z) + size.z) % size.z); return perlin3dCache__memory_10[((x + (y * size.x)) + ((z * size.x) * size.y))]; } @@ -116,22 +116,22 @@ describe('perlin noise example', () => { fn sample_6(pos: vec3f) -> f32 { var minJunction = floor(pos); - var xyz = dotProdGrid_7(pos, minJunction); - var xyZ = dotProdGrid_7(pos, (minJunction + vec3f(0, 0, 1))); - var xYz = dotProdGrid_7(pos, (minJunction + vec3f(0, 1, 0))); - var xYZ = dotProdGrid_7(pos, (minJunction + vec3f(0, 1, 1))); - var Xyz = dotProdGrid_7(pos, (minJunction + vec3f(1, 0, 0))); - var XyZ = dotProdGrid_7(pos, (minJunction + vec3f(1, 0, 1))); - var XYz = dotProdGrid_7(pos, (minJunction + vec3f(1, 1, 0))); - var XYZ = dotProdGrid_7(pos, (minJunction + vec3f(1))); + let xyz = dotProdGrid_7(pos, minJunction); + let xyZ = dotProdGrid_7(pos, (minJunction + vec3f(0, 0, 1))); + let xYz = dotProdGrid_7(pos, (minJunction + vec3f(0, 1, 0))); + let xYZ = dotProdGrid_7(pos, (minJunction + vec3f(0, 1, 1))); + let Xyz = dotProdGrid_7(pos, (minJunction + vec3f(1, 0, 0))); + let XyZ = dotProdGrid_7(pos, (minJunction + vec3f(1, 0, 1))); + let XYz = dotProdGrid_7(pos, (minJunction + vec3f(1, 1, 0))); + let XYZ = dotProdGrid_7(pos, (minJunction + vec3f(1))); var partial = (pos - minJunction); var smoothPartial = quinticInterpolationImpl_11(partial); - var xy = mix(xyz, xyZ, smoothPartial.z); - var xY = mix(xYz, xYZ, smoothPartial.z); - var Xy = mix(Xyz, XyZ, smoothPartial.z); - var XY = mix(XYz, XYZ, smoothPartial.z); - var x = mix(xy, xY, smoothPartial.y); - var X = mix(Xy, XY, smoothPartial.y); + let xy = mix(xyz, xyZ, smoothPartial.z); + let xY = mix(xYz, xYZ, smoothPartial.z); + let Xy = mix(Xyz, XyZ, smoothPartial.z); + let XY = mix(XYz, XYZ, smoothPartial.z); + let x = mix(xy, xY, smoothPartial.y); + let X = mix(Xy, XY, smoothPartial.y); return mix(x, X, smoothPartial.x); } @@ -147,9 +147,9 @@ describe('perlin noise example', () => { @fragment fn mainFragment_3(input: mainFragment_Input_14) -> @location(0) vec4f { var uv = (gridSize_4 * input.uv); - var n = sample_6(vec3f(uv, time_5)); - var sharp = exponentialSharpen_12(n, sharpness_13); - var n01 = ((sharp * 0.5f) + 0.5f); + let n = sample_6(vec3f(uv, time_5)); + let sharp = exponentialSharpen_12(n, sharpness_13); + let n01 = ((sharp * 0.5f) + 0.5f); var dark = vec3f(0, 0.20000000298023224, 1); var light = vec3f(1, 0.30000001192092896, 0.5); return vec4f(mix(dark, light, n01), 1f); diff --git a/packages/typegpu/tests/examples/individual/probability.test.ts b/packages/typegpu/tests/examples/individual/probability.test.ts index d7bb5d811c..a263541313 100644 --- a/packages/typegpu/tests/examples/individual/probability.test.ts +++ b/packages/typegpu/tests/examples/individual/probability.test.ts @@ -33,8 +33,8 @@ describe('probability distribution plot example', () => { } fn item_7() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; @@ -45,13 +45,13 @@ describe('probability distribution plot example', () => { } fn randNormal_8(mu: f32, sigma: f32) -> f32 { - var theta = (6.283185307179586f * randUniformExclusive_9()); - var R = sqrt((-2 * log(randUniformExclusive_9()))); + let theta = (6.283185307179586f * randUniformExclusive_9()); + let R = sqrt((-2f * log(randUniformExclusive_9()))); return (((R * sin(theta)) * sigma) + mu); } fn randInUnitSphere_6() -> vec3f { - var u = item_7(); + let u = item_7(); var v = vec3f(randNormal_8(0f, 1f), randNormal_8(0f, 1f), randNormal_8(0f, 1f)); var vNorm = normalize(v); return (vNorm * pow(u, 0.33f)); @@ -62,7 +62,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_10) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -85,19 +85,19 @@ describe('probability distribution plot example', () => { } fn item_7() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; } fn randOnUnitSphere_6() -> vec3f { - var z = ((2f * item_7()) - 1f); - var oneMinusZSq = sqrt((1f - (z * z))); - var theta = (6.283185307179586f * item_7()); - var x = (cos(theta) * oneMinusZSq); - var y = (sin(theta) * oneMinusZSq); + let z = ((2f * item_7()) - 1f); + let oneMinusZSq = sqrt((1f - (z * z))); + let theta = (6.283185307179586f * item_7()); + let x = (cos(theta) * oneMinusZSq); + let y = (sin(theta) * oneMinusZSq); return vec3f(x, y, z); } @@ -106,7 +106,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_8) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -129,16 +129,16 @@ describe('probability distribution plot example', () => { } fn item_8() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; } fn randInUnitCircle_7() -> vec2f { - var radius = sqrt(item_8()); - var angle = (item_8() * 6.283185307179586f); + let radius = sqrt(item_8()); + let angle = (item_8() * 6.283185307179586f); return vec2f((cos(angle) * radius), (sin(angle) * radius)); } @@ -151,7 +151,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_9) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -174,15 +174,15 @@ describe('probability distribution plot example', () => { } fn item_8() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; } fn randOnUnitCircle_7() -> vec2f { - var angle = (item_8() * 6.283185307179586f); + let angle = (item_8() * 6.283185307179586f); return vec2f(cos(angle), sin(angle)); } @@ -195,7 +195,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_9) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -218,8 +218,8 @@ describe('probability distribution plot example', () => { } fn item_7() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; @@ -234,7 +234,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_8) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -257,16 +257,16 @@ describe('probability distribution plot example', () => { } fn item_7() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; } fn randOnUnitCube_6() -> vec3f { - var face = u32((item_7() * 6f)); - var axis = (face % 3u); + let face = u32((item_7() * 6f)); + let axis = (face % 3u); var result = vec3f(); result[axis] = f32(select(0, 1, (face > 2u))); result[((axis + 1u) % 3u)] = item_7(); @@ -279,7 +279,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_8) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -302,8 +302,8 @@ describe('probability distribution plot example', () => { } fn item_9() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; @@ -314,13 +314,13 @@ describe('probability distribution plot example', () => { } fn randNormal_10(mu: f32, sigma: f32) -> f32 { - var theta = (6.283185307179586f * randUniformExclusive_11()); - var R = sqrt((-2 * log(randUniformExclusive_11()))); + let theta = (6.283185307179586f * randUniformExclusive_11()); + let R = sqrt((-2f * log(randUniformExclusive_11()))); return (((R * sin(theta)) * sigma) + mu); } fn randInUnitSphere_8() -> vec3f { - var u = item_9(); + let u = item_9(); var v = vec3f(randNormal_10(0f, 1f), randNormal_10(0f, 1f), randNormal_10(0f, 1f)); var vNorm = normalize(v); return (vNorm * pow(u, 0.33f)); @@ -328,7 +328,7 @@ describe('probability distribution plot example', () => { fn randInUnitHemisphere_7(normal: vec3f) -> vec3f { var value = randInUnitSphere_8(); - var alignment = dot(normal, value); + let alignment = dot(normal, value); return (sign(alignment) * value); } @@ -341,7 +341,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_12) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -364,25 +364,25 @@ describe('probability distribution plot example', () => { } fn item_9() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; } fn randOnUnitSphere_8() -> vec3f { - var z = ((2f * item_9()) - 1f); - var oneMinusZSq = sqrt((1f - (z * z))); - var theta = (6.283185307179586f * item_9()); - var x = (cos(theta) * oneMinusZSq); - var y = (sin(theta) * oneMinusZSq); + let z = ((2f * item_9()) - 1f); + let oneMinusZSq = sqrt((1f - (z * z))); + let theta = (6.283185307179586f * item_9()); + let x = (cos(theta) * oneMinusZSq); + let y = (sin(theta) * oneMinusZSq); return vec3f(x, y, z); } fn randOnUnitHemisphere_7(normal: vec3f) -> vec3f { var value = randOnUnitSphere_8(); - var alignment = dot(normal, value); + let alignment = dot(normal, value); return (sign(alignment) * value); } @@ -395,7 +395,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_10) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -418,15 +418,15 @@ describe('probability distribution plot example', () => { } fn item_8() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; } fn randBernoulli_7(p: f32) -> f32 { - var u = item_8(); + let u = item_8(); return step(u, p); } @@ -439,7 +439,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_9) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -462,8 +462,8 @@ describe('probability distribution plot example', () => { } fn item_8() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; @@ -482,7 +482,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_9) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -505,8 +505,8 @@ describe('probability distribution plot example', () => { } fn item_9() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; @@ -517,8 +517,8 @@ describe('probability distribution plot example', () => { } fn randExponential_7(rate: f32) -> f32 { - var u = randUniformExclusive_8(); - return ((-1 / rate) * log(u)); + let u = randUniformExclusive_8(); + return ((-1f / rate) * log(u)); } fn prng_6() -> vec3f { @@ -530,7 +530,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_10) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -553,8 +553,8 @@ describe('probability distribution plot example', () => { } fn item_9() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; @@ -565,8 +565,8 @@ describe('probability distribution plot example', () => { } fn randNormal_7(mu: f32, sigma: f32) -> f32 { - var theta = (6.283185307179586f * randUniformExclusive_8()); - var R = sqrt((-2 * log(randUniformExclusive_8()))); + let theta = (6.283185307179586f * randUniformExclusive_8()); + let R = sqrt((-2f * log(randUniformExclusive_8()))); return (((R * sin(theta)) * sigma) + mu); } @@ -579,7 +579,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_10) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } @@ -602,8 +602,8 @@ describe('probability distribution plot example', () => { } fn item_9() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; @@ -614,7 +614,7 @@ describe('probability distribution plot example', () => { } fn randCauchy_7(x0: f32, gamma: f32) -> f32 { - var u = randUniformExclusive_8(); + let u = randUniformExclusive_8(); return (x0 + (gamma * tan((3.141592653589793f * (u - 0.5f))))); } @@ -627,7 +627,7 @@ describe('probability distribution plot example', () => { } @compute @workgroup_size(64) fn item_0(input: item_10) { - var id = input.gid.x; + let id = input.gid.x; if ((id >= arrayLength(&samplesBuffer_1))) { return; } diff --git a/packages/typegpu/tests/examples/individual/ray-marching.test.ts b/packages/typegpu/tests/examples/individual/ray-marching.test.ts index 190faa701c..ce4cb46665 100644 --- a/packages/typegpu/tests/examples/individual/ray-marching.test.ts +++ b/packages/typegpu/tests/examples/individual/ray-marching.test.ts @@ -27,7 +27,7 @@ describe('ray-marching example', () => { } @vertex fn vertexMain_0(_arg_0: vertexMain_Input_2) -> vertexMain_Output_1 { - var pos = array(vec2f(-1, -1), vec2f(3f, -1), vec2f(-1, 3f)); + var pos = array(vec2f(-1), vec2f(3, -1), vec2f(-1, 3)); var uv = array(vec2f(), vec2f(2, 0), vec2f(0, 2)); return vertexMain_Output_1(vec4f(pos[_arg_0.idx], 0f, 1f), uv[_arg_0.idx]); } @@ -46,17 +46,17 @@ describe('ray-marching example', () => { fn sdBoxFrame3d_10(p: vec3f, size: vec3f, thickness: f32) -> f32 { var p1 = (abs(p) - size); var q = (abs((p1 + thickness)) - vec3f(thickness)); - var d1 = (length(max(vec3f(p1.x, q.y, q.z), vec3f())) + min(max(p1.x, max(q.y, q.z)), 0f)); - var d2 = (length(max(vec3f(q.x, p1.y, q.z), vec3f())) + min(max(q.x, max(p1.y, q.z)), 0f)); - var d3 = (length(max(vec3f(q.x, q.y, p1.z), vec3f())) + min(max(q.x, max(q.y, p1.z)), 0f)); + let d1 = (length(max(vec3f(p1.x, q.y, q.z), vec3f())) + min(max(p1.x, max(q.y, q.z)), 0f)); + let d2 = (length(max(vec3f(q.x, p1.y, q.z), vec3f())) + min(max(q.x, max(p1.y, q.z)), 0f)); + let d3 = (length(max(vec3f(q.x, q.y, p1.z), vec3f())) + min(max(q.x, max(q.y, p1.z)), 0f)); return min(min(d1, d2), d3); } fn smoothShapeUnion_11(a: Shape_6, b: Shape_6, k: f32) -> Shape_6 { - var h = (max((k - abs((a.dist - b.dist))), 0f) / k); - var m = (h * h); - var dist = (min(a.dist, b.dist) - ((m * k) * 0.25f)); - var weight = (m + select(0f, (1f - m), (a.dist > b.dist))); + let h = (max((k - abs((a.dist - b.dist))), 0f) / k); + let m = (h * h); + let dist = (min(a.dist, b.dist) - ((m * k) * 0.25f)); + let weight = (m + select(0f, (1f - m), (a.dist > b.dist))); var color = mix(a.color, b.color, weight); return Shape_6(color, dist); } @@ -64,8 +64,8 @@ describe('ray-marching example', () => { fn getMorphingShape_8(p: vec3f, t: f32) -> Shape_6 { var center = vec3f(0, 2, 6); var localP = (p - center); - var rotMatZ = mat4x4f(cos(-t), sin(-t), 0, 0, -sin(-t), cos(-t), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - var rotMatX = mat4x4f(1, 0, 0, 0, 0, cos((-t * 0.6f)), sin((-t * 0.6f)), 0, 0, -sin((-t * 0.6f)), cos((-t * 0.6f)), 0, 0, 0, 0, 1); + var rotMatZ = mat4x4f(cos(-(t)), sin(-(t)), 0, 0, -sin(-(t)), cos(-(t)), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + var rotMatX = mat4x4f(1, 0, 0, 0, 0, cos((-(t) * 0.6f)), sin((-(t) * 0.6f)), 0, 0, -sin((-(t) * 0.6f)), cos((-(t) * 0.6f)), 0, 0, 0, 0, 1); var rotatedP = (rotMatZ * (rotMatX * vec4f(localP, 1f))).xyz; var boxSize = vec3f(0.699999988079071); var sphere1Offset = vec3f((cos((t * 2f)) * 0.8f), (sin((t * 3f)) * 0.3f), (sin((t * 2f)) * 0.8f)); @@ -73,8 +73,8 @@ describe('ray-marching example', () => { var sphere1 = Shape_6(vec3f(0.4000000059604645, 0.5, 1), sdSphere_9((localP - sphere1Offset), 0.5f)); var sphere2 = Shape_6(vec3f(1, 0.800000011920929, 0.20000000298023224), sdSphere_9((localP - sphere2Offset), 0.3f)); var box = Shape_6(vec3f(1, 0.30000001192092896, 0.30000001192092896), sdBoxFrame3d_10(rotatedP, boxSize, 0.1f)); - var spheres = smoothShapeUnion_11(sphere1, sphere2, 0.1); - return smoothShapeUnion_11(spheres, box, 0.2); + var spheres = smoothShapeUnion_11(sphere1, sphere2, 0.1f); + return smoothShapeUnion_11(spheres, box, 0.2f); } @group(0) @binding(1) var time_12: f32; @@ -115,16 +115,16 @@ describe('ray-marching example', () => { } fn getNormal_16(p: vec3f) -> vec3f { - var dist = getSceneDist_7(p).dist; - var e = 0.01; + let dist = getSceneDist_7(p).dist; + const e = 0.01; var n = vec3f((getSceneDist_7((p + vec3f(e, 0f, 0f))).dist - dist), (getSceneDist_7((p + vec3f(0f, e, 0f))).dist - dist), (getSceneDist_7((p + vec3f(0f, 0f, e))).dist - dist)); return normalize(n); } fn getOrbitingLightPos_17(t: f32) -> vec3f { - var radius = 3f; - var height = 6f; - var speed = 1f; + const radius = 3f; + const height = 6f; + const speed = 1f; return vec3f((cos((t * speed)) * radius), (height + (sin((t * speed)) * radius)), 4f); } @@ -135,7 +135,7 @@ describe('ray-marching example', () => { if ((t >= maxT)) { break; } - var h = getSceneDist_7((ro + (rd * t))).dist; + let h = getSceneDist_7((ro + (rd * t))).dist; if ((h < 1e-3f)) { return 0; } @@ -155,16 +155,16 @@ describe('ray-marching example', () => { var ro = vec3f(0, 2, 3); var rd = normalize(vec3f(uv.x, uv.y, 1f)); var march = rayMarch_5(ro, rd); - var fog = pow(min((march.dist / 30f), 1f), 0.7f); + let fog = pow(min((march.dist / 30f), 1f), 0.7f); var p = (ro + (rd * march.dist)); var n = getNormal_16(p); var lightPos = getOrbitingLightPos_17(time_12); var l = normalize((lightPos - p)); - var diff = max(dot(n, l), 0f); - var shadowRo = p; - var shadowRd = l; - var shadowDist = length((lightPos - p)); - var shadow = softShadow_18(shadowRo, shadowRd, 0.1, shadowDist, 16f); + let diff = max(dot(n, l), 0f); + let shadowRo = (&p); + let shadowRd = (&l); + let shadowDist = length((lightPos - p)); + let shadow = softShadow_18((*shadowRo), (*shadowRd), 0.1f, shadowDist, 16f); var litColor = (march.color * diff); var finalColor = mix((litColor * 0.5), litColor, shadow); return mix(vec4f(finalColor, 1f), vec4f(0.699999988079071, 0.800000011920929, 0.8999999761581421, 1), fog); diff --git a/packages/typegpu/tests/examples/individual/simple-shadow.test.ts b/packages/typegpu/tests/examples/individual/simple-shadow.test.ts index 7b6b22c35f..98a59ef958 100644 --- a/packages/typegpu/tests/examples/individual/simple-shadow.test.ts +++ b/packages/typegpu/tests/examples/individual/simple-shadow.test.ts @@ -87,11 +87,11 @@ describe('simple shadow example', () => { } @vertex fn mainVert_0(_arg_0: mainVert_Input_7) -> mainVert_Output_6 { - var modelMatrixUniform = instanceInfo_1.modelMatrix; - var worldPos = (modelMatrixUniform * _arg_0.position); + let modelMatrixUniform = (&instanceInfo_1.modelMatrix); + var worldPos = ((*modelMatrixUniform) * _arg_0.position); var viewPos = (cameraUniform_4.view * worldPos); var clipPos = (cameraUniform_4.projection * viewPos); - var transformedNormal = (modelMatrixUniform * _arg_0.normal); + var transformedNormal = ((*modelMatrixUniform) * _arg_0.normal); return mainVert_Output_6(clipPos, transformedNormal, worldPos.xyz); } @@ -125,7 +125,7 @@ describe('simple shadow example', () => { } @fragment fn mainFrag_8(_arg_0: mainFrag_Input_17) -> @location(0) vec4f { - var instanceInfo = instanceInfo_1; + let instanceInfo = (&instanceInfo_1); var N = normalize(_arg_0.normal.xyz); var L = normalize(-(light_9.direction)); var V = normalize((cameraUniform_4.position - _arg_0.worldPos)); @@ -134,24 +134,24 @@ describe('simple shadow example', () => { var ndc = (lp4.xyz / lp4.w); var uv = ((ndc.xy * 0.5) + 0.5); uv = vec2f(uv.x, (1f - uv.y)); - var currentDepth = ndc.z; - var inBounds = (all((uv >= vec2f())) && all((uv <= vec2f(1)))); + let currentDepth = ndc.z; + let inBounds = (all((uv >= vec2f())) && all((uv <= vec2f(1)))); var shadowFactor = textureSampleCompare(shadowMap_13, comparisonSampler_14, uv, currentDepth); if (!inBounds) { shadowFactor = 1f; } - var ambient = (instanceInfo.material.ambient * light_9.color); - var diff = max(0f, dot(N, L)); - var diffuse = ((instanceInfo.material.diffuse * light_9.color) * diff); - var spec = pow(max(0f, dot(V, R)), instanceInfo.material.shininess); - var specular = ((instanceInfo.material.specular * light_9.color) * spec); + var ambient = ((*instanceInfo).material.ambient * light_9.color); + let diff = max(0f, dot(N, L)); + var diffuse = (((*instanceInfo).material.diffuse * light_9.color) * diff); + let spec = pow(max(0f, dot(V, R)), (*instanceInfo).material.shininess); + var specular = (((*instanceInfo).material.specular * light_9.color) * spec); var lit = ((diffuse + specular) * shadowFactor); var finalColor = (ambient + lit); if ((paramsUniform_15.shadowOnly == 1f)) { return vec4f(vec3f(shadowFactor), 1f); } if ((paramsUniform_15.lightDepth == 1f)) { - var remappedDepth = clamp(((currentDepth - 0.2f) / 0.49999999999999994f), 0f, 1f); + let remappedDepth = clamp(((currentDepth - 0.2f) / 0.49999999999999994f), 0f, 1f); return vec4f(vec3f(remappedDepth), 1f); } return vec4f(finalColor, 1f); diff --git a/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts b/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts index b018631740..0f89fa2b70 100644 --- a/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts +++ b/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts @@ -30,8 +30,8 @@ describe('slime mold 3d example', () => { } fn item_7() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; @@ -42,13 +42,13 @@ describe('slime mold 3d example', () => { } fn randNormal_8(mu: f32, sigma: f32) -> f32 { - var theta = (6.283185307179586f * randUniformExclusive_9()); - var R = sqrt((-2 * log(randUniformExclusive_9()))); + let theta = (6.283185307179586f * randUniformExclusive_9()); + let R = sqrt((-2f * log(randUniformExclusive_9()))); return (((R * sin(theta)) * sigma) + mu); } fn randInUnitSphere_6() -> vec3f { - var u = item_7(); + let u = item_7(); var v = vec3f(randNormal_8(0f, 1f), randNormal_8(0f, 1f), randNormal_8(0f, 1f)); var vNorm = normalize(v); return (vNorm * pow(u, 0.33f)); @@ -63,7 +63,7 @@ describe('slime mold 3d example', () => { fn wrappedCallback_2(x: u32, _arg_1: u32, _arg_2: u32) { randSeed_3((f32(x) / 8e+5f)); - var pos = ((randInUnitSphere_6() * 64.) + vec3f(128)); + var pos = ((randInUnitSphere_6() * 64f) + vec3f(128)); var center = vec3f(128); var dir = normalize((center - pos)); agentsData_10[x] = Agent_11(pos, dir); @@ -112,15 +112,15 @@ describe('slime mold 3d example', () => { var samplePos = (vec3i(_arg_0.gid.xyz) + vec3i(offsetX, offsetY, offsetZ)); var dimsi = vec3i(dims); if (((((((samplePos.x >= 0i) && (samplePos.x < dimsi.x)) && (samplePos.y >= 0i)) && (samplePos.y < dimsi.y)) && (samplePos.z >= 0i)) && (samplePos.z < dimsi.z))) { - var value = textureLoad(oldState_1, vec3u(samplePos)).x; + let value = textureLoad(oldState_1, vec3u(samplePos)).x; sum = (sum + value); count = (count + 1f); } } } } - var blurred = (sum / count); - var newValue = saturate((blurred - params_2.evaporationRate)); + let blurred = (sum / count); + let newValue = saturate((blurred - params_2.evaporationRate)); textureStore(newState_4, _arg_0.gid.xyz, vec4f(newValue, 0f, 0f, 1f)); } @@ -144,8 +144,8 @@ describe('slime mold 3d example', () => { @group(0) @binding(0) var agentsData_5: array; fn item_8() -> f32 { - var a = dot(seed_3, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_3, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_3, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_3, vec2f(54.47856521606445, 345.8415222167969)); seed_3.x = fract((cos(a) * 136.8168f)); seed_3.y = fract((cos(b) * 534.7645f)); return seed_3.y; @@ -157,9 +157,9 @@ describe('slime mold 3d example', () => { fn getPerpendicular_10(dir: vec3f) -> vec3f { var axis = vec3f(1, 0, 0); - var absX = abs(dir.x); - var absY = abs(dir.y); - var absZ = abs(dir.z); + let absX = abs(dir.x); + let absY = abs(dir.y); + let absZ = abs(dir.z); if (((absY <= absX) && (absY <= absZ))) { axis = vec3f(0, 1, 0); } @@ -194,14 +194,14 @@ describe('slime mold 3d example', () => { var totalWeight = 0f; var perp1 = getPerpendicular_10(direction); var perp2 = cross(direction, perp1); - var numSamples = 8; + const numSamples = 8; for (var i = 0; (i < numSamples); i++) { - var theta = (((f32(i) / f32(numSamples)) * 2f) * 3.141592653589793f); + let theta = (((f32(i) / f32(numSamples)) * 2f) * 3.141592653589793f); var coneOffset = ((perp1 * cos(theta)) + (perp2 * sin(theta))); var sensorDir = normalize((direction + (coneOffset * sin(params_11.sensorAngle)))); var sensorPos = (pos + (sensorDir * params_11.sensorDistance)); var sensorPosInt = vec3u(clamp(sensorPos, vec3f(), (dimsf - vec3f(1)))); - var weight = textureLoad(oldState_4, sensorPosInt).x; + let weight = textureLoad(oldState_4, sensorPosInt).x; weightedDir = (weightedDir + (sensorDir * weight)); totalWeight = (totalWeight + weight); } @@ -213,13 +213,13 @@ describe('slime mold 3d example', () => { } fn randNormal_16(mu: f32, sigma: f32) -> f32 { - var theta = (6.283185307179586f * randUniformExclusive_17()); - var R = sqrt((-2 * log(randUniformExclusive_17()))); + let theta = (6.283185307179586f * randUniformExclusive_17()); + let R = sqrt((-2f * log(randUniformExclusive_17()))); return (((R * sin(theta)) * sigma) + mu); } fn randInUnitSphere_15() -> vec3f { - var u = item_8(); + let u = item_8(); var v = vec3f(randNormal_16(0f, 1f), randNormal_16(0f, 1f), randNormal_16(0f, 1f)); var vNorm = normalize(v); return (vNorm * pow(u, 0.33f)); @@ -227,7 +227,7 @@ describe('slime mold 3d example', () => { fn randInUnitHemisphere_14(normal: vec3f) -> vec3f { var value = randInUnitSphere_15(); - var alignment = dot(normal, value); + let alignment = dot(normal, value); return (sign(alignment) * value); } @@ -244,10 +244,10 @@ describe('slime mold 3d example', () => { randSeed_1(((f32(_arg_0.gid.x) / 8e+5f) + 0.1f)); var dims = textureDimensions(oldState_4); var dimsf = vec3f(dims); - var agent = agentsData_5[_arg_0.gid.x]; - var random = randFloat01_7(); - var direction = normalize(agent.direction); - var senseResult = sense3D_9(agent.position, direction); + let agent = (&agentsData_5[_arg_0.gid.x]); + let random = randFloat01_7(); + var direction = normalize((*agent).direction); + var senseResult = sense3D_9((*agent).position, direction); if ((senseResult.totalWeight > 0.01f)) { var targetDir = normalize(senseResult.weightedDir); direction = normalize((direction + (targetDir * (params_11.turnSpeed * params_11.deltaTime)))); @@ -257,13 +257,13 @@ describe('slime mold 3d example', () => { var randomOffset = (perp * ((((random * 2f) - 1f) * params_11.turnSpeed) * params_11.deltaTime)); direction = normalize((direction + randomOffset)); } - var newPos = (agent.position + (direction * (params_11.moveSpeed * params_11.deltaTime))); + var newPos = ((*agent).position + (direction * (params_11.moveSpeed * params_11.deltaTime))); var center = (dimsf / 2); if (((newPos.x < 0f) || (newPos.x >= dimsf.x))) { newPos.x = clamp(newPos.x, 0f, (dimsf.x - 1f)); var normal = vec3f(1, 0, 0); if ((newPos.x > 1f)) { - normal = vec3f(-1, 0f, 0f); + normal = vec3f(-1, 0, 0); } var randomDir = randInUnitHemisphere_14(normal); var toCenter = normalize((center - newPos)); @@ -273,7 +273,7 @@ describe('slime mold 3d example', () => { newPos.y = clamp(newPos.y, 0f, (dimsf.y - 1f)); var normal = vec3f(0, 1, 0); if ((newPos.y > 1f)) { - normal = vec3f(0f, -1, 0f); + normal = vec3f(0, -1, 0); } var randomDir = randInUnitHemisphere_14(normal); var toCenter = normalize((center - newPos)); @@ -283,15 +283,15 @@ describe('slime mold 3d example', () => { newPos.z = clamp(newPos.z, 0f, (dimsf.z - 1f)); var normal = vec3f(0, 0, 1); if ((newPos.z > 1f)) { - normal = vec3f(0f, 0f, -1); + normal = vec3f(0, 0, -1); } var randomDir = randInUnitHemisphere_14(normal); var toCenter = normalize((center - newPos)); direction = normalize(((randomDir * 0.3) + (toCenter * 0.7))); } agentsData_5[_arg_0.gid.x] = Agent_6(newPos, direction); - var oldState = textureLoad(oldState_4, vec3u(newPos)).x; - var newState = (oldState + 1f); + let oldState = textureLoad(oldState_4, vec3u(newPos)).x; + let newState = (oldState + 1f); textureStore(newState_18, vec3u(newPos), vec4f(newState, 0f, 0f, 1f)); } @@ -331,9 +331,9 @@ describe('slime mold 3d example', () => { var t1 = ((boxMax - rayOrigin) * invDir); var tmin = min(t0, t1); var tmax = max(t0, t1); - var tNear = max(max(tmin.x, tmin.y), tmin.z); - var tFar = min(min(tmax.x, tmax.y), tmax.z); - var hit = ((tFar >= tNear) && (tFar >= 0f)); + let tNear = max(max(tmin.x, tmin.y), tmin.z); + let tFar = min(min(tmax.x, tmax.y), tmax.z); + let hit = ((tFar >= tNear) && (tFar >= 0f)); return RayBoxResult_7(tNear, tFar, hit); } @@ -347,7 +347,7 @@ describe('slime mold 3d example', () => { @fragment fn fragmentShader_3(_arg_0: fragmentShader_Input_10) -> @location(0) vec4f { var ndc = vec2f(((_arg_0.uv.x * 2f) - 1f), (1f - (_arg_0.uv.y * 2f))); - var ndcNear = vec4f(ndc, -1, 1f); + var ndcNear = vec4f(ndc, -1f, 1f); var ndcFar = vec4f(ndc, 1f, 1f); var worldNear = (cameraData_4.invViewProj * ndcNear); var worldFar = (cameraData_4.invViewProj * ndcFar); @@ -360,34 +360,34 @@ describe('slime mold 3d example', () => { if (!isect.hit) { return vec4f(); } - var tStart = max(isect.tNear, 0f); - var tEnd = isect.tFar; - var numSteps = 128; - var stepSize = ((tEnd - tStart) / f32(numSteps)); - var thresholdLo = 0.05999999865889549f; - var thresholdHi = 0.25f; - var gamma = 1.399999976158142f; - var sigmaT = 0.05000000074505806f; + let tStart = max(isect.tNear, 0f); + let tEnd = isect.tFar; + const numSteps = 128; + let stepSize = ((tEnd - tStart) / f32(numSteps)); + const thresholdLo = 0.05999999865889549f; + const thresholdHi = 0.25f; + const gamma = 1.399999976158142f; + const sigmaT = 0.05000000074505806f; var albedo = vec3f(0.5699999928474426, 0.4399999976158142, 0.9599999785423279); var transmittance = 1f; var accum = vec3f(); - var TMin = 0.0010000000474974513f; + const TMin = 0.0010000000474974513f; for (var i = 0; (i < numSteps); i++) { if ((transmittance <= TMin)) { break; } - var t = (tStart + ((f32(i) + 0.5f) * stepSize)); + let t = (tStart + ((f32(i) + 0.5f) * stepSize)); var pos = (rayOrigin + (rayDir * t)); var texCoord = (pos / vec3f(256)); - var sampleValue = textureSampleLevel(state_8, sampler_9, texCoord, 0).x; - var d0 = smoothstep(thresholdLo, thresholdHi, sampleValue); - var density = pow(d0, gamma); - var alphaSrc = (1f - exp(((-sigmaT * density) * stepSize))); + let sampleValue = textureSampleLevel(state_8, sampler_9, texCoord, 0).x; + let d0 = smoothstep(thresholdLo, thresholdHi, sampleValue); + let density = pow(d0, gamma); + let alphaSrc = (1f - exp(((-(sigmaT) * density) * stepSize))); var contrib = (albedo * alphaSrc); accum = (accum + (contrib * transmittance)); transmittance = (transmittance * (1f - alphaSrc)); } - var alpha = (1f - transmittance); + let alpha = (1f - transmittance); return vec4f(accum, alpha); }" `); diff --git a/packages/typegpu/tests/examples/individual/slime-mold.test.ts b/packages/typegpu/tests/examples/individual/slime-mold.test.ts index fe38c0aa96..f474932a76 100644 --- a/packages/typegpu/tests/examples/individual/slime-mold.test.ts +++ b/packages/typegpu/tests/examples/individual/slime-mold.test.ts @@ -30,16 +30,16 @@ describe('slime mold example', () => { } fn item_7() -> f32 { - var a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_5, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_5, vec2f(54.47856521606445, 345.8415222167969)); seed_5.x = fract((cos(a) * 136.8168f)); seed_5.y = fract((cos(b) * 534.7645f)); return seed_5.y; } fn randInUnitCircle_6() -> vec2f { - var radius = sqrt(item_7()); - var angle = (item_7() * 6.283185307179586f); + let radius = sqrt(item_7()); + let angle = (item_7() * 6.283185307179586f); return vec2f((cos(angle) * radius), (sin(angle) * radius)); } @@ -52,8 +52,8 @@ describe('slime mold example', () => { fn wrappedCallback_2(x: u32, _arg_1: u32, _arg_2: u32) { randSeed_3(((f32(x) / 2e+5f) + 0.1f)); - var pos = ((randInUnitCircle_6() * 140.) + vec2f(150, 75)); - var angle = atan2((75f - pos.y), (150f - pos.x)); + var pos = ((randInUnitCircle_6() * 140f) + vec2f(150, 75)); + let angle = atan2((75f - pos.y), (150f - pos.x)); agentsData_8[x] = Agent_9(pos, angle); } @@ -129,8 +129,8 @@ describe('slime mold example', () => { @group(0) @binding(0) var agentsData_5: array; fn item_8() -> f32 { - var a = dot(seed_3, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_3, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_3, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_3, vec2f(54.47856521606445, 345.8415222167969)); seed_3.x = fract((cos(a) * 136.8168f)); seed_3.y = fract((cos(b) * 534.7645f)); return seed_3.y; @@ -151,7 +151,7 @@ describe('slime mold example', () => { @group(0) @binding(1) var params_10: Params_11; fn sense_9(pos: vec2f, angle: f32, sensorAngleOffset: f32) -> f32 { - var sensorAngle = (angle + sensorAngleOffset); + let sensorAngle = (angle + sensorAngleOffset); var sensorDir = vec2f(cos(sensorAngle), sin(sensorAngle)); var sensorPos = (pos + (sensorDir * params_10.sensorDistance)); var dims = textureDimensions(oldState_4); @@ -175,12 +175,12 @@ describe('slime mold example', () => { } randSeed_1(((f32(_arg_0.gid.x) / 2e+5f) + 0.1f)); var dims = textureDimensions(oldState_4); - var agent = agentsData_5[_arg_0.gid.x]; - var random = randFloat01_7(); - var weightForward = sense_9(agent.position, agent.angle, 0f); - var weightLeft = sense_9(agent.position, agent.angle, params_10.sensorAngle); - var weightRight = sense_9(agent.position, agent.angle, -params_10.sensorAngle); - var angle = agent.angle; + let agent = (&agentsData_5[_arg_0.gid.x]); + let random = randFloat01_7(); + let weightForward = sense_9((*agent).position, (*agent).angle, 0f); + let weightLeft = sense_9((*agent).position, (*agent).angle, params_10.sensorAngle); + let weightRight = sense_9((*agent).position, (*agent).angle, -(params_10.sensorAngle)); + var angle = (*agent).angle; if (((weightForward > weightLeft) && (weightForward > weightRight))) { } @@ -200,7 +200,7 @@ describe('slime mold example', () => { } } var dir = vec2f(cos(angle), sin(angle)); - var newPos = (agent.position + (dir * (params_10.moveSpeed * deltaTime_12))); + var newPos = ((*agent).position + (dir * (params_10.moveSpeed * deltaTime_12))); var dimsf = vec2f(dims); if (((((newPos.x < 0f) || (newPos.x > dimsf.x)) || (newPos.y < 0f)) || (newPos.y > dimsf.y))) { newPos = clamp(newPos, vec2f(), (dimsf - vec2f(1))); @@ -208,7 +208,7 @@ describe('slime mold example', () => { angle = (3.141592653589793f - angle); } if (((newPos.y <= 0f) || (newPos.y >= (dimsf.y - 1f)))) { - angle = -angle; + angle = -(angle); } angle += ((random - 0.5f) * 0.1f); } @@ -228,8 +228,8 @@ describe('slime mold example', () => { } @vertex fn fullScreenTriangle_0(input: fullScreenTriangle_Input_2) -> fullScreenTriangle_Output_1 { - var pos = array(vec2f(-1, -1), vec2f(3f, -1), vec2f(-1, 3f)); - var uv = array(vec2f(0, 1), vec2f(2, 1), vec2f(0f, -1)); + var pos = array(vec2f(-1), vec2f(3, -1), vec2f(-1, 3)); + var uv = array(vec2f(0, 1), vec2f(2, 1), vec2f(0, -1)); return fullScreenTriangle_Output_1(vec4f(pos[input.vertexIndex], 0f, 1f), uv[input.vertexIndex]); } diff --git a/packages/typegpu/tests/examples/individual/square.test.ts b/packages/typegpu/tests/examples/individual/square.test.ts index 5e30e8cb06..4fe2d98ce0 100644 --- a/packages/typegpu/tests/examples/individual/square.test.ts +++ b/packages/typegpu/tests/examples/individual/square.test.ts @@ -28,7 +28,7 @@ describe('square example', () => { } @vertex fn vertex_0(_arg_0: vertex_Input_2) -> vertex_Output_1 { - var vertices = array(vec2f(-1, -1), vec2f(1f, -1), vec2f(1), vec2f(-1, 1f)); + var vertices = array(vec2f(-1), vec2f(1, -1), vec2f(1), vec2f(-1, 1)); return vertex_Output_1(_arg_0.color, vec4f(vertices[_arg_0.idx], 0f, 1f)); } diff --git a/packages/typegpu/tests/examples/individual/stable-fluid.test.ts b/packages/typegpu/tests/examples/individual/stable-fluid.test.ts index 98028f39ad..126a860671 100644 --- a/packages/typegpu/tests/examples/individual/stable-fluid.test.ts +++ b/packages/typegpu/tests/examples/individual/stable-fluid.test.ts @@ -44,10 +44,10 @@ describe('stable-fluid example', () => { return; } var velocity = textureLoad(src_1, pixelPos, 0); - var timeStep = simParams_3.dt; + let timeStep = simParams_3.dt; var prevPos = (vec2f(pixelPos) - (timeStep * velocity.xy)); - var clampedPos = clamp(prevPos, vec2f(-0.5), vec2f((vec2f(texSize.xy) - vec2f(0.5)))); - var normalizedPos = ((clampedPos + vec2f(0.5)) / vec2f(texSize.xy)); + var clampedPos = clamp(prevPos, vec2f(-0.5), (vec2f(texSize.xy) - 0.5)); + var normalizedPos = ((clampedPos + 0.5) / vec2f(texSize.xy)); var prevVelocity = textureSampleLevel(src_1, linSampler_5, normalizedPos, 0); textureStore(dst_2, pixelPos, prevVelocity); } @@ -55,7 +55,7 @@ describe('stable-fluid example', () => { @group(0) @binding(0) var in_1: texture_2d; fn getNeighbors_2(coords: vec2i, bounds: vec2i) -> array { - var adjacentOffsets = array(vec2i(-1, 0i), vec2i(0i, -1), vec2i(1, 0), vec2i(0, 1)); + var adjacentOffsets = array(vec2i(-1, 0), vec2i(0, -1), vec2i(1, 0), vec2i(0, 1)); for (var i = 0; (i < 4i); i++) { adjacentOffsets[i] = clamp((coords + adjacentOffsets[i]), vec2i(), (bounds - vec2i(1))); } @@ -80,14 +80,14 @@ describe('stable-fluid example', () => { var texSize = vec2i(textureDimensions(in_1)); var centerVal = textureLoad(in_1, pixelPos, 0); var neighbors = getNeighbors_2(pixelPos, texSize); - var leftVal = textureLoad(in_1, neighbors[0], 0); - var upVal = textureLoad(in_1, neighbors[1], 0); - var rightVal = textureLoad(in_1, neighbors[2], 0); - var downVal = textureLoad(in_1, neighbors[3], 0); - var timeStep = simParams_3.dt; - var viscosity = simParams_3.viscosity; - var diffuseRate = (viscosity * timeStep); - var blendFactor = (1f / (4f + diffuseRate)); + var leftVal = textureLoad(in_1, neighbors[0i], 0); + var upVal = textureLoad(in_1, neighbors[1i], 0); + var rightVal = textureLoad(in_1, neighbors[2i], 0); + var downVal = textureLoad(in_1, neighbors[3i], 0); + let timeStep = simParams_3.dt; + let viscosity = simParams_3.viscosity; + let diffuseRate = (viscosity * timeStep); + let blendFactor = (1f / (4f + diffuseRate)); var diffusedVal = (vec4f(blendFactor) * (((leftVal + rightVal) + (upVal + downVal)) + (diffuseRate * centerVal))); textureStore(out_5, pixelPos, diffusedVal); } @@ -95,7 +95,7 @@ describe('stable-fluid example', () => { @group(0) @binding(0) var vel_1: texture_2d; fn getNeighbors_2(coords: vec2i, bounds: vec2i) -> array { - var adjacentOffsets = array(vec2i(-1, 0i), vec2i(0i, -1), vec2i(1, 0), vec2i(0, 1)); + var adjacentOffsets = array(vec2i(-1, 0), vec2i(0, -1), vec2i(1, 0), vec2i(0, 1)); for (var i = 0; (i < 4i); i++) { adjacentOffsets[i] = clamp((coords + adjacentOffsets[i]), vec2i(), (bounds - vec2i(1))); } @@ -112,18 +112,18 @@ describe('stable-fluid example', () => { var pixelPos = vec2i(input.gid.xy); var texSize = vec2i(textureDimensions(vel_1)); var neighbors = getNeighbors_2(pixelPos, texSize); - var leftVel = textureLoad(vel_1, neighbors[0], 0); - var upVel = textureLoad(vel_1, neighbors[1], 0); - var rightVel = textureLoad(vel_1, neighbors[2], 0); - var downVel = textureLoad(vel_1, neighbors[3], 0); - var divergence = (0.5f * ((rightVel.x - leftVel.x) + (downVel.y - upVel.y))); + var leftVel = textureLoad(vel_1, neighbors[0i], 0); + var upVel = textureLoad(vel_1, neighbors[1i], 0); + var rightVel = textureLoad(vel_1, neighbors[2i], 0); + var downVel = textureLoad(vel_1, neighbors[3i], 0); + let divergence = (0.5f * ((rightVel.x - leftVel.x) + (downVel.y - upVel.y))); textureStore(div_3, pixelPos, vec4f(divergence, 0f, 0f, 1f)); } @group(0) @binding(0) var x_1: texture_2d; fn getNeighbors_2(coords: vec2i, bounds: vec2i) -> array { - var adjacentOffsets = array(vec2i(-1, 0i), vec2i(0i, -1), vec2i(1, 0), vec2i(0, 1)); + var adjacentOffsets = array(vec2i(-1, 0), vec2i(0, -1), vec2i(1, 0), vec2i(0, 1)); for (var i = 0; (i < 4i); i++) { adjacentOffsets[i] = clamp((coords + adjacentOffsets[i]), vec2i(), (bounds - vec2i(1))); } @@ -142,19 +142,19 @@ describe('stable-fluid example', () => { var pixelPos = vec2i(input.gid.xy); var texSize = vec2i(textureDimensions(x_1)); var neighbors = getNeighbors_2(pixelPos, texSize); - var leftPressure = textureLoad(x_1, neighbors[0], 0); - var upPressure = textureLoad(x_1, neighbors[1], 0); - var rightPressure = textureLoad(x_1, neighbors[2], 0); - var downPressure = textureLoad(x_1, neighbors[3], 0); - var divergence = textureLoad(b_3, pixelPos, 0).x; - var newPressure = (0.25f * ((((leftPressure.x + rightPressure.x) + upPressure.x) + downPressure.x) - divergence)); + var leftPressure = textureLoad(x_1, neighbors[0i], 0); + var upPressure = textureLoad(x_1, neighbors[1i], 0); + var rightPressure = textureLoad(x_1, neighbors[2i], 0); + var downPressure = textureLoad(x_1, neighbors[3i], 0); + let divergence = textureLoad(b_3, pixelPos, 0).x; + let newPressure = (0.25f * ((((leftPressure.x + rightPressure.x) + upPressure.x) + downPressure.x) - divergence)); textureStore(out_4, pixelPos, vec4f(newPressure, 0f, 0f, 1f)); } @group(0) @binding(0) var vel_1: texture_2d; fn getNeighbors_2(coords: vec2i, bounds: vec2i) -> array { - var adjacentOffsets = array(vec2i(-1, 0i), vec2i(0i, -1), vec2i(1, 0), vec2i(0, 1)); + var adjacentOffsets = array(vec2i(-1, 0), vec2i(0, -1), vec2i(1, 0), vec2i(0, 1)); for (var i = 0; (i < 4i); i++) { adjacentOffsets[i] = clamp((coords + adjacentOffsets[i]), vec2i(), (bounds - vec2i(1))); } @@ -174,10 +174,10 @@ describe('stable-fluid example', () => { var texSize = vec2i(textureDimensions(vel_1)); var velocity = textureLoad(vel_1, pixelPos, 0); var neighbors = getNeighbors_2(pixelPos, texSize); - var leftPressure = textureLoad(p_3, neighbors[0], 0); - var upPressure = textureLoad(p_3, neighbors[1], 0); - var rightPressure = textureLoad(p_3, neighbors[2], 0); - var downPressure = textureLoad(p_3, neighbors[3], 0); + var leftPressure = textureLoad(p_3, neighbors[0i], 0); + var upPressure = textureLoad(p_3, neighbors[1i], 0); + var rightPressure = textureLoad(p_3, neighbors[2i], 0); + var downPressure = textureLoad(p_3, neighbors[3i], 0); var pressureGrad = vec2f((0.5f * (rightPressure.x - leftPressure.x)), (0.5f * (downPressure.x - upPressure.x))); var projectedVel = (velocity.xy - pressureGrad); textureStore(out_4, pixelPos, vec4f(projectedVel, 0f, 1f)); @@ -206,7 +206,7 @@ describe('stable-fluid example', () => { var texSize = textureDimensions(src_1); var pixelPos = input.gid.xy; var velocity = textureLoad(vel_2, pixelPos, 0).xy; - var timeStep = simParams_3.dt; + let timeStep = simParams_3.dt; var prevPos = (vec2f(pixelPos) - (timeStep * velocity)); var clampedPos = clamp(prevPos, vec2f(-0.5), (vec2f(texSize.xy) - vec2f(0.5))); var normalizedPos = ((clampedPos + vec2f(0.5)) / vec2f(texSize.xy)); @@ -224,7 +224,7 @@ describe('stable-fluid example', () => { } @vertex fn renderFn_0(input: renderFn_Input_2) -> renderFn_Output_1 { - var vertices = array(vec2f(-1, -1), vec2f(3f, -1), vec2f(-1, 3f)); + var vertices = array(vec2f(-1), vec2f(3, -1), vec2f(-1, 3)); var texCoords = array(vec2f(), vec2f(2, 0), vec2f(0, 2)); return renderFn_Output_1(vec4f(vertices[input.idx], 0f, 1f), texCoords[input.idx]); } @@ -240,16 +240,16 @@ describe('stable-fluid example', () => { } @fragment fn fragmentImageFn_3(input: fragmentImageFn_Input_7) -> @location(0) vec4f { - var pixelStep = 0.001953125f; - var leftSample = textureSample(result_4, linSampler_5, vec2f((input.uv.x - pixelStep), input.uv.y)).x; - var rightSample = textureSample(result_4, linSampler_5, vec2f((input.uv.x + pixelStep), input.uv.y)).x; - var upSample = textureSample(result_4, linSampler_5, vec2f(input.uv.x, (input.uv.y + pixelStep))).x; - var downSample = textureSample(result_4, linSampler_5, vec2f(input.uv.x, (input.uv.y - pixelStep))).x; - var gradientX = (rightSample - leftSample); - var gradientY = (upSample - downSample); - var distortStrength = 0.8; + const pixelStep = 0.001953125f; + let leftSample = textureSample(result_4, linSampler_5, vec2f((input.uv.x - pixelStep), input.uv.y)).x; + let rightSample = textureSample(result_4, linSampler_5, vec2f((input.uv.x + pixelStep), input.uv.y)).x; + let upSample = textureSample(result_4, linSampler_5, vec2f(input.uv.x, (input.uv.y + pixelStep))).x; + let downSample = textureSample(result_4, linSampler_5, vec2f(input.uv.x, (input.uv.y - pixelStep))).x; + let gradientX = (rightSample - leftSample); + let gradientY = (upSample - downSample); + const distortStrength = 0.8; var distortVector = vec2f(gradientX, gradientY); - var distortedUV = (input.uv + (distortVector * vec2f(distortStrength, -distortStrength))); + var distortedUV = (input.uv + (distortVector * vec2f(distortStrength, -(distortStrength)))); var outputColor = textureSample(background_6, linSampler_5, vec2f(distortedUV.x, (1f - distortedUV.y))); return vec4f(outputColor.xyz, 1f); }" diff --git a/packages/typegpu/tests/examples/individual/tgsl-parsing-test.test.ts b/packages/typegpu/tests/examples/individual/tgsl-parsing-test.test.ts index 6bd4dcac8b..b2c3b71c35 100644 --- a/packages/typegpu/tests/examples/individual/tgsl-parsing-test.test.ts +++ b/packages/typegpu/tests/examples/individual/tgsl-parsing-test.test.ts @@ -36,10 +36,10 @@ describe('tgsl parsing test example', () => { fn logicalExpressionTests_1() -> bool { var s = true; - s = (s && (true == true)); - s = (s && (true == true)); - s = (s && (true == true)); - s = (s && (false == false)); + s = (s && true); + s = (s && true); + s = (s && true); + s = (s && true); s = (s && true); s = (s && !false); s = (s && true); @@ -47,10 +47,10 @@ describe('tgsl parsing test example', () => { s = (s && true); s = (s && !false); s = (s && true); - s = (s && all((vec3f(1f, -1, 0f) < vec3f(1f, 1f, -1)) == vec3(false, true, false))); - s = (s && all((vec3f(1f, -1, 0f) <= vec3f(1f, 1f, -1)) == vec3(true, true, false))); - s = (s && all((vec3f(1f, -1, 0f) > vec3f(1f, 1f, -1)) == vec3(false, false, true))); - s = (s && all((vec3f(1f, -1, 0f) >= vec3f(1f, 1f, -1)) == vec3(true, false, true))); + s = (s && true); + s = (s && true); + s = (s && true); + s = (s && true); s = (s && true); s = (s && true); s = (s && true); @@ -58,8 +58,8 @@ describe('tgsl parsing test example', () => { s = (s && !false); s = (s && true); s = (s && !false); - s = (s && all(select(vec2i(-1, -2), vec2i(1, 2), true) == vec2i(1, 2))); - s = (s && all(select(vec4i(-1, -2, -3, -4), vec4i(1, 2, 3, 4), vec4(true, true, false, false)) == vec4i(1i, 2i, -3, -4))); + s = (s && true); + s = (s && true); var vec = vec3(true, false, true); s = (s && all(!(vec) == negate_2(vec))); var inputStruct = Schema_3(vec2(false, true), vec4(false, true, false, true), vec3(true, true, false), true); @@ -73,21 +73,21 @@ describe('tgsl parsing test example', () => { fn matrixOpsTests_5() -> bool { var s = true; - s = (s && all(abs((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0f, 1f).x, vec3f(-1, 0f, 1f).y, vec3f(-1, 0f, 1f).z, 1) * vec4f(1, 2, 3, 1)) - vec4f(0, 2, 4, 1)) <= ((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0f, 1f).x, vec3f(-1, 0f, 1f).y, vec3f(-1, 0f, 1f).z, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0f, 1f).x, vec3f(-1, 0f, 1f).y, vec3f(-1, 0f, 1f).z, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); - s = (s && all(abs((mat4x4f(vec3f(-1, 0f, 1f).x, 0, 0, 0, 0, vec3f(-1, 0f, 1f).y, 0, 0, 0, 0, vec3f(-1, 0f, 1f).z, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - vec4f(-1, 0f, 3f, 1f)) <= ((mat4x4f(vec3f(-1, 0f, 1f).x, 0, 0, 0, 0, vec3f(-1, 0f, 1f).y, 0, 0, 0, 0, vec3f(-1, 0f, 1f).z, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(vec3f(-1, 0f, 1f).x, 0, 0, 0, 0, vec3f(-1, 0f, 1f).y, 0, 0, 0, 0, vec3f(-1, 0f, 1f).z, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); - s = (s && all(abs((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - vec4f(1f, -3, 2f, 1f)) <= ((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); - s = (s && all(abs((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - vec4f(3f, 2f, -1, 1f)) <= ((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); - s = (s && all(abs((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - vec4f(-2, 1f, 3f, 1f)) <= ((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); - s = (s && all(abs(((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0f, 1f).x, vec3f(-1, 0f, 1f).y, vec3f(-1, 0f, 1f).z, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(0, 2, 4, 1)) <= (((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0f, 1f).x, vec3f(-1, 0f, 1f).y, vec3f(-1, 0f, 1f).z, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0f, 1f).x, vec3f(-1, 0f, 1f).y, vec3f(-1, 0f, 1f).z, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); - s = (s && all(abs(((mat4x4f(vec3f(-1, 0f, 1f).x, 0, 0, 0, 0, vec3f(-1, 0f, 1f).y, 0, 0, 0, 0, vec3f(-1, 0f, 1f).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(-1, 0f, 3f, 1f)) <= (((mat4x4f(vec3f(-1, 0f, 1f).x, 0, 0, 0, 0, vec3f(-1, 0f, 1f).y, 0, 0, 0, 0, vec3f(-1, 0f, 1f).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(vec3f(-1, 0f, 1f).x, 0, 0, 0, 0, vec3f(-1, 0f, 1f).y, 0, 0, 0, 0, vec3f(-1, 0f, 1f).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); - s = (s && all(abs(((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(1f, -3, 2f, 1f)) <= (((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); - s = (s && all(abs(((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(3f, 2f, -1, 1f)) <= (((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); - s = (s && all(abs(((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(-2, 1f, 3f, 1f)) <= (((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0, 1).x, vec3f(-1, 0, 1).y, vec3f(-1, 0, 1).z, 1) * vec4f(1, 2, 3, 1)) - vec4f(0, 2, 4, 1)) <= ((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0, 1).x, vec3f(-1, 0, 1).y, vec3f(-1, 0, 1).z, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0, 1).x, vec3f(-1, 0, 1).y, vec3f(-1, 0, 1).z, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs((mat4x4f(vec3f(-1, 0, 1).x, 0, 0, 0, 0, vec3f(-1, 0, 1).y, 0, 0, 0, 0, vec3f(-1, 0, 1).z, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - vec4f(-1, 0, 3, 1)) <= ((mat4x4f(vec3f(-1, 0, 1).x, 0, 0, 0, 0, vec3f(-1, 0, 1).y, 0, 0, 0, 0, vec3f(-1, 0, 1).z, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(vec3f(-1, 0, 1).x, 0, 0, 0, 0, vec3f(-1, 0, 1).y, 0, 0, 0, 0, vec3f(-1, 0, 1).z, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - vec4f(1, -3, 2, 1)) <= ((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - vec4f(3, 2, -1, 1)) <= ((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - vec4f(-2, 1, 3, 1)) <= ((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1)) - (mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs(((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0, 1).x, vec3f(-1, 0, 1).y, vec3f(-1, 0, 1).z, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(0, 2, 4, 1)) <= (((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0, 1).x, vec3f(-1, 0, 1).y, vec3f(-1, 0, 1).z, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(-1, 0, 1).x, vec3f(-1, 0, 1).y, vec3f(-1, 0, 1).z, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs(((mat4x4f(vec3f(-1, 0, 1).x, 0, 0, 0, 0, vec3f(-1, 0, 1).y, 0, 0, 0, 0, vec3f(-1, 0, 1).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(-1, 0, 3, 1)) <= (((mat4x4f(vec3f(-1, 0, 1).x, 0, 0, 0, 0, vec3f(-1, 0, 1).y, 0, 0, 0, 0, vec3f(-1, 0, 1).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(vec3f(-1, 0, 1).x, 0, 0, 0, 0, vec3f(-1, 0, 1).y, 0, 0, 0, 0, vec3f(-1, 0, 1).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs(((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(1, -3, 2, 1)) <= (((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs(((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(3, 2, -1, 1)) <= (((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); + s = (s && all(abs(((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - vec4f(-2, 1, 3, 1)) <= (((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1)) - ((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)) * vec4f(1, 2, 3, 1))) + 0.01f)); s = (s && all(abs(((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * (mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(1, 0, 0, 1)) - vec4f(0, 1, 0, 1)) <= (((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * (mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(1, 0, 0, 1)) - ((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * (mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(1, 0, 0, 1))) + 0.01f)); s = (s && all(abs(((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * (mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(1, 0, 0, 1)) - vec4f(0, 0, 1, 1)) <= (((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * (mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(1, 0, 0, 1)) - ((mat4x4f(1, 0, 0, 0, 0, cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1) * (mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(1, 0, 0, 1))) + 0.01f)); s = (s && all(abs(((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(0, 1, 0).x, vec3f(0, 1, 0).y, vec3f(0, 1, 0).z, 1) * (mat4x4f(vec3f(2, 3, 4).x, 0, 0, 0, 0, vec3f(2, 3, 4).y, 0, 0, 0, 0, vec3f(2, 3, 4).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(1, 0, 0, 1)) - vec4f(2, 1, 0, 1)) <= (((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(0, 1, 0).x, vec3f(0, 1, 0).y, vec3f(0, 1, 0).z, 1) * (mat4x4f(vec3f(2, 3, 4).x, 0, 0, 0, 0, vec3f(2, 3, 4).y, 0, 0, 0, 0, vec3f(2, 3, 4).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(1, 0, 0, 1)) - ((mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(0, 1, 0).x, vec3f(0, 1, 0).y, vec3f(0, 1, 0).z, 1) * (mat4x4f(vec3f(2, 3, 4).x, 0, 0, 0, 0, vec3f(2, 3, 4).y, 0, 0, 0, 0, vec3f(2, 3, 4).z, 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(1, 0, 0, 1))) + 0.01f)); s = (s && all(abs(((mat4x4f(vec3f(2, 3, 4).x, 0, 0, 0, 0, vec3f(2, 3, 4).y, 0, 0, 0, 0, vec3f(2, 3, 4).z, 0, 0, 0, 0, 1) * (mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(0, 1, 0).x, vec3f(0, 1, 0).y, vec3f(0, 1, 0).z, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(0, 0, 0, 1)) - vec4f(0, 3, 0, 1)) <= (((mat4x4f(vec3f(2, 3, 4).x, 0, 0, 0, 0, vec3f(2, 3, 4).y, 0, 0, 0, 0, vec3f(2, 3, 4).z, 0, 0, 0, 0, 1) * (mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(0, 1, 0).x, vec3f(0, 1, 0).y, vec3f(0, 1, 0).z, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(0, 0, 0, 1)) - ((mat4x4f(vec3f(2, 3, 4).x, 0, 0, 0, 0, vec3f(2, 3, 4).y, 0, 0, 0, 0, vec3f(2, 3, 4).z, 0, 0, 0, 0, 1) * (mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, vec3f(0, 1, 0).x, vec3f(0, 1, 0).y, vec3f(0, 1, 0).z, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(0, 0, 0, 1))) + 0.01f)); - s = (s && all(abs(((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * (mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(0, 1, 0, 1)) - vec4f(-1, 0f, 0f, 1f)) <= (((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * (mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(0, 1, 0, 1)) - ((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * (mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(0, 1, 0, 1))) + 0.01f)); + s = (s && all(abs(((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * (mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(0, 1, 0, 1)) - vec4f(-1, 0, 0, 1)) <= (((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * (mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(0, 1, 0, 1)) - ((mat4x4f(cos(1.5707963267948966), sin(1.5707963267948966), 0, 0, -sin(1.5707963267948966), cos(1.5707963267948966), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) * (mat4x4f(cos(1.5707963267948966), 0, -sin(1.5707963267948966), 0, 0, 1, 0, 0, sin(1.5707963267948966), 0, cos(1.5707963267948966), 0, 0, 0, 0, 1) * mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1))) * vec4f(0, 1, 0, 1))) + 0.01f)); return s; } @@ -104,7 +104,7 @@ describe('tgsl parsing test example', () => { s = (s && true); s = (s && all(abs(((mat2x2f(1, 2, 3, 4) * 2) * vec2f(1, 10)) - vec2f(62, 84)) <= (((mat2x2f(1, 2, 3, 4) * 2) * vec2f(1, 10)) - ((mat2x2f(1, 2, 3, 4) * 2) * vec2f(1, 10))) + 0.01f)); s = (s && all(abs(((vec2f(1, 10) * mat2x2f(1, 2, 3, 4)) * -1) - vec2f(-21, -43)) <= (((vec2f(1, 10) * mat2x2f(1, 2, 3, 4)) * -1) - ((vec2f(1, 10) * mat2x2f(1, 2, 3, 4)) * -1)) + 0.01f)); - s = (s && all(abs(((vec2f(1, 10) * -1) * mat2x2f(1, 2, 3, 4)) - vec2f(-21, -43)) <= (((vec2f(1, 10) * -1) * mat2x2f(1, 2, 3, 4)) - ((vec2f(1, 10) * -1) * mat2x2f(1, 2, 3, 4))) + 0.01f)); + s = (s && all(abs((vec2f(-1, -10) * mat2x2f(1, 2, 3, 4)) - vec2f(-21, -43)) <= ((vec2f(-1, -10) * mat2x2f(1, 2, 3, 4)) - (vec2f(-1, -10) * mat2x2f(1, 2, 3, 4))) + 0.01f)); s = (s && all((((((vec3f(1, 10, 100) * mat3x3f(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5)) * -1) * mat3x3f(1, 2, 3, 4, 5, 6, 7, 8, 9)) * -1) * mat3x3f(2, 0, 0, 0, 2, 0, 0, 0, 2)) == vec3f(321, 654, 987))); s = (s && all((getVec_7() * getVec_7()) == vec3f(1, 4, 9))); s = (s && true); @@ -125,45 +125,45 @@ describe('tgsl parsing test example', () => { var s = true; var defaultComplexStruct = ComplexStruct_9(); s = (s && (2 == 2)); - s = (s && (defaultComplexStruct.arr[0] == 0i)); - s = (s && (defaultComplexStruct.arr[1] == 0i)); + s = (s && (defaultComplexStruct.arr[0i] == 0i)); + s = (s && (defaultComplexStruct.arr[1i] == 0i)); var defaultComplexArray = array(); s = (s && (3 == 3)); - s = (s && all(defaultComplexArray[0].vec == vec2f())); - s = (s && all(defaultComplexArray[1].vec == vec2f())); - s = (s && all(defaultComplexArray[2].vec == vec2f())); + s = (s && all(defaultComplexArray[0i].vec == vec2f())); + s = (s && all(defaultComplexArray[1i].vec == vec2f())); + s = (s && all(defaultComplexArray[2i].vec == vec2f())); var simpleStruct = SimpleStruct_10(vec2f(1, 2)); var clonedSimpleStruct = simpleStruct; s = (s && all(simpleStruct.vec == clonedSimpleStruct.vec)); - simpleStruct.vec[1] += 1f; + simpleStruct.vec[1i] += 1f; s = (s && !all(simpleStruct.vec == clonedSimpleStruct.vec)); var simpleArray = array(3i, 4i); var clonedSimpleArray = simpleArray; - s = (s && (simpleArray[0] == clonedSimpleArray[0])); - s = (s && (simpleArray[1] == clonedSimpleArray[1])); - simpleArray[1] += 1i; - s = (s && !(simpleArray[1] == clonedSimpleArray[1])); + s = (s && (simpleArray[0i] == clonedSimpleArray[0i])); + s = (s && (simpleArray[1i] == clonedSimpleArray[1i])); + simpleArray[1i] += 1i; + s = (s && !(simpleArray[1i] == clonedSimpleArray[1i])); var complexStruct = ComplexStruct_9(array(5i, 6i)); var clonedComplexStruct = complexStruct; - s = (s && (complexStruct.arr[0] == clonedComplexStruct.arr[0])); - s = (s && (complexStruct.arr[1] == clonedComplexStruct.arr[1])); - complexStruct.arr[1] += 1i; - s = (s && !(complexStruct.arr[1] == clonedComplexStruct.arr[1])); + s = (s && (complexStruct.arr[0i] == clonedComplexStruct.arr[0i])); + s = (s && (complexStruct.arr[1i] == clonedComplexStruct.arr[1i])); + complexStruct.arr[1i] += 1i; + s = (s && !(complexStruct.arr[1i] == clonedComplexStruct.arr[1i])); var complexArray = array(SimpleStruct_10(vec2f(7, 8)), SimpleStruct_10(vec2f(9, 10)), SimpleStruct_10(vec2f(11, 12))); var clonedComplexArray = complexArray; - s = (s && all(complexArray[2].vec == clonedComplexArray[2].vec)); - complexArray[2].vec[1] += 1f; - s = (s && !all(complexArray[2].vec == clonedComplexArray[2].vec)); - var indirectClonedStruct = complexArray[0]; - s = (s && all(indirectClonedStruct.vec == complexArray[0].vec)); + s = (s && all(complexArray[2i].vec == clonedComplexArray[2i].vec)); + complexArray[2i].vec[1i] += 1f; + s = (s && !all(complexArray[2i].vec == clonedComplexArray[2i].vec)); + var indirectClonedStruct = complexArray[0i]; + s = (s && all(indirectClonedStruct.vec == complexArray[0i].vec)); var indirectlyClonedArray = complexStruct.arr; - s = (s && (indirectlyClonedArray[0] == complexStruct.arr[0])); - s = (s && (indirectlyClonedArray[1] == complexStruct.arr[1])); + s = (s && (indirectlyClonedArray[0i] == complexStruct.arr[0i])); + s = (s && (indirectlyClonedArray[1i] == complexStruct.arr[1i])); return s; } fn modifyNumFn_12(ptr: ptr) { - *ptr += 1u; + (*ptr) += 1u; } fn modifyVecFn_13(ptr: ptr) { @@ -178,45 +178,37 @@ describe('tgsl parsing test example', () => { (*ptr).vec.x += 1f; } - var privateNum_16: u32; - - fn modifyNumPrivate_17(ptr: ptr) { - *ptr += 1u; - } - - var privateVec_18: vec2f; - - fn modifyVecPrivate_19(ptr: ptr) { + fn modifyVecPrivate_16(ptr: ptr) { (*ptr).x += 1f; } - var privateStruct_20: SimpleStruct_14; + var privateVec_17: vec2f; - fn modifyStructPrivate_21(ptr: ptr) { + fn modifyStructPrivate_18(ptr: ptr) { (*ptr).vec.x += 1f; } + var privateStruct_19: SimpleStruct_14; + fn pointersTest_11() -> bool { var s = true; var num = 0u; - modifyNumFn_12(&num); + modifyNumFn_12((&num)); s = (s && (num == 1u)); var vec = vec2f(); - modifyVecFn_13(&vec); + modifyVecFn_13((&vec)); s = (s && all(vec == vec2f(1, 0))); var myStruct = SimpleStruct_14(); - modifyStructFn_15(&myStruct); + modifyStructFn_15((&myStruct)); s = (s && all(myStruct.vec == vec2f(1, 0))); - modifyNumPrivate_17(&privateNum_16); - s = (s && (privateNum_16 == 1u)); - modifyVecPrivate_19(&privateVec_18); - s = (s && all(privateVec_18 == vec2f(1, 0))); - modifyStructPrivate_21(&privateStruct_20); - s = (s && all(privateStruct_20.vec == vec2f(1, 0))); + modifyVecPrivate_16((&privateVec_17)); + s = (s && all(privateVec_17 == vec2f(1, 0))); + modifyStructPrivate_18((&privateStruct_19)); + s = (s && all(privateStruct_19.vec == vec2f(1, 0))); return s; } - @group(0) @binding(0) var result_22: i32; + @group(0) @binding(0) var result_20: i32; @compute @workgroup_size(1) fn computeRunTests_0() { var s = true; @@ -226,10 +218,10 @@ describe('tgsl parsing test example', () => { s = (s && arrayAndStructConstructorsTest_8()); s = (s && pointersTest_11()); if (s) { - result_22 = 1i; + result_20 = 1i; } else { - result_22 = 0i; + result_20 = 0i; } }" `); diff --git a/packages/typegpu/tests/examples/individual/vaporrave.test.ts b/packages/typegpu/tests/examples/individual/vaporrave.test.ts index 91e1d5661f..56068948ef 100644 --- a/packages/typegpu/tests/examples/individual/vaporrave.test.ts +++ b/packages/typegpu/tests/examples/individual/vaporrave.test.ts @@ -32,19 +32,19 @@ describe('vaporrave example', () => { } fn item_9() -> f32 { - var a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); - var b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); + let a = dot(seed_7, vec2f(23.140779495239258, 232.6168975830078)); + let b = dot(seed_7, vec2f(54.47856521606445, 345.8415222167969)); seed_7.x = fract((cos(a) * 136.8168f)); seed_7.y = fract((cos(b) * 534.7645f)); return seed_7.y; } fn randOnUnitSphere_8() -> vec3f { - var z = ((2f * item_9()) - 1f); - var oneMinusZSq = sqrt((1f - (z * z))); - var theta = (6.283185307179586f * item_9()); - var x = (cos(theta) * oneMinusZSq); - var y = (sin(theta) * oneMinusZSq); + let z = ((2f * item_9()) - 1f); + let oneMinusZSq = sqrt((1f - (z * z))); + let theta = (6.283185307179586f * item_9()); + let x = (cos(theta) * oneMinusZSq); + let y = (sin(theta) * oneMinusZSq); return vec3f(x, y, z); } @@ -54,7 +54,7 @@ describe('vaporrave example', () => { } fn wrappedCallback_2(x: u32, y: u32, z: u32) { - var idx = ((x + (y * 7u)) + ((z * 7u) * 7u)); + let idx = ((x + (y * 7u)) + ((z * 7u) * 7u)); memoryBuffer_3[idx] = computeJunctionGradient_4(vec3i(i32(x), i32(y), i32(z))); } @@ -79,7 +79,7 @@ describe('vaporrave example', () => { } @vertex fn vertexMain_0(_arg_0: vertexMain_Input_2) -> vertexMain_Output_1 { - var pos = array(vec2f(-1, -1), vec2f(3f, -1), vec2f(-1, 3f)); + var pos = array(vec2f(-1), vec2f(3, -1), vec2f(-1, 3)); var uv = array(vec2f(), vec2f(2, 0), vec2f(0, 2)); return vertexMain_Output_1(vec4f(pos[_arg_0.idx], 0f, 1f), uv[_arg_0.idx]); } @@ -92,15 +92,15 @@ describe('vaporrave example', () => { } fn rotateXY_9(angle: f32) -> mat2x2f { - return mat2x2f(vec2f(cos(angle), sin(angle)), vec2f(-sin(angle), cos(angle))); + return mat2x2f(vec2f(cos(angle), sin(angle)), vec2f(-(sin(angle)), cos(angle))); } fn circles_8(uv: vec2f, angle: f32) -> vec3f { var uvRotated = (rotateXY_9(angle) * vec2f(uv.x, (uv.y - 12f))); var uvNormalized = fract((vec2f(uvRotated.x, uvRotated.y) / 1.2)); var diff2 = pow((vec2f(0.5) - uvNormalized), vec2f(2)); - var distO = pow((diff2.x + diff2.y), 0.5f); - return mix(vec3f(), vec3f(0.9200000166893005, 0.20999999344348907, 0.9599999785423279), exp((-5 * distO))); + let distO = pow((diff2.x + diff2.y), 0.5f); + return mix(vec3f(), vec3f(0.9200000166893005, 0.20999999344348907, 0.9599999785423279), exp((-5f * distO))); } @group(0) @binding(1) var floorAngleUniform_10: f32; @@ -110,11 +110,11 @@ describe('vaporrave example', () => { } fn rotateAroundZ_13(angle: f32) -> mat3x3f { - return mat3x3f(vec3f(cos(angle), sin(angle), 0f), vec3f(-sin(angle), cos(angle), 0f), vec3f(0, 0, 1)); + return mat3x3f(vec3f(cos(angle), sin(angle), 0f), vec3f(-(sin(angle)), cos(angle), 0f), vec3f(0, 0, 1)); } fn rotateAroundX_14(angle: f32) -> mat3x3f { - return mat3x3f(vec3f(1, 0, 0), vec3f(0f, cos(angle), sin(angle)), vec3f(0f, -sin(angle), cos(angle))); + return mat3x3f(vec3f(1, 0, 0), vec3f(0f, cos(angle), sin(angle)), vec3f(0f, -(sin(angle)), cos(angle))); } fn sdSphere_15(p: vec3f, radius: f32) -> f32 { @@ -125,9 +125,9 @@ describe('vaporrave example', () => { fn getJunctionGradient_18(pos: vec3i) -> vec3f { var size_i = vec3i(7); - var x = (((pos.x % size_i.x) + size_i.x) % size_i.x); - var y = (((pos.y % size_i.y) + size_i.y) % size_i.y); - var z = (((pos.z % size_i.z) + size_i.z) % size_i.z); + let x = (((pos.x % size_i.x) + size_i.x) % size_i.x); + let y = (((pos.y % size_i.y) + size_i.y) % size_i.y); + let z = (((pos.z % size_i.z) + size_i.z) % size_i.z); return memoryBuffer_19[((x + (y * size_i.x)) + ((z * size_i.x) * size_i.y))]; } @@ -143,32 +143,32 @@ describe('vaporrave example', () => { fn sample_16(pos: vec3f) -> f32 { var minJunction = floor(pos); - var xyz = dotProdGrid_17(pos, minJunction); - var xyZ = dotProdGrid_17(pos, (minJunction + vec3f(0, 0, 1))); - var xYz = dotProdGrid_17(pos, (minJunction + vec3f(0, 1, 0))); - var xYZ = dotProdGrid_17(pos, (minJunction + vec3f(0, 1, 1))); - var Xyz = dotProdGrid_17(pos, (minJunction + vec3f(1, 0, 0))); - var XyZ = dotProdGrid_17(pos, (minJunction + vec3f(1, 0, 1))); - var XYz = dotProdGrid_17(pos, (minJunction + vec3f(1, 1, 0))); - var XYZ = dotProdGrid_17(pos, (minJunction + vec3f(1))); + let xyz = dotProdGrid_17(pos, minJunction); + let xyZ = dotProdGrid_17(pos, (minJunction + vec3f(0, 0, 1))); + let xYz = dotProdGrid_17(pos, (minJunction + vec3f(0, 1, 0))); + let xYZ = dotProdGrid_17(pos, (minJunction + vec3f(0, 1, 1))); + let Xyz = dotProdGrid_17(pos, (minJunction + vec3f(1, 0, 0))); + let XyZ = dotProdGrid_17(pos, (minJunction + vec3f(1, 0, 1))); + let XYz = dotProdGrid_17(pos, (minJunction + vec3f(1, 1, 0))); + let XYZ = dotProdGrid_17(pos, (minJunction + vec3f(1))); var partial = (pos - minJunction); var smoothPartial = quinticInterpolationImpl_20(partial); - var xy = mix(xyz, xyZ, smoothPartial.z); - var xY = mix(xYz, xYZ, smoothPartial.z); - var Xy = mix(Xyz, XyZ, smoothPartial.z); - var XY = mix(XYz, XYZ, smoothPartial.z); - var x = mix(xy, xY, smoothPartial.y); - var X = mix(Xy, XY, smoothPartial.y); + let xy = mix(xyz, xyZ, smoothPartial.z); + let xY = mix(xYz, xYZ, smoothPartial.z); + let Xy = mix(Xyz, XyZ, smoothPartial.z); + let XY = mix(XYz, XYZ, smoothPartial.z); + let x = mix(xy, xY, smoothPartial.y); + let X = mix(Xy, XY, smoothPartial.y); return mix(x, X, smoothPartial.x); } fn getSphere_12(p: vec3f, sphereColor: vec3f, sphereCenter: vec3f, angle: f32) -> Ray_6 { var localP = (p - sphereCenter); - var rotMatZ = rotateAroundZ_13((-angle * 0.3f)); - var rotMatX = rotateAroundX_14((-angle * 0.7f)); + var rotMatZ = rotateAroundZ_13((-(angle) * 0.3f)); + var rotMatX = rotateAroundX_14((-(angle) * 0.7f)); var rotatedP = ((localP * rotMatZ) * rotMatX); - var radius = (3f + sin(angle)); - var rawDist = sdSphere_15(rotatedP, radius); + let radius = (3f + sin(angle)); + let rawDist = sdSphere_15(rotatedP, radius); var noise = 0f; if ((rawDist < 1f)) { noise += sample_16((rotatedP + angle)); @@ -203,7 +203,7 @@ describe('vaporrave example', () => { var p = ((rd * distOrigin) + ro); var scene = getSceneRay_7(p); var sphereDist = getSphere_12(p, sphereColorUniform_21, vec3f(0, 6, 12), sphereAngleUniform_22); - glow = ((vec3f(sphereColorUniform_21) * exp(-sphereDist.dist)) + glow); + glow = ((sphereColorUniform_21 * exp(-(sphereDist.dist))) + glow); distOrigin += scene.dist; if ((distOrigin > 19f)) { result.dist = 19f; @@ -227,12 +227,12 @@ describe('vaporrave example', () => { @fragment fn fragmentMain_3(input: fragmentMain_Input_26) -> @location(0) vec4f { var uv = ((input.uv * 2) - 1); uv.x *= (resolutionUniform_4.x / resolutionUniform_4.y); - var ro = vec3f(0f, 2f, -1); + var ro = vec3f(0, 2, -1); var rd = normalize(vec3f(uv.x, uv.y, 1f)); var march = rayMarch_5(ro, rd); - var y = (((rd * march.ray.dist) + ro).y - 2f); + let y = (((rd * march.ray.dist) + ro).y - 2f); var sky = mix(vec4f(0.10000000149011612, 0, 0.20000000298023224, 1), vec4f(0.2800000011920929, 0, 0.5400000214576721, 1), (y / 19f)); - var fog = min((march.ray.dist / 19f), 1f); + let fog = min((march.ray.dist / 19f), 1f); return mix(mix(vec4f(march.ray.color, 1f), sky, fog), vec4f(march.glow, 1f), glowIntensityUniform_25); }" `); diff --git a/packages/typegpu/tests/examples/individual/wgsl-resolution.test.ts b/packages/typegpu/tests/examples/individual/wgsl-resolution.test.ts index f77c91278d..1719364b75 100644 --- a/packages/typegpu/tests/examples/individual/wgsl-resolution.test.ts +++ b/packages/typegpu/tests/examples/individual/wgsl-resolution.test.ts @@ -19,12 +19,12 @@ describe('wgsl resolution example', () => { expect(wgslElement.innerText).toMatchInlineSnapshot(` "fn get_rotation_from_velocity_util_1(velocity: vec2f) -> f32 { - return -atan2(velocity.x, velocity.y); + return -(atan2(velocity.x, velocity.y)); } fn rotate_util_2(v: vec2f, angle: f32) -> vec2f { - var cos = cos(angle); - var sin = sin(angle); + let cos = cos(angle); + let sin = sin(angle); return vec2f(((v.x * cos) - (v.y * sin)), ((v.x * sin) + (v.y * cos))); } @@ -42,7 +42,7 @@ describe('wgsl resolution example', () => { } @vertex fn vertex_shader_0(input: vertex_shader_Input_5) -> vertex_shader_Output_4 { - var angle = get_rotation_from_velocity_util_1(input.velocity); + let angle = get_rotation_from_velocity_util_1(input.velocity); var rotated = rotate_util_2(input.v, angle); var pos = vec4f((rotated.x + input.center.x), (rotated.y + input.center.y), 0f, 1f); var color = vec4f(((sin((angle + colorPalette_3.x)) * 0.45f) + 0.45f), ((sin((angle + colorPalette_3.y)) * 0.45f) + 0.45f), ((sin((angle + colorPalette_3.z)) * 0.45f) + 0.45f), 1f); @@ -83,8 +83,8 @@ describe('wgsl resolution example', () => { } @compute @workgroup_size(1) fn compute_shader_8(input: compute_shader_Input_14) { - var index = input.gid.x; - var instanceInfo = currentTrianglePos_9[index]; + let index = input.gid.x; + let instanceInfo = (¤tTrianglePos_9[index]); var separation = vec2f(); var alignment = vec2f(); var cohesion = vec2f(); @@ -94,17 +94,17 @@ describe('wgsl resolution example', () => { if ((i == index)) { continue; } - var other = currentTrianglePos_9[i]; - var dist = distance(instanceInfo.position, other.position); + let other = (¤tTrianglePos_9[i]); + let dist = distance((*instanceInfo).position, (*other).position); if ((dist < paramsBuffer_11.separationDistance)) { - separation = (separation + (instanceInfo.position - other.position)); + separation = (separation + ((*instanceInfo).position - (*other).position)); } if ((dist < paramsBuffer_11.alignmentDistance)) { - alignment = (alignment + other.velocity); + alignment = (alignment + (*other).velocity); alignmentCount++; } if ((dist < paramsBuffer_11.cohesionDistance)) { - cohesion = (cohesion + other.position); + cohesion = (cohesion + (*other).position); cohesionCount++; } } @@ -113,27 +113,27 @@ describe('wgsl resolution example', () => { } if ((cohesionCount > 0i)) { cohesion = ((1f / f32(cohesionCount)) * cohesion); - cohesion = (cohesion - instanceInfo.position); + cohesion = (cohesion - (*instanceInfo).position); } var velocity = (paramsBuffer_11.separationStrength * separation); velocity = (velocity + (paramsBuffer_11.alignmentStrength * alignment)); velocity = (velocity + (paramsBuffer_11.cohesionStrength * cohesion)); - instanceInfo.velocity = (instanceInfo.velocity + velocity); - instanceInfo.velocity = (clamp(length(instanceInfo.velocity), 0f, 0.01f) * normalize(instanceInfo.velocity)); - if ((instanceInfo.position.x > 1.03f)) { - instanceInfo.position.x = (-1 - 0.03); + (*instanceInfo).velocity = ((*instanceInfo).velocity + velocity); + (*instanceInfo).velocity = (clamp(length((*instanceInfo).velocity), 0f, 0.01f) * normalize((*instanceInfo).velocity)); + if (((*instanceInfo).position.x > 1.03f)) { + (*instanceInfo).position.x = -1.03f; } - if ((instanceInfo.position.y > 1.03f)) { - instanceInfo.position.y = (-1 - 0.03); + if (((*instanceInfo).position.y > 1.03f)) { + (*instanceInfo).position.y = -1.03f; } - if ((instanceInfo.position.x < (-1 - 0.03))) { - instanceInfo.position.x = 1.03f; + if (((*instanceInfo).position.x < -1.03f)) { + (*instanceInfo).position.x = 1.03f; } - if ((instanceInfo.position.y < (-1 - 0.03))) { - instanceInfo.position.y = 1.03f; + if (((*instanceInfo).position.y < -1.03f)) { + (*instanceInfo).position.y = 1.03f; } - instanceInfo.position = (instanceInfo.position + instanceInfo.velocity); - nextTrianglePos_13[index] = instanceInfo; + (*instanceInfo).position = ((*instanceInfo).position + (*instanceInfo).velocity); + nextTrianglePos_13[index] = (*instanceInfo); }" `); }); diff --git a/packages/typegpu/tests/examples/individual/xor-dev-centrifuge-2.test.ts b/packages/typegpu/tests/examples/individual/xor-dev-centrifuge-2.test.ts index 9f76bf7177..51449ca70b 100644 --- a/packages/typegpu/tests/examples/individual/xor-dev-centrifuge-2.test.ts +++ b/packages/typegpu/tests/examples/individual/xor-dev-centrifuge-2.test.ts @@ -27,7 +27,7 @@ describe('xor dev centrifuge example', () => { } @vertex fn vertexMain_0(input: vertexMain_Input_2) -> vertexMain_Output_1 { - var pos = array(vec2f(-1, -1), vec2f(3f, -1), vec2f(-1, 3f)); + var pos = array(vec2f(-1), vec2f(3, -1), vec2f(-1, 3)); return vertexMain_Output_1(vec4f(pos[input.vertexIndex], 0f, 1f), pos[input.vertexIndex]); } @@ -57,7 +57,7 @@ describe('xor dev centrifuge example', () => { @fragment fn fragmentMain_3(_arg_0: fragmentMain_Input_13) -> @location(0) vec4f { var ratio = vec2f(aspectRatio_4, 1f); - var dir = normalize(vec3f((_arg_0.uv * ratio), -1)); + var dir = normalize(vec3f((_arg_0.uv * ratio), -1f)); var z = 0f; var acc = vec3f(); for (var i = 0; (i < tunnelDepth_5); i++) { @@ -66,7 +66,7 @@ describe('xor dev centrifuge example', () => { p.y += cameraPos_6.y; var coords = vec3f(((atan2(p.y, p.x) * bigStrips_7) + time_8), ((p.z * dollyZoom_9) - (5f * time_8)), (length(p.xy) - 11f)); var coords2 = (cos((coords + cos((coords * smallStrips_10)))) - 1); - var dd = ((length(vec4f(coords.z, coords2)) * 0.5f) - 0.1f); + let dd = ((length(vec4f(coords.z, coords2)) * 0.5f) - 0.1f); acc = (acc + ((1.2 - cos((color_11 * p.z))) / dd)); z += dd; } diff --git a/packages/typegpu/tests/examples/individual/xor-dev-runner.test.ts b/packages/typegpu/tests/examples/individual/xor-dev-runner.test.ts index 2138adb205..b00b988745 100644 --- a/packages/typegpu/tests/examples/individual/xor-dev-runner.test.ts +++ b/packages/typegpu/tests/examples/individual/xor-dev-runner.test.ts @@ -27,7 +27,7 @@ describe('xor dev runner example', () => { } @vertex fn vertexMain_0(input: vertexMain_Input_2) -> vertexMain_Output_1 { - var pos = array(vec2f(-1, -1), vec2f(3f, -1), vec2f(-1, 3f)); + var pos = array(vec2f(-1), vec2f(3, -1), vec2f(-1, 3)); return vertexMain_Output_1(vec4f(pos[input.vertexIndex], 0f, 1f), pos[input.vertexIndex]); } @@ -44,7 +44,7 @@ describe('xor dev runner example', () => { } fn rotateXZ_9(angle: f32) -> mat3x3f { - return mat3x3f(vec3f(cos(angle), 0f, sin(angle)), vec3f(0, 1, 0), vec3f(-sin(angle), 0f, cos(angle))); + return mat3x3f(vec3f(cos(angle), 0f, sin(angle)), vec3f(0, 1, 0), vec3f(-(sin(angle)), 0f, cos(angle))); } @group(0) @binding(4) var shift_10: f32; @@ -60,7 +60,7 @@ describe('xor dev runner example', () => { @fragment fn fragmentMain_3(_arg_0: fragmentMain_Input_12) -> @location(0) vec4f { var icolor = (color_4 * 4); var ratio = vec2f(aspectRatio_5, 1f); - var dir = normalize(vec3f((_arg_0.uv * ratio), -1)); + var dir = normalize(vec3f((_arg_0.uv * ratio), -1f)); var acc = vec3f(); var z = 0f; for (var l = 0; (l < 30i); l++) { @@ -71,7 +71,7 @@ describe('xor dev runner example', () => { var prox = p.y; for (var i = 40.1; (i > 0.01f); i *= 0.2f) { q = ((i * 0.9f) - abs((mod_8(q, (i + i)) - i))); - var minQ = min(min(q.x, q.y), q.z); + let minQ = min(min(q.x, q.y), q.z); prox = max(prox, minQ); q = (q * rotateXZ_9(shift_10)); } diff --git a/packages/typegpu/tests/indent.test.ts b/packages/typegpu/tests/indent.test.ts index c0be93d6ab..587b85ee8c 100644 --- a/packages/typegpu/tests/indent.test.ts +++ b/packages/typegpu/tests/indent.test.ts @@ -115,9 +115,9 @@ describe('indents', () => { } fn main_0() { - for (var i = 0; (i < 100); i++) { - var particle = systemData_1.particles[i]; - systemData_1.particles[i] = updateParicle_4(particle, systemData_1.gravity, systemData_1.deltaTime); + for (var i = 0; (i < 100i); i++) { + let particle = (&systemData_1.particles[i]); + systemData_1.particles[i] = updateParicle_4((*particle), systemData_1.gravity, systemData_1.deltaTime); } }" `); @@ -231,7 +231,7 @@ describe('indents', () => { } fn updateParticle_7(particle: Particle_5, gravity: vec3f, deltaTime: f32) -> Particle_5 { - var density = getDensityAt_8(particle.physics.position); + let density = getDensityAt_8(particle.physics.position); var force = (gravity * density); var newVelocity = (particle.physics.velocity + (force * deltaTime)); var newPosition = (particle.physics.position + (newVelocity * deltaTime)); @@ -240,9 +240,9 @@ describe('indents', () => { fn main_0() { incrementCounter_1(); - for (var i = 0; (i < 100); i++) { - var particle = systemData_3.particles[i]; - systemData_3.particles[i] = updateParticle_7(particle, systemData_3.gravity, systemData_3.deltaTime); + for (var i = 0; (i < 100i); i++) { + let particle = (&systemData_3.particles[i]); + systemData_3.particles[i] = updateParticle_7((*particle), systemData_3.gravity, systemData_3.deltaTime); } }" `); @@ -258,12 +258,13 @@ describe('indents', () => { [Particle, d.vec3f], Particle, )((particle, gravity) => { - if (particle.velocity.x > 0) { - particle.position = std.add(particle.position, particle.velocity); + const newParticle = Particle(particle); + if (newParticle.velocity.x > 0) { + newParticle.position = newParticle.position.add(newParticle.velocity); } else { - particle.position = std.add(particle.position, gravity); + newParticle.position = newParticle.position.add(gravity); } - return particle; + return newParticle; }); const code = tgpu.resolve({ externals: { updateParticle } }); @@ -274,13 +275,14 @@ describe('indents', () => { } fn updateParticle_0(particle: Particle_1, gravity: vec3f) -> Particle_1 { - if ((particle.velocity.x > 0f)) { - particle.position = (particle.position + particle.velocity); + var newParticle = particle; + if ((newParticle.velocity.x > 0f)) { + newParticle.position = (newParticle.position + newParticle.velocity); } else { - particle.position = (particle.position + gravity); + newParticle.position = (newParticle.position + gravity); } - return particle; + return newParticle; }" `); }); @@ -295,15 +297,18 @@ describe('indents', () => { [Particle, d.vec3f], Particle, )((particle, gravity) => { + const newParticle = Particle(particle); let iterations = 0; while (iterations < 10) { - particle.position = std.add(particle.position, particle.velocity); + newParticle.position = newParticle.position.add( + newParticle.velocity, + ); iterations += 1; - while (particle.position.x < 0) { - particle.position = std.add(particle.position, gravity); + while (newParticle.position.x < 0) { + newParticle.position = newParticle.position.add(gravity); } } - return particle; + return newParticle; }); const code = tgpu.resolve({ externals: { updateParticle } }); @@ -314,15 +319,16 @@ describe('indents', () => { } fn updateParticle_0(particle: Particle_1, gravity: vec3f) -> Particle_1 { + var newParticle = particle; var iterations = 0; while ((iterations < 10i)) { - particle.position = (particle.position + particle.velocity); + newParticle.position = (newParticle.position + newParticle.velocity); iterations += 1i; - while ((particle.position.x < 0f)) { - particle.position = (particle.position + gravity); + while ((newParticle.position.x < 0f)) { + newParticle.position = (newParticle.position + gravity); } } - return particle; + return newParticle; }" `); }); @@ -411,16 +417,16 @@ describe('indents', () => { } @vertex fn someVertex_0(input: someVertex_Input_7) -> someVertex_Output_6 { - var uniBoid = boids_1; + let uniBoid = (&boids_1); for (var i = 0u; (i < -1u); i++) { var sampled = textureSample(sampled_3, sampler_4, vec2f(0.5), i); var someVal = textureLoad(smoothRender_5, vec2i(), 0); if (((someVal.x + sampled.x) > 0.5f)) { - var newPos = (uniBoid.position + vec4f(1, 2, 3, 4)); + var newPos = ((*uniBoid).position + vec4f(1, 2, 3, 4)); } else { while (true) { - var newPos = (uniBoid.position + vec4f(1, 2, 3, 4)); + var newPos = ((*uniBoid).position + vec4f(1, 2, 3, 4)); if ((newPos.x > 0f)) { var evenNewer = (newPos + input.position); } diff --git a/packages/typegpu/tests/namespace.test.ts b/packages/typegpu/tests/namespace.test.ts index e71e436c79..b587709e18 100644 --- a/packages/typegpu/tests/namespace.test.ts +++ b/packages/typegpu/tests/namespace.test.ts @@ -43,7 +43,7 @@ describe('tgpu.namespace', () => { }); const updateBoid = tgpu.fn([d.ptrFn(Boid)])((boid) => { - boid.pos.x += 1; + boid.$.pos.x += 1; }); const names = tgpu['~unstable'].namespace(); diff --git a/packages/typegpu/tests/numeric.test.ts b/packages/typegpu/tests/numeric.test.ts index 67e77cdade..d06a010fe3 100644 --- a/packages/typegpu/tests/numeric.test.ts +++ b/packages/typegpu/tests/numeric.test.ts @@ -72,11 +72,11 @@ describe('TGSL', () => { expect(asWgsl(main)).toMatchInlineSnapshot(` "fn main() { - var f = 0f; - var h = 0h; - var i = 0i; - var u = 0u; - var b = false; + const f = 0f; + const h = 0h; + const i = 0i; + const u = 0u; + const b = false; }" `); }); diff --git a/packages/typegpu/tests/ref.test.ts b/packages/typegpu/tests/ref.test.ts new file mode 100644 index 0000000000..f0b42be186 --- /dev/null +++ b/packages/typegpu/tests/ref.test.ts @@ -0,0 +1,248 @@ +import tgpu from '../src/index.ts'; +import * as d from '../src/data/index.ts'; +import { describe, expect } from 'vitest'; +import { it } from './utils/extendedIt.ts'; +import { asWgsl } from './utils/parseResolved.ts'; + +describe('d.ref', () => { + it('fails when using a ref as an external', () => { + const sup = d.ref(0); + + const foo = () => { + 'use gpu'; + sup.$ += 1; + }; + + expect(() => asWgsl(foo)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:foo + - fn*:foo(): Cannot use refs (d.ref(...)) from the outer scope.] + `); + }); + + it('creates a regular looking variable in WGSL', () => { + const hello = () => { + 'use gpu'; + const ref = d.ref(0); + }; + + expect(asWgsl(hello)).toMatchInlineSnapshot(` + "fn hello() { + var ref_1 = 0; + }" + `); + }); + + it('fails when trying to assign a ref to an existing variable', () => { + const update = (value: d.ref) => { + 'use gpu'; + value.$ += 1; + }; + + const hello = () => { + 'use gpu'; + let foo = d.ref(0); + update(foo); + // Nuh-uh + foo = d.ref(1); + update(foo); + }; + + expect(() => asWgsl(hello)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:hello + - fn*:hello(): Cannot assign a ref to an existing variable '(&foo)', define a new variable instead.] + `); + }); + + it('fails when creating a ref with a reference, and assigning it to a variable', () => { + const hello = () => { + 'use gpu'; + const position = d.vec3f(1, 2, 3); + const foo = d.ref(position); + }; + + expect(() => asWgsl(hello)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:hello + - fn*:hello(): Cannot store d.ref() in a variable if it references another value. Copy the value passed into d.ref() instead.] + `); + }); + + it('allows updating a whole struct from another function', () => { + type Entity = d.Infer; + const Entity = d.struct({ pos: d.vec3f }); + + const clearEntity = (entity: d.ref) => { + 'use gpu'; + entity.$ = Entity({ pos: d.vec3f(0, 0, 0) }); + }; + + const main = () => { + 'use gpu'; + const entity = Entity({ pos: d.vec3f(1, 2, 3) }); + clearEntity(d.ref(entity)); + // entity.pos should be vec3f(0, 0, 0) + return entity; + }; + + // Works in JS + expect(main().pos).toStrictEqual(d.vec3f(0, 0, 0)); + + // And on the GPU + expect(asWgsl(main)).toMatchInlineSnapshot(` + "struct Entity { + pos: vec3f, + } + + fn clearEntity(entity: ptr) { + (*entity) = Entity(vec3f()); + } + + fn main() -> Entity { + var entity = Entity(vec3f(1, 2, 3)); + clearEntity((&entity)); + return entity; + }" + `); + }); + + it('allows updating a number from another function', () => { + const increment = (value: d.ref) => { + 'use gpu'; + value.$ += 1; + }; + + const main = () => { + 'use gpu'; + const value = d.ref(0); + increment(value); + return value.$; + }; + + // Works in JS + expect(main()).toBe(1); + + // And on the GPU + expect(asWgsl(main)).toMatchInlineSnapshot(` + "fn increment(value: ptr) { + (*value) += 1i; + } + + fn main() -> i32 { + var value = 0; + increment((&value)); + return value; + }" + `); + }); + + it('rejects passing d.ref created from non-refs directly into functions', () => { + const increment = (value: d.ref) => { + 'use gpu'; + value.$ += 1; + }; + + const main = () => { + 'use gpu'; + increment(d.ref(0)); + }; + + expect(() => asWgsl(main)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:main + - fn*:main(): d.ref() created with primitive types must be stored in a variable before use] + `); + }); + + it('fails when returning a ref', () => { + const foo = () => { + 'use gpu'; + const value = d.ref(0); + return value; + }; + + const bar = () => { + 'use gpu'; + return d.ref(0); + }; + + expect(() => asWgsl(foo)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:foo + - fn*:foo(): Cannot return references, returning 'value'] + `); + + expect(() => asWgsl(bar)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:bar + - fn*:bar(): Cannot return references, returning '0'] + `); + }); + + it('fails when taking a reference of an argument', () => { + const advance = (value: d.ref) => { + 'use gpu'; + value.$.x += 1; + }; + + const foo = (hello: d.v3f) => { + 'use gpu'; + // Trying to cheat and mutate a non-ref argument by taking a reference of it here. + advance(d.ref(hello)); + }; + + const main = () => { + 'use gpu'; + foo(d.vec3f()); + }; + + expect(() => asWgsl(main)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:main + - fn*:main() + - fn*:foo(vec3f) + - fn:ref: d.ref(hello) is illegal, cannot take a reference of an argument. Copy the value locally first, and take a reference of the copy.] + `); + }); + + it('turns an implicit pointer into an explicit one', () => { + const layout = tgpu.bindGroupLayout({ + positions: { storage: d.arrayOf(d.vec3f) }, + }); + + const advance = (value: d.ref) => { + 'use gpu'; + value.$.x += 1; + }; + + const main = () => { + 'use gpu'; + // biome-ignore lint/style/noNonNullAssertion: it's there + const pos = layout.$.positions[0]!; + advance(d.ref(pos)); + d.ref(pos); + }; + + expect(asWgsl(main)).toMatchInlineSnapshot(` + "@group(0) @binding(0) var positions: array; + + fn advance(value: ptr) { + (*value).x += 1f; + } + + fn main() { + let pos = (&positions[0i]); + advance(pos); + pos; + }" + `); + }); +}); diff --git a/packages/typegpu/tests/resolve.test.ts b/packages/typegpu/tests/resolve.test.ts index 424292b5e6..74e97d77ef 100644 --- a/packages/typegpu/tests/resolve.test.ts +++ b/packages/typegpu/tests/resolve.test.ts @@ -57,7 +57,7 @@ describe('tgpu resolve', () => { [$gpuValueOf]: { [$internal]: true, get [$ownSnippet]() { - return snip(this, d.f32); + return snip(this, d.f32, /* ref */ 'runtime'); }, [$resolve]: (ctx: ResolutionCtx) => ctx.resolve(intensity), } as unknown as number, @@ -67,7 +67,7 @@ describe('tgpu resolve', () => { ctx.addDeclaration( `@group(0) @binding(0) var ${name}: f32;`, ); - return snip(name, d.f32); + return snip(name, d.f32, /* ref */ 'runtime'); }, get value(): number { diff --git a/packages/typegpu/tests/slot.test.ts b/packages/typegpu/tests/slot.test.ts index a9a70bee84..53fd6983c3 100644 --- a/packages/typegpu/tests/slot.test.ts +++ b/packages/typegpu/tests/slot.test.ts @@ -317,11 +317,11 @@ describe('tgpu.slot', () => { fn func() { var pos = vec3f(1, 2, 3); - var posX = 1; - var vel = boid.vel; - var velX = boid.vel.x; - var vel_ = boid.vel; - var velX_ = boid.vel.x; + const posX = 1f; + let vel = (&boid.vel); + let velX = boid.vel.x; + let vel_ = (&boid.vel); + let velX_ = boid.vel.x; var color = getColor(); }" `); diff --git a/packages/typegpu/tests/std/matrix/rotate.test.ts b/packages/typegpu/tests/std/matrix/rotate.test.ts index d3f53117d8..8e400aef7b 100644 --- a/packages/typegpu/tests/std/matrix/rotate.test.ts +++ b/packages/typegpu/tests/std/matrix/rotate.test.ts @@ -16,7 +16,7 @@ describe('rotate', () => { expect(asWgsl(rotateFn)).toMatchInlineSnapshot(` "fn rotateFn() { - var angle = 4; + const 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) * mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1)); }" `); @@ -32,7 +32,7 @@ describe('rotate', () => { expect(asWgsl(rotateFn)).toMatchInlineSnapshot(` "fn rotateFn() { - var angle = 4; + const 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) * mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1)); }" `); @@ -48,7 +48,7 @@ describe('rotate', () => { expect(asWgsl(rotateFn)).toMatchInlineSnapshot(` "fn rotateFn() { - var angle = 4; + const 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) * mat4x4f(1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1)); }" `); diff --git a/packages/typegpu/tests/struct.test.ts b/packages/typegpu/tests/struct.test.ts index 4a72f1de7d..15a69581e7 100644 --- a/packages/typegpu/tests/struct.test.ts +++ b/packages/typegpu/tests/struct.test.ts @@ -371,7 +371,7 @@ describe('struct', () => { fn testFn() { var myStructs = array(TestStruct(1u, 2f)); - var myClone = myStructs[0]; + var myClone = myStructs[0i]; return; }" `); diff --git a/packages/typegpu/tests/tgsl/argumentOrigin.test.ts b/packages/typegpu/tests/tgsl/argumentOrigin.test.ts new file mode 100644 index 0000000000..25a0d930d4 --- /dev/null +++ b/packages/typegpu/tests/tgsl/argumentOrigin.test.ts @@ -0,0 +1,148 @@ +import { describe, expect } from 'vitest'; +import * as d from '../../src/data/index.ts'; +import { it } from '../utils/extendedIt.ts'; +import { asWgsl } from '../utils/parseResolved.ts'; + +describe('function argument origin tracking', () => { + it('should allow mutation of primitive arguments', () => { + const foo = (a: number) => { + 'use gpu'; + a += 1; + }; + + const main = () => { + 'use gpu'; + foo(1); + }; + + expect(asWgsl(main)).toMatchInlineSnapshot(` + "fn foo(a: i32) { + a += 1i; + } + + fn main() { + foo(1i); + }" + `); + }); + + it('should allow mutation of destructured primitive arguments', () => { + const Foo = d.struct({ a: d.f32 }); + + const foo = ({ a }: { a: number }) => { + 'use gpu'; + a += 1; + }; + + const main = () => { + 'use gpu'; + foo(Foo({ a: 1 })); + }; + + expect(asWgsl(main)).toMatchInlineSnapshot(` + "struct Foo { + a: f32, + } + + fn foo(_arg_0: Foo) { + _arg_0.a += 1f; + } + + fn main() { + foo(Foo(1f)); + }" + `); + }); + + it('should fail on mutation of non-primitive arguments', () => { + const foo = (a: d.v3f) => { + 'use gpu'; + a.x += 1; + }; + + const main = () => { + 'use gpu'; + foo(d.vec3f(1, 2, 3)); + }; + + expect(() => asWgsl(main)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:main + - fn*:main() + - fn*:foo(vec3f): 'a.x += 1f' is invalid, because non-pointer arguments cannot be mutated.] + `); + }); + + it('should fail on transitive mutation of non-primitive arguments', () => { + const foo = (a: d.v3f) => { + 'use gpu'; + const b = a; + b.x += 1; + }; + + const main = () => { + 'use gpu'; + foo(d.vec3f(1, 2, 3)); + }; + + expect(() => asWgsl(main)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:main + - fn*:main() + - fn*:foo(vec3f): 'b.x += 1f' is invalid, because non-pointer arguments cannot be mutated.] + `); + }); + + it('should fail on create a let variable from an argument reference', () => { + const foo = (a: d.v3f) => { + 'use gpu'; + let b = a; + b = d.vec3f(); + return b; + }; + + const main = () => { + 'use gpu'; + foo(d.vec3f(1, 2, 3)); + }; + + expect(() => asWgsl(main)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:main + - fn*:main() + - fn*:foo(vec3f): 'let b = a' is invalid, because references to arguments cannot be assigned to 'let' variable declarations. + ----- + - Try 'let b = vec3f(a)' if you need to reassign 'b' later + - Try 'const b = a' if you won't reassign 'b' later. + -----] + `); + }); + + it('should fail on assigning an argument reference to a variable', () => { + const foo = (a: d.v3f) => { + 'use gpu'; + let b = d.vec3f(); + b = a; + return b; + }; + + const main = () => { + 'use gpu'; + foo(d.vec3f(1, 2, 3)); + }; + + expect(() => asWgsl(main)).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:main + - fn*:main() + - fn*:foo(vec3f): 'b = a' is invalid, because argument references cannot be assigned. + ----- + Try 'b = vec3f(a)' to copy the value instead. + -----] + `); + }); +}); diff --git a/packages/typegpu/tests/tgsl/consoleLog.test.ts b/packages/typegpu/tests/tgsl/consoleLog.test.ts index 221b2772c7..001e6a7771 100644 --- a/packages/typegpu/tests/tgsl/consoleLog.test.ts +++ b/packages/typegpu/tests/tgsl/consoleLog.test.ts @@ -33,7 +33,7 @@ describe('wgslGenerator with console.log', () => { `); expect(consoleWarnSpy).toHaveBeenCalledWith( - "'console.log' is currently only supported in compute pipelines.", + "'console.log' is only supported when resolving pipelines.", ); expect(consoleWarnSpy).toHaveBeenCalledTimes(1); }); @@ -601,7 +601,7 @@ describe('wgslGenerator with console.log', () => { - computePipeline:pipeline - computePipelineCore - computeFn:fn - - consoleLog: Logged data needs to fit in 252 bytes (one of the logs requires 256 bytes). Consider increasing the limit by passing appropriate options to tgpu.init().] + - fn:consoleLog: Logged data needs to fit in 252 bytes (one of the logs requires 256 bytes). Consider increasing the limit by passing appropriate options to tgpu.init().] `); }); diff --git a/packages/typegpu/tests/tgsl/conversion.test.ts b/packages/typegpu/tests/tgsl/conversion.test.ts index ff610ab964..9ffa12bdf7 100644 --- a/packages/typegpu/tests/tgsl/conversion.test.ts +++ b/packages/typegpu/tests/tgsl/conversion.test.ts @@ -14,6 +14,7 @@ import { UnknownData } from '../../src/data/dataTypes.ts'; import { ResolutionCtxImpl } from '../../src/resolutionCtx.ts'; import { namespace } from '../../src/core/resolve/namespace.ts'; import wgslGenerator from '../../src/tgsl/wgslGenerator.ts'; +import { INTERNAL_createPtr } from '../../src/data/ptr.ts'; const ctx = new ResolutionCtxImpl({ namespace: namespace({ names: 'strict' }), @@ -30,8 +31,20 @@ afterAll(() => { }); describe('getBestConversion', () => { - const ptrF32 = d.ptrPrivate(d.f32); - const ptrI32 = d.ptrPrivate(d.i32); + // d.ptrPrivate(d.f32) + const ptrF32 = INTERNAL_createPtr( + 'private', + d.f32, + 'read-write', + /* implicit */ true, + ); + // d.ptrPrivate(d.i32) + const ptrI32 = INTERNAL_createPtr( + 'private', + d.i32, + 'read-write', + /* implicit */ true, + ); it('returns result for identical types', () => { const res = getBestConversion([d.f32, d.f32]); @@ -185,13 +198,17 @@ describe('getBestConversion', () => { }); describe('convertToCommonType', () => { - const snippetF32 = snip('2.22', d.f32); - const snippetI32 = snip('-12', d.i32); - const snippetU32 = snip('33', d.u32); - const snippetAbsFloat = snip('1.1', abstractFloat); - const snippetAbsInt = snip('1', abstractInt); - const snippetPtrF32 = snip('ptr_f32', d.ptrPrivate(d.f32)); - const snippetUnknown = snip('?', UnknownData); + const snippetF32 = snip('2.22', d.f32, /* ref */ 'runtime'); + const snippetI32 = snip('-12', d.i32, /* ref */ 'runtime'); + const snippetU32 = snip('33', d.u32, /* ref */ 'runtime'); + const snippetAbsFloat = snip('1.1', abstractFloat, /* ref */ 'runtime'); + const snippetAbsInt = snip('1', abstractInt, /* ref */ 'runtime'); + const snippetPtrF32 = snip( + 'ptr_f32', + INTERNAL_createPtr('private', d.f32, 'read-write', /* implicit */ true), + /* ref */ 'function', + ); + const snippetUnknown = snip('?', UnknownData, /* ref */ 'runtime'); it('converts identical types', () => { const result = convertToCommonType([snippetF32, snippetF32]); @@ -235,13 +252,13 @@ describe('convertToCommonType', () => { expect(result).toBeDefined(); expect(result?.length).toBe(2); expect(result?.[0]?.dataType).toBe(d.f32); - expect(result?.[0]?.value).toBe('*ptr_f32'); // Deref applied + expect(result?.[0]?.value).toBe('(*ptr_f32)'); // Deref applied expect(result?.[1]?.dataType).toBe(d.f32); expect(result?.[1]?.value).toBe('2.22'); }); it('returns undefined for incompatible types', () => { - const snippetVec2f = snip('v2', d.vec2f); + const snippetVec2f = snip('v2', d.vec2f, /* ref */ 'runtime'); const result = convertToCommonType([snippetF32, snippetVec2f]); expect(result).toBeUndefined(); }); @@ -281,7 +298,10 @@ describe('convertToCommonType', () => { }); it('handles void gracefully', () => { - const result = convertToCommonType([snippetF32, snip('void', d.Void)]); + const result = convertToCommonType([ + snippetF32, + snip('void', d.Void, /* ref */ 'runtime'), + ]); expect(result).toBeUndefined(); }); @@ -301,10 +321,10 @@ describe('convertStructValues', () => { it('maps values matching types exactly', () => { const snippets: Record = { - a: snip('1.0', d.f32), - b: snip('2', d.i32), - c: snip('vec2f(1.0, 1.0)', d.vec2f), - d: snip('true', d.bool), + a: snip('1.0', d.f32, /* ref */ 'runtime'), + b: snip('2', d.i32, /* ref */ 'runtime'), + c: snip('vec2f(1.0, 1.0)', d.vec2f, /* ref */ 'runtime'), + d: snip('true', d.bool, /* ref */ 'runtime'), }; const res = convertStructValues(structType, snippets); expect(res.length).toBe(4); @@ -316,25 +336,25 @@ describe('convertStructValues', () => { it('maps values requiring implicit casts and warns', () => { const snippets: Record = { - a: snip('1', d.i32), // i32 -> f32 (cast) - b: snip('2', d.u32), // u32 -> i32 (cast) - c: snip('2.22', d.f32), - d: snip('true', d.bool), + a: snip('1', d.i32, /* ref */ 'runtime'), // i32 -> f32 (cast) + b: snip('2', d.u32, /* ref */ 'runtime'), // u32 -> i32 (cast) + c: snip('2.22', d.f32, /* ref */ 'runtime'), + d: snip('true', d.bool, /* ref */ 'runtime'), }; const res = convertStructValues(structType, snippets); expect(res.length).toBe(4); - expect(res[0]).toEqual(snip('f32(1)', d.f32)); // Cast applied - expect(res[1]).toEqual(snip('i32(2)', d.i32)); // Cast applied + expect(res[0]).toEqual(snip('f32(1)', d.f32, /* ref */ 'runtime')); // Cast applied + expect(res[1]).toEqual(snip('i32(2)', d.i32, /* ref */ 'runtime')); // Cast applied expect(res[2]).toEqual(snippets.c); expect(res[3]).toEqual(snippets.d); }); it('throws on missing property', () => { const snippets: Record = { - a: snip('1.0', d.f32), + a: snip('1.0', d.f32, /* ref */ 'runtime'), // b is missing - c: snip('vec2f(1.0, 1.0)', d.vec2f), - d: snip('true', d.bool), + c: snip('vec2f(1.0, 1.0)', d.vec2f, /* ref */ 'runtime'), + d: snip('true', d.bool, /* ref */ 'runtime'), }; expect(() => convertStructValues(structType, snippets)).toThrow( /Missing property b/, diff --git a/packages/typegpu/tests/tgsl/createDualImpl.test.ts b/packages/typegpu/tests/tgsl/createDualImpl.test.ts index b9b09bd30a..d8d05292c2 100644 --- a/packages/typegpu/tests/tgsl/createDualImpl.test.ts +++ b/packages/typegpu/tests/tgsl/createDualImpl.test.ts @@ -41,7 +41,7 @@ describe('dualImpl', () => { expect(asWgsl(myFn)).toMatchInlineSnapshot(` "fn myFn() { - var a = 5; + const a = 5; }" `); }); @@ -77,7 +77,7 @@ describe('dualImpl', () => { expect(asWgsl(myFn)).toMatchInlineSnapshot(` "fn myFn() { - var a = fallback(2); + let a = fallback(2); }" `); }); @@ -98,7 +98,7 @@ describe('dualImpl', () => { expect(asWgsl(myFn)).toMatchInlineSnapshot(` "fn myFn() { - var a = fallback(2); + let a = fallback(2); }" `); }); @@ -122,7 +122,7 @@ describe('dualImpl', () => { [Error: Resolution of the following tree failed: - - fn:myFn - - myDualImpl: Division by zero] + - fn:myDualImpl: Division by zero] `); }); }); diff --git a/packages/typegpu/tests/tgsl/generationHelpers.test.ts b/packages/typegpu/tests/tgsl/generationHelpers.test.ts index 9cf27f8d82..27d269466a 100644 --- a/packages/typegpu/tests/tgsl/generationHelpers.test.ts +++ b/packages/typegpu/tests/tgsl/generationHelpers.test.ts @@ -1,4 +1,4 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { arrayOf } from '../../src/data/array.ts'; import { mat2x2f, mat3x3f, mat4x4f } from '../../src/data/matrix.ts'; import { @@ -20,41 +20,23 @@ import { vec4h, } from '../../src/data/vector.ts'; import { + accessIndex, + accessProp, coerceToSnippet, - type GenerationCtx, - getTypeForIndexAccess, - getTypeForPropAccess, numericLiteralToSnippet, } from '../../src/tgsl/generationHelpers.ts'; import { UnknownData } from '../../src/data/dataTypes.ts'; import { snip } from '../../src/data/snippet.ts'; -import { CodegenState } from '../../src/types.ts'; import { INTERNAL_setCtx } from '../../src/execMode.ts'; - -const mockCtx = { - indent: () => '', - dedent: () => '', - pushBlockScope: () => {}, - popBlockScope: () => {}, - mode: new CodegenState(), - getById: vi.fn(), - defineVariable: vi.fn((id, dataType) => ({ value: id, dataType })), - resolve: vi.fn((val) => { - if ( - (typeof val === 'function' || typeof val === 'object') && - 'type' in val - ) { - return val.type; - } - return val; - }), - unwrap: vi.fn((val) => val), - pre: '', -} as unknown as GenerationCtx; +import { ResolutionCtxImpl } from '../../src/resolutionCtx.ts'; +import { namespace } from '../../src/core/resolve/namespace.ts'; describe('generationHelpers', () => { beforeEach(() => { - INTERNAL_setCtx(mockCtx); + const ctx = new ResolutionCtxImpl({ + namespace: namespace(), + }); + INTERNAL_setCtx(ctx); }); afterEach(() => { @@ -64,77 +46,123 @@ describe('generationHelpers', () => { describe('numericLiteralToSnippet', () => { it('should convert numeric literals to correct snippets', () => { expect(numericLiteralToSnippet(1)).toEqual( - snip(1, abstractInt), + snip(1, abstractInt, /* origin */ 'constant'), ); expect(numericLiteralToSnippet(1.1)).toEqual( - snip(1.1, abstractFloat), + snip(1.1, abstractFloat, /* origin */ 'constant'), ); expect(numericLiteralToSnippet(1e10)).toEqual( - snip(1e10, abstractInt), + snip(1e10, abstractInt, /* origin */ 'constant'), ); expect(numericLiteralToSnippet(0.5)).toEqual( - snip(0.5, abstractFloat), + snip(0.5, abstractFloat, /* origin */ 'constant'), ); expect(numericLiteralToSnippet(-45)).toEqual( - snip(-45, abstractInt), + snip(-45, abstractInt, /* origin */ 'constant'), ); expect(numericLiteralToSnippet(0x1A)).toEqual( - snip(0x1A, abstractInt), + snip(0x1A, abstractInt, /* origin */ 'constant'), ); - expect(numericLiteralToSnippet(0b101)).toEqual(snip(5, abstractInt)); + expect(numericLiteralToSnippet(0b101)).toEqual( + snip(5, abstractInt, /* origin */ 'constant'), + ); }); }); - describe('getTypeForPropAccess', () => { + describe('accessProp', () => { const MyStruct = struct({ foo: f32, bar: vec3f, }); it('should return struct property types', () => { - expect(getTypeForPropAccess(MyStruct, 'foo')).toBe(f32); - expect(getTypeForPropAccess(MyStruct, 'bar')).toBe(vec3f); - expect(getTypeForPropAccess(MyStruct, 'notfound')).toBe(UnknownData); + const target = snip('foo', MyStruct, 'function'); + expect(accessProp(target, 'foo')).toStrictEqual( + snip('foo.foo', f32, /* origin */ 'runtime'), + ); + expect(accessProp(target, 'bar')).toStrictEqual( + snip('foo.bar', vec3f, /* origin */ 'function'), + ); + expect(accessProp(target, 'notfound')).toStrictEqual(undefined); }); it('should return swizzle types on vectors', () => { - expect(getTypeForPropAccess(vec4f, 'x')).toBe(f32); - expect(getTypeForPropAccess(vec4f, 'yz')).toBe(vec2f); - expect(getTypeForPropAccess(vec4f, 'xyzw')).toBe(vec4f); + const target = snip('foo', vec4f, 'function'); + + expect(accessProp(target, 'x')).toStrictEqual( + snip('foo.x', f32, /* origin */ 'runtime'), + ); + expect(accessProp(target, 'yz')).toStrictEqual( + snip('foo.yz', vec2f, /* origin */ 'runtime'), + ); + expect(accessProp(target, 'xyzw')).toStrictEqual( + snip('foo.xyzw', vec4f, /* origin */ 'runtime'), + ); }); - it('should return UnknownData when applied to primitives or invalid', () => { - expect(getTypeForPropAccess(u32, 'x')).toBe(UnknownData); - expect(getTypeForPropAccess(bool, 'x')).toBe(UnknownData); + it('should return undefined when applied to primitives or invalid', () => { + const target1 = snip('foo', u32, /* origin */ 'runtime'); + const target2 = snip('foo', bool, /* origin */ 'runtime'); + expect(accessProp(target1, 'x')).toBe(undefined); + expect(accessProp(target2, 'x')).toBe(undefined); }); }); - describe('getTypeForIndexAccess', () => { + describe('accessIndex', () => { const arr = arrayOf(f32, 2); + const index = snip('0', u32, /* origin */ 'runtime'); it('returns element type for arrays', () => { - expect(getTypeForIndexAccess(arr)).toBe(f32); + const target = snip('foo', arr, /* origin */ 'runtime'); + expect(accessIndex(target, index)).toStrictEqual( + snip('foo[0]', f32, 'runtime'), + ); }); it('returns vector component', () => { - expect(getTypeForIndexAccess(vec2i)).toBe(i32); - expect(getTypeForIndexAccess(vec4h)).toBe(f16); + const target1 = snip('foo', vec2i, /* origin */ 'runtime'); + const target2 = snip('foo', vec4h, /* origin */ 'runtime'); + expect(accessIndex(target1, index)).toStrictEqual( + snip('foo[0]', i32, 'runtime'), + ); + expect(accessIndex(target2, index)).toStrictEqual( + snip('foo[0]', f16, 'runtime'), + ); }); it('returns matrix column type', () => { - expect(getTypeForIndexAccess(mat2x2f)).toBe(vec2f); - expect(getTypeForIndexAccess(mat3x3f)).toBe(vec3f); - expect(getTypeForIndexAccess(mat4x4f)).toBe(vec4f); + const target1 = accessProp( + snip('foo', mat2x2f, /* origin */ 'runtime'), + 'columns', + ); + const target2 = accessProp( + snip('foo', mat3x3f, /* origin */ 'runtime'), + 'columns', + ); + const target3 = accessProp( + snip('foo', mat4x4f, /* origin */ 'runtime'), + 'columns', + ); + expect(target1 && accessIndex(target1, index)).toStrictEqual( + snip('foo[0]', vec2f, 'runtime'), + ); + expect(target2 && accessIndex(target2, index)).toStrictEqual( + snip('foo[0]', vec3f, 'runtime'), + ); + expect(target3 && accessIndex(target3, index)).toStrictEqual( + snip('foo[0]', vec4f, 'runtime'), + ); }); - it('returns UnknownData otherwise', () => { - expect(getTypeForIndexAccess(f32)).toBe(UnknownData); + it('returns undefined otherwise', () => { + const target = snip('foo', f32, /* origin */ 'runtime'); + expect(accessIndex(target, index)).toBe(undefined); }); }); @@ -142,21 +170,39 @@ describe('generationHelpers', () => { const arr = arrayOf(f32, 2); it('coerces JS numbers', () => { - expect(coerceToSnippet(1)).toEqual(snip(1, abstractInt)); - expect(coerceToSnippet(2.5)).toEqual(snip(2.5, abstractFloat)); - expect(coerceToSnippet(-10)).toEqual(snip(-10, abstractInt)); - expect(coerceToSnippet(0.0)).toEqual(snip(0, abstractInt)); + expect(coerceToSnippet(1)).toEqual( + snip(1, abstractInt, /* origin */ 'constant'), + ); + expect(coerceToSnippet(2.5)).toEqual( + snip(2.5, abstractFloat, /* origin */ 'constant'), + ); + expect(coerceToSnippet(-10)).toEqual( + snip(-10, abstractInt, /* origin */ 'constant'), + ); + expect(coerceToSnippet(0.0)).toEqual( + snip(0, abstractInt, /* origin */ 'constant'), + ); }); it('coerces JS booleans', () => { - expect(coerceToSnippet(true)).toEqual(snip(true, bool)); - expect(coerceToSnippet(false)).toEqual(snip(false, bool)); + expect(coerceToSnippet(true)).toEqual( + snip(true, bool, /* origin */ 'constant'), + ); + expect(coerceToSnippet(false)).toEqual( + snip(false, bool, /* origin */ 'constant'), + ); }); it(`coerces schemas to UnknownData (as they're not instance types)`, () => { - expect(coerceToSnippet(f32)).toEqual(snip(f32, UnknownData)); - expect(coerceToSnippet(vec3i)).toEqual(snip(vec3i, UnknownData)); - expect(coerceToSnippet(arr)).toEqual(snip(arr, UnknownData)); + expect(coerceToSnippet(f32)).toEqual( + snip(f32, UnknownData, /* origin */ 'constant'), + ); + expect(coerceToSnippet(vec3i)).toEqual( + snip(vec3i, UnknownData, /* origin */ 'constant'), + ); + expect(coerceToSnippet(arr)).toEqual( + snip(arr, UnknownData, /* origin */ 'constant'), + ); }); it('coerces arrays to unknown', () => { @@ -180,12 +226,22 @@ describe('generationHelpers', () => { }); it('returns UnknownData for other types', () => { - expect(coerceToSnippet('foo')).toEqual(snip('foo', UnknownData)); - expect(coerceToSnippet({})).toEqual(snip({}, UnknownData)); - expect(coerceToSnippet(null)).toEqual(snip(null, UnknownData)); - expect(coerceToSnippet(undefined)).toEqual(snip(undefined, UnknownData)); + expect(coerceToSnippet('foo')).toEqual( + snip('foo', UnknownData, /* origin */ 'constant'), + ); + expect(coerceToSnippet({})).toEqual( + snip({}, UnknownData, /* origin */ 'constant'), + ); + expect(coerceToSnippet(null)).toEqual( + snip(null, UnknownData, /* origin */ 'constant'), + ); + expect(coerceToSnippet(undefined)).toEqual( + snip(undefined, UnknownData, /* origin */ 'constant'), + ); const fn = () => {}; - expect(coerceToSnippet(fn)).toEqual(snip(fn, UnknownData)); + expect(coerceToSnippet(fn)).toEqual( + snip(fn, UnknownData, /* origin */ 'constant'), + ); }); }); }); diff --git a/packages/typegpu/tests/tgsl/memberAccess.test.ts b/packages/typegpu/tests/tgsl/memberAccess.test.ts new file mode 100644 index 0000000000..5e028aa4dd --- /dev/null +++ b/packages/typegpu/tests/tgsl/memberAccess.test.ts @@ -0,0 +1,82 @@ +import { describe } from 'vitest'; +import { it } from '../utils/extendedIt.ts'; +import { expectSnippetOf } from '../utils/parseResolved.ts'; +import * as d from '../../src/data/index.ts'; +import { snip } from '../../src/data/snippet.ts'; +import tgpu from '../../src/index.ts'; + +describe('Member Access', () => { + const Boid = d.struct({ + pos: d.vec3f, + }); + + it('should access member properties of literals', () => { + expectSnippetOf(() => { + 'use gpu'; + Boid().pos; + }).toStrictEqual(snip('Boid().pos', d.vec3f, 'runtime')); + + expectSnippetOf(() => { + 'use gpu'; + Boid().pos.xyz; + }).toStrictEqual(snip('Boid().pos.xyz', d.vec3f, 'runtime')); + }); + + it('should access member properties of externals', () => { + const boid = Boid({ pos: d.vec3f(1, 2, 3) }); + + expectSnippetOf(() => { + 'use gpu'; + boid.pos; + }).toStrictEqual(snip(d.vec3f(1, 2, 3), d.vec3f, 'constant')); + + expectSnippetOf(() => { + 'use gpu'; + boid.pos.zyx; + }).toStrictEqual(snip(d.vec3f(3, 2, 1), d.vec3f, 'constant')); + }); + + it('should access member properties of variables', () => { + const boidVar = tgpu.privateVar(Boid); + + expectSnippetOf(() => { + 'use gpu'; + boidVar.$.pos; + }).toStrictEqual(snip('boidVar.pos', d.vec3f, 'private')); + + expectSnippetOf(() => { + 'use gpu'; + boidVar.$.pos.xyz; + }).toStrictEqual(snip('boidVar.pos.xyz', d.vec3f, 'runtime')); // < swizzles are new objects + }); + + it('derefs access to local variables with proper address space', () => { + expectSnippetOf(() => { + 'use gpu'; + // Creating a new Boid instance + const boid = Boid(); + // Taking a reference that is local to this function + const boidRef = boid; + boidRef.pos; + }).toStrictEqual(snip('(*boidRef).pos', d.vec3f, 'function')); + }); + + it('derefs access to storage with proper address space', ({ root }) => { + const boidReadonly = root.createReadonly(Boid); + const boidMutable = root.createMutable(Boid); + + expectSnippetOf(() => { + 'use gpu'; + // Taking a reference to a storage variable + const boidRef = boidReadonly.$; + boidRef.pos; + }).toStrictEqual(snip('(*boidRef).pos', d.vec3f, 'readonly')); + + expectSnippetOf(() => { + 'use gpu'; + // Taking a reference to a storage variable + const boidRef = boidMutable.$; + boidRef.pos; + }).toStrictEqual(snip('(*boidRef).pos', d.vec3f, 'mutable')); + }); +}); diff --git a/packages/typegpu/tests/tgsl/shellless.test.ts b/packages/typegpu/tests/tgsl/shellless.test.ts index 6a22229b18..4c0632ab19 100644 --- a/packages/typegpu/tests/tgsl/shellless.test.ts +++ b/packages/typegpu/tests/tgsl/shellless.test.ts @@ -94,7 +94,7 @@ describe('shellless', () => { } fn main() -> f32 { - var x = someFn(1, 2); + let x = someFn(1i, 2i); return x; }" `); @@ -124,7 +124,7 @@ describe('shellless', () => { [Error: Resolution of the following tree failed: - - fn:main - - fn*:someFn: Expected function to have a single return type, got [u32, i32, f32]. Cast explicitly to the desired type.] + - fn*:someFn(f32, i32): Expected function to have a single return type, got [u32, i32, f32]. Cast explicitly to the desired type.] `); }); @@ -158,6 +158,86 @@ describe('shellless', () => { `); }); + it('handles refs and generates pointer arguments for them', () => { + const advance = (pos: d.ref, vel: d.v3f) => { + 'use gpu'; + pos.$.x += vel.x; + pos.$.y += vel.y; + pos.$.z += vel.z; + }; + + const main = () => { + 'use gpu'; + const pos = d.ref(d.vec3f(0, 0, 0)); + advance(pos, d.vec3f(1, 2, 3)); + }; + + expect(asWgsl(main)).toMatchInlineSnapshot(` + "fn advance(pos: ptr, vel: vec3f) { + (*pos).x += vel.x; + (*pos).y += vel.y; + (*pos).z += vel.z; + } + + fn main() { + var pos = vec3f(); + advance((&pos), vec3f(1, 2, 3)); + }" + `); + }); + + it('generates private pointer params when passing a private variable ref to a function', ({ root }) => { + const foo = tgpu.privateVar(d.vec3f); + + const sumComponents = (vec: d.ref) => { + 'use gpu'; + return vec.$.x + vec.$.y + vec.$.z; + }; + + const main = () => { + 'use gpu'; + sumComponents(d.ref(foo.$)); + }; + + expect(asWgsl(main)).toMatchInlineSnapshot(` + "fn sumComponents(vec: ptr) -> f32 { + return (((*vec).x + (*vec).y) + (*vec).z); + } + + var foo: vec3f; + + fn main() { + sumComponents((&foo)); + }" + `); + }); + + it('generates uniform pointer params when passing a fixed uniform ref to a function', ({ root }) => { + const posUniform = root.createUniform(d.vec3f); + + const sumComponents = (vec: d.ref) => { + 'use gpu'; + return vec.$.x + vec.$.y + vec.$.z; + }; + + const main = () => { + 'use gpu'; + sumComponents(d.ref(posUniform.$)); + }; + + expect(asWgsl(main)).toMatchInlineSnapshot(` + "fn sumComponents(vec: ptr) -> f32 { + return (((*vec).x + (*vec).y) + (*vec).z); + } + + @group(0) @binding(0) var posUniform: vec3f; + + fn main() { + sumComponents((&posUniform)); + }" + `); + }); + it('resolves when accepting no arguments', () => { const main = () => { 'use gpu'; diff --git a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts index 1dc0c8f1c3..7bb1a20424 100644 --- a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts +++ b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts @@ -294,7 +294,11 @@ describe('wgslGenerator', () => { } const args = astInfo.ast.params.map((arg) => - snip((arg as { type: 'i'; name: string }).name, d.u32) + snip( + (arg as { type: 'i'; name: string }).name, + d.u32, + /* ref */ 'runtime', + ) ); provideCtx(ctx, () => { @@ -316,7 +320,7 @@ describe('wgslGenerator', () => { // Check for: const vec = std.mix(d.vec4f(), testUsage.value.a, value); // ^ this part should be a vec4f ctx[$internal].itemStateStack.pushBlockScope(); - wgslGenerator.blockVariable('value', d.i32); + wgslGenerator.blockVariable('var', 'value', d.i32, 'runtime'); const res2 = wgslGenerator.expression( (astInfo.ast?.body[1][1] as tinyest.Const)[2], ); @@ -328,7 +332,7 @@ describe('wgslGenerator', () => { // ^ this part should be an atomic u32 // ^ this part should be void ctx[$internal].itemStateStack.pushBlockScope(); - wgslGenerator.blockVariable('vec', d.vec4f); + wgslGenerator.blockVariable('var', 'vec', d.vec4f, 'function'); const res3 = wgslGenerator.expression( (astInfo.ast?.body[1][2] as tinyest.Call)[2][0] as tinyest.Expression, ); @@ -492,7 +496,7 @@ describe('wgslGenerator', () => { provideCtx(ctx, () => { ctx[$internal].itemStateStack.pushFunctionScope( - [snip('idx', d.u32)], + [snip('idx', d.u32, /* ref */ 'runtime')], {}, d.f32, astInfo.externals ?? {}, @@ -517,7 +521,7 @@ describe('wgslGenerator', () => { expect(asWgsl(testFn)).toMatchInlineSnapshot(` "fn testFn() -> u32 { var arr = array(1u, 2u, 3u); - return arr[1]; + return arr[1i]; }" `); @@ -570,7 +574,7 @@ describe('wgslGenerator', () => { expect(asWgsl(testFn)).toMatchInlineSnapshot(` "fn testFn() -> u32 { var arr = array(vec2u(1, 2), vec2u(3, 4), vec2u(5, 6)); - return arr[1].x; + return arr[1i].x; }" `); }); @@ -587,7 +591,7 @@ describe('wgslGenerator', () => { expect(asWgsl(testFn)).toMatchInlineSnapshot(` "fn testFn() -> u32 { var a = 12u; - var b = 2.5f; + const b = 2.5f; a = u32(b); return a; }" @@ -613,7 +617,7 @@ describe('wgslGenerator', () => { fn testFn() -> f32 { var arr = array(TestStruct(1u, 2f), TestStruct(3u, 4f)); - return arr[1].y; + return arr[1i].y; }" `); @@ -659,7 +663,7 @@ describe('wgslGenerator', () => { expect(asWgsl(testFn)).toMatchInlineSnapshot(` "fn testFn() -> f32 { var arr = array(vec2f(44, 88), vec2f(88, 176)); - return arr[1].y; + return arr[1i].y; }" `); @@ -899,7 +903,7 @@ describe('wgslGenerator', () => { [Error: Resolution of the following tree failed: - - fn:testFn - - internalTestFn: Cannot convert value of type 'array' to type 'vec2f'] + - fn:internalTestFn: Cannot convert value of type 'arrayOf(i32, 3)' to type 'vec2f'] `); }); @@ -913,7 +917,7 @@ describe('wgslGenerator', () => { [Error: Resolution of the following tree failed: - - fn:testFn - - translate4: Cannot read properties of undefined (reading 'dataType')] + - fn:translate4: Cannot read properties of undefined (reading 'dataType')] `); }); @@ -928,18 +932,18 @@ describe('wgslGenerator', () => { [Error: Resolution of the following tree failed: - - fn:testFn - - vec4f: Cannot convert value of type 'array' to type 'f32'] + - fn:vec4f: Cannot convert value of type 'arrayOf(i32, 4)' to type 'f32'] `); }); it('generates correct code for pointer value assignment', () => { const increment = tgpu.fn([d.ptrFn(d.f32)])((val) => { - val += 1; + val.$ += 1; }); expect(asWgsl(increment)).toMatchInlineSnapshot(` "fn increment(val: ptr) { - *val += 1f; + (*val) += 1f; }" `); }); @@ -953,8 +957,8 @@ describe('wgslGenerator', () => { expect(asWgsl(main)).toMatchInlineSnapshot(` "fn main() -> i32 { - var notAKeyword = 0; - var struct_1 = 1; + const notAKeyword = 0; + const struct_1 = 1; return struct_1; }" `); @@ -1034,9 +1038,9 @@ describe('wgslGenerator', () => { expect(asWgsl(main)).toMatchInlineSnapshot(` "fn main() { - var mut_1 = 1; - var mut_1_1 = 2; - var mut_1_2 = 2; + const mut_1 = 1; + const mut_1_1 = 2; + const mut_1_2 = 2; }" `); }); @@ -1061,9 +1065,9 @@ describe('wgslGenerator', () => { expect(asWgsl(power)).toMatchInlineSnapshot(` "fn power() { - var a = 10f; - var b = 3f; - var n = pow(a, b); + const a = 10f; + const b = 3f; + let n = pow(a, b); }" `); }); @@ -1076,7 +1080,7 @@ describe('wgslGenerator', () => { expect(asWgsl(power)).toMatchInlineSnapshot(` "fn power() { - var n = 16.; + const n = 16.; }" `); }); @@ -1090,9 +1094,9 @@ describe('wgslGenerator', () => { expect(asWgsl(power)).toMatchInlineSnapshot(` "fn power() { - var a = 3u; - var b = 5i; - var m = pow(f32(a), f32(b)); + const a = 3u; + const b = 5i; + let m = pow(f32(a), f32(b)); }" `); }); @@ -1122,9 +1126,9 @@ describe('wgslGenerator', () => { expect(asWgsl(testFn)).toMatchInlineSnapshot(` "fn testFn() { var matrix = mat4x4f(); - var column = matrix[1]; - var element = column[0]; - var directElement = matrix[1][0]; + let column = (&matrix[1i]); + let element = (*column)[0i]; + let directElement = matrix[1i][0i]; }" `); }); @@ -1138,12 +1142,12 @@ describe('wgslGenerator', () => { }); expect(asWgsl(testFn)).toMatchInlineSnapshot(` - "var index: u32; + "var matrix: mat4x4f; - var matrix: mat4x4f; + var index: u32; fn testFn() { - var element = matrix[index]; + let element = (&matrix[index]); }" `); }); @@ -1158,7 +1162,7 @@ describe('wgslGenerator', () => { expect(() => asWgsl(testFn)).toThrowErrorMatchingInlineSnapshot(` [Error: Resolution of the following tree failed: - - - fn:testFn: Unable to index a value of unknown type with index i. If the value is an array, to address this, consider one of the following approaches: (1) declare the array using 'tgpu.const', (2) store the array in a buffer, or (3) define the array within the GPU function scope.] + - fn:testFn: Value undefined (as json: undefined) is not resolvable to type u32] `); }); diff --git a/packages/typegpu/tests/tgslFn.test.ts b/packages/typegpu/tests/tgslFn.test.ts index 4f0faef656..f6649b3ea7 100644 --- a/packages/typegpu/tests/tgslFn.test.ts +++ b/packages/typegpu/tests/tgslFn.test.ts @@ -162,9 +162,9 @@ describe('TGSL tgpu.fn function', () => { } @vertex fn vertex_fn(input: vertex_fn_Input) -> vertex_fn_Output { - var vi = f32(input.vi); - var ii = f32(input.ii); - var color = input.color; + let vi = f32(input.vi); + let ii = f32(input.ii); + let color = input.color; return vertex_fn_Output(vec4f(color.w, ii, vi, 1f), vec2f(color.w, vi)); }" `); @@ -300,9 +300,9 @@ describe('TGSL tgpu.fn function', () => { } @compute @workgroup_size(24) fn compute_fn(input: compute_fn_Input) { - var index = input.gid.x; - var iterationF = 0f; - var sign = 0; + let index = input.gid.x; + const iterationF = 0f; + const sign = 0; var change = vec4f(); }" `); @@ -327,9 +327,9 @@ describe('TGSL tgpu.fn function', () => { } @compute @workgroup_size(24) fn compute_fn(_arg_0: compute_fn_Input) { - var index = _arg_0.gid.x; - var iterationF = 0f; - var sign = 0; + let index = _arg_0.gid.x; + const iterationF = 0f; + const sign = 0; var change = vec4f(); }" `); @@ -391,7 +391,7 @@ describe('TGSL tgpu.fn function', () => { } @fragment fn fragmentFn(input: fragmentFn_Input) -> fragmentFn_Output { - var pos = input.pos; + let pos = input.pos; var sampleMask = 0; if (((input.sampleMask > 0u) && (pos.x > 0f))) { sampleMask = 1i; @@ -645,9 +645,9 @@ describe('TGSL tgpu.fn function', () => { it('resolves a function with a pointer parameter', () => { const addOnes = tgpu.fn([d.ptrStorage(d.vec3f, 'read-write')])((ptr) => { - ptr.x += 1; - ptr.y += 1; - ptr.z += 1; + ptr.$.x += 1; + ptr.$.y += 1; + ptr.$.z += 1; }); expect(asWgsl(addOnes)).toMatchInlineSnapshot(` @@ -658,10 +658,11 @@ describe('TGSL tgpu.fn function', () => { }" `); - const callAddOnes = tgpu.fn([])(() => { - const someVec = d.vec3f(1, 2, 3); + const callAddOnes = () => { + 'use gpu'; + const someVec = d.ref(d.vec3f(1, 2, 3)); addOnes(someVec); - }); + }; expect(asWgsl(callAddOnes)).toMatchInlineSnapshot(` "fn addOnes(ptr: ptr) { @@ -672,7 +673,7 @@ describe('TGSL tgpu.fn function', () => { fn callAddOnes() { var someVec = vec3f(1, 2, 3); - addOnes(&someVec); + addOnes((&someVec)); }" `); }); @@ -945,10 +946,9 @@ describe('tgsl fn when using plugin', () => { [Error: Resolution of the following tree failed: - - fn:bar - - call:foo - fn:foo - - call:bar: Recursive function fn:bar detected. Recursion is not allowed on the GPU.] - `); + - fn:bar: Recursive function fn:bar detected. Recursion is not allowed on the GPU.] + `); }); it('throws when it detects a cyclic dependency (when using slots)', () => { @@ -964,13 +964,10 @@ describe('tgsl fn when using plugin', () => { [Error: Resolution of the following tree failed: - - fn:one - - call:two - fn:two - - call:three - fn:three - - call:inner - fn:inner - - call:one: Recursive function fn:one detected. Recursion is not allowed on the GPU.] + - fn:one: Recursive function fn:one detected. Recursion is not allowed on the GPU.] `); }); @@ -995,9 +992,8 @@ describe('tgsl fn when using plugin', () => { [Error: Resolution of the following tree failed: - - fn:one - - call:fallbackFn - fn:fallbackFn - - call:one: Recursive function fn:one detected. Recursion is not allowed on the GPU.] + - fn:one: Recursive function fn:one detected. Recursion is not allowed on the GPU.] `); const boundOne = one.with(flagSlot, true); diff --git a/packages/typegpu/tests/utils/parseResolved.ts b/packages/typegpu/tests/utils/parseResolved.ts index 5b5219501a..c8ee7bf91d 100644 --- a/packages/typegpu/tests/utils/parseResolved.ts +++ b/packages/typegpu/tests/utils/parseResolved.ts @@ -9,6 +9,8 @@ import { CodegenState, type Wgsl } from '../../src/types.ts'; import { getMetaData } from '../../src/shared/meta.ts'; import wgslGenerator from '../../src/tgsl/wgslGenerator.ts'; import { namespace } from '../../src/core/resolve/namespace.ts'; +import type { Snippet } from '../../src/data/snippet.ts'; +import { $internal } from '../../src/shared/symbols.ts'; /** * Just a shorthand for tgpu.resolve @@ -23,37 +25,69 @@ export function asWgsl(...values: unknown[]): string { }); } -export function expectDataTypeOf( - cb: () => unknown, -): Assertion { +function extractSnippetFromFn(cb: () => unknown): Snippet { const ctx = new ResolutionCtxImpl({ namespace: namespace({ names: 'strict' }), }); - const dataType = provideCtx( + return provideCtx( ctx, () => { + let pushedFnScope = false; try { + const meta = getMetaData(cb); + + if (!meta || !meta.ast) { + throw new Error('No metadata found for the function'); + } + ctx.pushMode(new CodegenState()); - // Extracting the first expression from the block - const statements = (getMetaData(cb)?.ast?.body as tinyest.Block)[1]; - if (statements.length !== 1) { + ctx[$internal].itemStateStack.pushItem(); + ctx[$internal].itemStateStack.pushFunctionScope( + [], + {}, + undefined, + meta.externals ?? {}, + ); + ctx.pushBlockScope(); + pushedFnScope = true; + + // Extracting the last expression from the block + const statements = meta.ast.body[1] ?? []; + if (statements.length === 0) { throw new Error( - `Expected exactly one expression, got ${statements.length}`, + `Expected at least one expression, got ${statements.length}`, ); } wgslGenerator.initGenerator(ctx); - const exprSnippet = wgslGenerator.expression( - statements[0] as tinyest.Expression, + // Prewarming statements + for (const statement of statements) { + wgslGenerator.statement(statement); + } + return wgslGenerator.expression( + statements[statements.length - 1] as tinyest.Expression, ); - - return exprSnippet.dataType; } finally { + if (pushedFnScope) { + ctx.popBlockScope(); + ctx[$internal].itemStateStack.popFunctionScope(); + ctx[$internal].itemStateStack.popItem(); + } ctx.popMode('codegen'); } }, ); +} - return expect(dataType); +export function expectSnippetOf( + cb: () => unknown, +): Assertion { + return expect(extractSnippetFromFn(cb)); +} + +export function expectDataTypeOf( + cb: () => unknown, +): Assertion { + return expect(extractSnippetFromFn(cb).dataType); } diff --git a/packages/typegpu/tests/variable.test.ts b/packages/typegpu/tests/variable.test.ts index 287ffb91ce..94ded6482b 100644 --- a/packages/typegpu/tests/variable.test.ts +++ b/packages/typegpu/tests/variable.test.ts @@ -123,9 +123,9 @@ var x: array = array(s(1u, vec2i(2, 3)), s(4u, vec2i(5, 6)) var boid: Boid = Boid(vec3f(1, 2, 3), vec3u(4, 5, 6)); fn func() { - var pos = boid; - var vel = boid.vel; - var velX = boid.vel.x; + let pos = (&boid); + let vel = (&boid.vel); + let velX = boid.vel.x; }" `); }); @@ -142,8 +142,8 @@ var x: array = array(s(1u, vec2i(2, 3)), s(4u, vec2i(5, 6)) "var atomicCounter: atomic; fn func() { - var oldValue = atomicAdd(&atomicCounter, 1u); - var currentValue = atomicLoad(&atomicCounter); + let oldValue = atomicAdd(&atomicCounter, 1u); + let currentValue = atomicLoad(&atomicCounter); }" `); }); diff --git a/packages/typegpu/tests/vector.test.ts b/packages/typegpu/tests/vector.test.ts index 36ecd96d9c..d5dacb338d 100644 --- a/packages/typegpu/tests/vector.test.ts +++ b/packages/typegpu/tests/vector.test.ts @@ -4,6 +4,7 @@ import { readData, writeData } from '../src/data/dataIO.ts'; import * as d from '../src/data/index.ts'; import { sizeOf } from '../src/data/sizeOf.ts'; import tgpu from '../src/index.ts'; +import * as std from '../src/std/index.ts'; import { asWgsl } from './utils/parseResolved.ts'; describe('constructors', () => { @@ -972,3 +973,41 @@ describe('v4b', () => { }); }); }); + +describe('type predicates', () => { + it('prunes branches', () => { + const ceil = (input: d.v3f | d.v3i): d.v3i => { + 'use gpu'; + if (input.kind === 'vec3f') { + return d.vec3i(std.ceil(input)); + } else { + return input; + } + }; + + const main = () => { + 'use gpu'; + const foo = ceil(d.vec3f(1, 2, 3)); + const bar = ceil(d.vec3i(1, 2, 3)); + }; + + expect(asWgsl(main)).toMatchInlineSnapshot(` + "fn ceil(input: vec3f) -> vec3i { + { + return vec3i(ceil(input)); + } + } + + fn ceil_1(input: vec3i) -> vec3i { + { + return input; + } + } + + fn main() { + var foo = ceil(vec3f(1, 2, 3)); + var bar = ceil_1(vec3i(1, 2, 3)); + }" + `); + }); +}); diff --git a/tsconfig.base.json b/tsconfig.base.json index bb661755d4..60c668debb 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -16,6 +16,7 @@ "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "skipLibCheck": true, + "rootDir": "${configDir}/.", "types": ["@webgpu/types", "@vitest/browser/matchers"] }, "exclude": ["${configDir}/dist", "${configDir}/node_modules"]