typegpu 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/chunk-3JTP2ATR.cjs.map +1 -1
- package/chunk-3QXCKMEJ.cjs.map +1 -1
- package/chunk-QRLTUOBJ.cjs.map +1 -1
- package/data/index.cjs.map +1 -1
- package/index.cjs +16 -16
- package/index.cjs.map +1 -1
- package/index.js +16 -16
- package/index.js.map +1 -1
- package/package.json +2 -2
- package/std/index.cjs.map +1 -1
package/index.cjs.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","../src/smol/generationHelpers.ts","../src/core/resolve/resolveData.ts","../src/smol/wgslGenerator.ts","../src/data/compiledIO.ts","../src/data/dataIO.ts","../src/resolutionCtx.ts","../src/core/pipeline/computePipeline.ts"],"names":["swizzleableTypes","swizzleLenToType","f32","vec2f","vec3f","vec4f","f16","vec2h","vec3h","vec4h","i32","vec2i","vec3i","vec4i","u32","vec2u","vec3u","vec4u","kindToSchema","mat2x2f","mat3x3f","mat4x4f","indexableTypeToResult","getTypeForPropAccess","targetType","propName","_a","UnknownData","isDerived","isSlot","ctx","getResolutionCtx","unwrapped","getTypeFromWgsl","target","hasInternalDataType","$internal","isDecorated","targetTypeStr","propLength","swizzleTypeChar","swizzleType","isWgslData","getTypeForIndexAccess","resource","isWgslArray","_b","numericLiteralToResource","bool","kind","value","abstractInt"],"mappings":"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","file":"/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import { isDerived, isSlot } from '../core/slot/slotTypes';\nimport { mat2x2f, mat3x3f, mat4x4f } from '../data/matrix';\nimport {\n abstractFloat,\n abstractInt,\n bool,\n f16,\n f32,\n i32,\n u32,\n} from '../data/numeric';\nimport type { WgslStruct } from '../data/struct';\nimport {\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n} from '../data/vector';\nimport {\n type AnyWgslData,\n type BaseData,\n isDecorated,\n isWgslArray,\n isWgslData,\n} from '../data/wgslTypes';\nimport { getResolutionCtx } from '../gpuMode';\nimport { $internal } from '../shared/symbols';\nimport {\n type Resource,\n UnknownData,\n type Wgsl,\n hasInternalDataType,\n} from '../types';\n\nconst swizzleableTypes = [\n 'vec2f',\n 'vec2h',\n 'vec2i',\n 'vec2u',\n 'vec3f',\n 'vec3h',\n 'vec3i',\n 'vec3u',\n 'vec4f',\n 'vec4h',\n 'vec4i',\n 'vec4u',\n 'struct',\n] as const;\n\ntype SwizzleableType = 'f' | 'h' | 'i' | 'u';\ntype SwizzleLength = 1 | 2 | 3 | 4;\n\nconst swizzleLenToType: Record<\n SwizzleableType,\n Record<SwizzleLength, AnyWgslData>\n> = {\n f: {\n 1: f32,\n 2: vec2f,\n 3: vec3f,\n 4: vec4f,\n },\n h: {\n 1: f16,\n 2: vec2h,\n 3: vec3h,\n 4: vec4h,\n },\n i: {\n 1: i32,\n 2: vec2i,\n 3: vec3i,\n 4: vec4i,\n },\n u: {\n 1: u32,\n 2: vec2u,\n 3: vec3u,\n 4: vec4u,\n },\n} as const;\n\nconst kindToSchema = {\n vec2f: vec2f,\n vec2h: vec2h,\n vec2i: vec2i,\n vec2u: vec2u,\n vec3f: vec3f,\n vec3h: vec3h,\n vec3i: vec3i,\n vec3u: vec3u,\n vec4f: vec4f,\n vec4h: vec4h,\n vec4i: vec4i,\n vec4u: vec4u,\n mat2x2f: mat2x2f,\n mat3x3f: mat3x3f,\n mat4x4f: mat4x4f,\n} as const;\n\nconst indexableTypeToResult = {\n vec2f: f32,\n vec2h: f16,\n vec2i: i32,\n vec2u: u32,\n vec3f: f32,\n vec3h: f16,\n vec3i: i32,\n vec3u: u32,\n vec4f: f32,\n vec4h: f16,\n vec4i: i32,\n vec4u: u32,\n mat2x2f: vec2f,\n mat3x3f: vec3f,\n mat4x4f: vec4f,\n} as const;\n\nexport function getTypeForPropAccess(\n targetType: Wgsl,\n propName: string,\n): BaseData | UnknownData {\n if (\n typeof targetType === 'string' ||\n typeof targetType === 'number' ||\n typeof targetType === 'boolean'\n ) {\n return UnknownData;\n }\n\n if (isDerived(targetType) || isSlot(targetType)) {\n const ctx = getResolutionCtx();\n if (!ctx) {\n throw new Error(\n 'Resolution context not found when unwrapping slot or derived',\n );\n }\n const unwrapped = ctx.unwrap(targetType);\n\n return getTypeFromWgsl(unwrapped as Wgsl) as BaseData;\n }\n\n let target = targetType as BaseData;\n\n if (hasInternalDataType(target)) {\n target = target[$internal].dataType;\n }\n while (isDecorated(target)) {\n target = target.inner;\n }\n\n const targetTypeStr =\n 'kind' in target ? (target.kind as string) : target.type;\n\n if (targetTypeStr === 'struct') {\n return (target as WgslStruct).propTypes[propName] ?? UnknownData;\n }\n\n const propLength = propName.length;\n if (\n swizzleableTypes.includes(\n targetTypeStr as (typeof swizzleableTypes)[number],\n ) &&\n propLength >= 1 &&\n propLength <= 4\n ) {\n const swizzleTypeChar = targetTypeStr[4] as SwizzleableType;\n const swizzleType =\n swizzleLenToType[swizzleTypeChar][propLength as SwizzleLength];\n if (swizzleType) {\n return swizzleType;\n }\n }\n\n return isWgslData(target) ? target : UnknownData;\n}\n\nexport function getTypeForIndexAccess(resource: Wgsl): BaseData | UnknownData {\n if (isWgslData(resource)) {\n // array\n if (isWgslArray(resource)) {\n return resource.elementType;\n }\n\n // vector or matrix\n if (resource.type in indexableTypeToResult) {\n return indexableTypeToResult[\n resource.type as keyof typeof indexableTypeToResult\n ];\n }\n }\n\n return UnknownData;\n}\n\nexport function getTypeFromWgsl(resource: Wgsl): BaseData | UnknownData {\n if (isDerived(resource) || isSlot(resource)) {\n return getTypeFromWgsl(resource.value as Wgsl);\n }\n\n if (typeof resource === 'string') {\n return UnknownData;\n }\n if (typeof resource === 'number') {\n return numericLiteralToResource(String(resource))?.dataType ?? UnknownData;\n }\n if (typeof resource === 'boolean') {\n return bool;\n }\n\n if ('kind' in resource) {\n const kind = resource.kind;\n if (kind in kindToSchema) {\n return kindToSchema[kind];\n }\n }\n\n return isWgslData(resource) ? resource : UnknownData;\n}\n\nexport function numericLiteralToResource(value: string): Resource | undefined {\n // Hex literals\n if (/^0x[0-9a-f]+$/i.test(value)) {\n return { value, dataType: abstractInt };\n }\n\n // Binary literals\n if (/^0b[01]+$/i.test(value)) {\n return {\n value: `${Number.parseInt(value.slice(2), 2)}`,\n dataType: abstractInt,\n };\n }\n\n // Floating point literals\n if (/^-?(?:\\d+\\.\\d*|\\d*\\.\\d+)$/i.test(value)) {\n return { value, dataType: abstractFloat };\n }\n\n // Floating point literals with scientific notation\n if (/^-?\\d+(?:\\.\\d+)?e-?\\d+$/i.test(value)) {\n return { value, dataType: abstractFloat };\n }\n\n // Integer literals\n if (/^-?\\d+$/i.test(value)) {\n return { value, dataType: abstractInt };\n }\n\n return undefined;\n}\n","import { getAttributesString } from '../../data/attributes';\nimport {\n type AnyData,\n type Disarray,\n type Unstruct,\n isLooseData,\n} from '../../data/dataTypes';\nimport type { WgslStruct } from '../../data/struct';\nimport { formatToWGSLType } from '../../data/vertexFormatData';\nimport type {\n AnyWgslData,\n BaseData,\n Bool,\n F16,\n F32,\n I32,\n Mat2x2f,\n Mat3x3f,\n Mat4x4f,\n U32,\n Vec2f,\n Vec2h,\n Vec2i,\n Vec2u,\n Vec3f,\n Vec3h,\n Vec3i,\n Vec3u,\n Vec4f,\n Vec4h,\n Vec4i,\n Vec4u,\n WgslArray,\n} from '../../data/wgslTypes';\nimport { assertExhaustive } from '../../shared/utilityTypes';\nimport type { ResolutionCtx } from '../../types';\nimport { isAttribute } from '../vertexLayout/connectAttributesToShader';\n\n/**\n * Schemas for which their `type` property directly\n * translates to the resulting WGSL code.\n */\nconst identityTypes = [\n 'bool',\n 'f32',\n 'f16',\n 'i32',\n 'u32',\n 'vec2f',\n 'vec3f',\n 'vec4f',\n 'vec2h',\n 'vec3h',\n 'vec4h',\n 'vec2i',\n 'vec3i',\n 'vec4i',\n 'vec2u',\n 'vec3u',\n 'vec4u',\n 'mat2x2f',\n 'mat3x3f',\n 'mat4x4f',\n];\n\ntype IdentityType =\n | Bool\n | F32\n | F16\n | I32\n | U32\n | Vec2f\n | Vec3f\n | Vec4f\n | Vec2h\n | Vec3h\n | Vec4h\n | Vec2i\n | Vec3i\n | Vec4i\n | Vec2u\n | Vec3u\n | Vec4u\n | Mat2x2f\n | Mat3x3f\n | Mat4x4f;\n\nfunction isIdentityType(data: AnyWgslData): data is IdentityType {\n return identityTypes.includes(data.type);\n}\n\n/**\n * Resolves a single property of a struct.\n * @param ctx - The resolution context.\n * @param key - The key of the property.\n * @param property - The property itself.\n *\n * @returns The resolved property string.\n */\nfunction resolveStructProperty(\n ctx: ResolutionCtx,\n [key, property]: [string, BaseData],\n) {\n return ` ${getAttributesString(property)}${key}: ${ctx.resolve(property as AnyWgslData)},\\n`;\n}\n\n/**\n * Resolves a struct and adds its declaration to the resolution context.\n * @param ctx - The resolution context.\n * @param struct - The struct to resolve.\n *\n * @returns The resolved struct name.\n */\nfunction resolveStruct(ctx: ResolutionCtx, struct: WgslStruct) {\n const id = ctx.names.makeUnique(struct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(struct.propTypes)\n .map((prop) => resolveStructProperty(ctx, prop))\n .join('')}\\\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an unstruct (struct that does not align data by default) to its struct data counterpart.\n * @param ctx - The resolution context.\n * @param unstruct - The unstruct to resolve.\n *\n * @returns The resolved unstruct name.\n *\n * @example\n * ```ts\n * resolveUnstruct(ctx, {\n * uv: d.float16x2, // -> d.vec2f after resolution\n * color: d.snorm8x4, -> d.vec4f after resolution\n * });\n * ```\n */\nfunction resolveUnstruct(ctx: ResolutionCtx, unstruct: Unstruct) {\n const id = ctx.names.makeUnique(unstruct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(unstruct.propTypes)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [prop[0], formatToWGSLType[prop[1].format]])\n : resolveStructProperty(ctx, prop),\n )\n .join('')}\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an array.\n * @param ctx - The resolution context.\n * @param array - The array to resolve.\n *\n * @returns The resolved array name along with its element type and count (if not runtime-sized).\n *\n * @example\n * ```ts\n * resolveArray(ctx, d.arrayOf(d.u32, 0)); // 'array<u32>' (not a real pattern, a function is preferred)\n * resolveArray(ctx, d.arrayOf(d.u32, 5)); // 'array<u32, 5>'\n * ```\n */\nfunction resolveArray(ctx: ResolutionCtx, array: WgslArray) {\n const element = ctx.resolve(array.elementType as AnyWgslData);\n\n return array.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${array.elementCount}>`;\n}\n\nfunction resolveDisarray(ctx: ResolutionCtx, disarray: Disarray) {\n const element = ctx.resolve(\n isAttribute(disarray.elementType)\n ? formatToWGSLType[disarray.elementType.format]\n : (disarray.elementType as AnyWgslData),\n );\n\n return disarray.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${disarray.elementCount}>`;\n}\n\n/**\n * Resolves a WGSL data-type schema to a string.\n * @param ctx - The resolution context.\n * @param data - The data-type to resolve.\n *\n * @returns The resolved data-type string.\n */\nexport function resolveData(ctx: ResolutionCtx, data: AnyData): string {\n if (isLooseData(data)) {\n if (data.type === 'unstruct') {\n return resolveUnstruct(ctx, data);\n }\n\n if (data.type === 'disarray') {\n return resolveDisarray(ctx, data);\n }\n\n if (data.type === 'loose-decorated') {\n return ctx.resolve(\n isAttribute(data.inner)\n ? formatToWGSLType[data.inner.format]\n : data.inner,\n );\n }\n\n return ctx.resolve(formatToWGSLType[data.type]);\n }\n\n if (isIdentityType(data)) {\n return data.type;\n }\n\n if (data.type === 'struct') {\n return resolveStruct(ctx, data);\n }\n\n if (data.type === 'array') {\n return resolveArray(ctx, data);\n }\n\n if (data.type === 'atomic') {\n return `atomic<${resolveData(ctx, data.inner)}>`;\n }\n\n if (data.type === 'decorated') {\n return ctx.resolve(data.inner as AnyWgslData);\n }\n\n if (data.type === 'ptr') {\n if (data.addressSpace === 'storage') {\n return `ptr<storage, ${ctx.resolve(data.inner)}, ${data.access === 'read-write' ? 'read_write' : data.access}>`;\n }\n return `ptr<${data.addressSpace}, ${ctx.resolve(data.inner)}>`;\n }\n\n if (data.type === 'abstractInt' || data.type === 'abstractFloat') {\n throw new Error('Abstract types have no concrete representation in WGSL');\n }\n\n assertExhaustive(data, 'resolveData');\n}\n","import type * as smol from 'tinyest';\nimport * as d from '../data';\nimport * as wgsl from '../data/wgslTypes';\nimport {\n type ResolutionCtx,\n type Resource,\n UnknownData,\n type Wgsl,\n isWgsl,\n} from '../types';\nimport {\n getTypeForIndexAccess,\n getTypeForPropAccess,\n getTypeFromWgsl,\n numericLiteralToResource,\n} from './generationHelpers';\n\nconst parenthesizedOps = [\n '==',\n '!=',\n '<',\n '<=',\n '>',\n '>=',\n '<<',\n '>>',\n '+',\n '-',\n '*',\n '/',\n '%',\n '|',\n '^',\n '&',\n '&&',\n '||',\n];\n\nconst binaryLogicalOps = ['&&', '||', '==', '!=', '<', '<=', '>', '>='];\n\ntype Operator =\n | smol.BinaryOperator\n | smol.AssignmentOperator\n | smol.LogicalOperator\n | smol.UnaryOperator;\n\nfunction operatorToType<\n TL extends wgsl.AnyWgslData | UnknownData,\n TR extends wgsl.AnyWgslData | UnknownData,\n>(lhs: TL, op: Operator, rhs?: TR): TL | TR | wgsl.Bool {\n if (!rhs) {\n if (op === '!' || op === '~') {\n return d.bool;\n }\n\n return lhs;\n }\n\n if (binaryLogicalOps.includes(op)) {\n return d.bool;\n }\n\n if (op === '=') {\n return rhs;\n }\n\n return lhs;\n}\n\nexport type GenerationCtx = ResolutionCtx & {\n readonly pre: string;\n readonly callStack: unknown[];\n indent(): string;\n dedent(): string;\n pushBlockScope(): void;\n popBlockScope(): void;\n getById(id: string): Resource | null;\n defineVariable(\n id: string,\n dataType: wgsl.AnyWgslData | UnknownData,\n ): Resource;\n};\n\nexport function resolveRes(ctx: GenerationCtx, res: Resource): string {\n if (isWgsl(res.value)) {\n return ctx.resolve(res.value);\n }\n\n return String(res.value);\n}\n\nfunction assertExhaustive(value: never): never {\n throw new Error(\n `'${JSON.stringify(value)}' was not handled by the WGSL generator.`,\n );\n}\n\nexport function generateBoolean(ctx: GenerationCtx, value: boolean): Resource {\n return { value: value ? 'true' : 'false', dataType: d.bool };\n}\n\nexport function generateBlock(ctx: GenerationCtx, value: smol.Block): string {\n ctx.pushBlockScope();\n try {\n return `${ctx.indent()}{\n${value.b.map((statement) => generateStatement(ctx, statement)).join('\\n')}\n${ctx.dedent()}}`;\n } finally {\n ctx.popBlockScope();\n }\n}\n\nexport function registerBlockVariable(\n ctx: GenerationCtx,\n id: string,\n dataType: wgsl.AnyWgslData | UnknownData,\n): Resource {\n return ctx.defineVariable(id, dataType);\n}\n\nexport function generateIdentifier(ctx: GenerationCtx, id: string): Resource {\n const res = ctx.getById(id);\n if (!res) {\n throw new Error(`Identifier ${id} not found`);\n }\n\n return res;\n}\n\nexport function generateExpression(\n ctx: GenerationCtx,\n expression: smol.Expression,\n): Resource {\n if (typeof expression === 'string') {\n return generateIdentifier(ctx, expression);\n }\n if (typeof expression === 'boolean') {\n return generateBoolean(ctx, expression);\n }\n\n if ('x' in expression) {\n // Logical/Binary/Assignment Expression\n const [lhs, op, rhs] = expression.x;\n const lhsExpr = generateExpression(ctx, lhs);\n const rhsExpr = generateExpression(ctx, rhs);\n\n const lhsStr = resolveRes(ctx, lhsExpr);\n const rhsStr = resolveRes(ctx, rhsExpr);\n const type = operatorToType(lhsExpr.dataType, op, rhsExpr.dataType);\n\n return {\n value: parenthesizedOps.includes(op)\n ? `(${lhsStr} ${op} ${rhsStr})`\n : `${lhsStr} ${op} ${rhsStr}`,\n dataType: type,\n };\n }\n\n if ('p' in expression) {\n // Update Expression\n const [op, arg] = expression.p;\n const argExpr = generateExpression(ctx, arg);\n const argStr = resolveRes(ctx, argExpr);\n\n return {\n value: `${argStr}${op}`,\n dataType: argExpr.dataType,\n };\n }\n\n if ('u' in expression) {\n // Unary Expression\n const [op, arg] = expression.u;\n const argExpr = generateExpression(ctx, arg);\n const argStr = resolveRes(ctx, argExpr);\n\n const type = operatorToType(argExpr.dataType, op);\n return {\n value: `${op}${argStr}`,\n dataType: type,\n };\n }\n\n if ('a' in expression) {\n // Member Access\n const [targetId, property] = expression.a;\n const target = generateExpression(ctx, targetId);\n\n if (typeof target.value === 'string') {\n return {\n value: `${target.value}.${property}`,\n dataType: getTypeForPropAccess(target.dataType as Wgsl, property),\n };\n }\n // biome-ignore lint/suspicious/noExplicitAny: <sorry TypeScript>\n const propValue = (target.value as any)[property];\n\n if (isWgsl(target.value)) {\n return {\n value: propValue,\n dataType: getTypeForPropAccess(target.value as d.AnyWgslData, property),\n };\n }\n\n if (typeof target.value === 'object') {\n const dataType = isWgsl(propValue)\n ? getTypeFromWgsl(propValue)\n : UnknownData;\n\n return {\n value: propValue,\n dataType,\n };\n }\n\n throw new Error(`Cannot access member ${property} of ${target.value}`);\n }\n\n if ('i' in expression) {\n // Index Access\n const [target, property] = expression.i;\n const targetExpr = generateExpression(ctx, target);\n const propertyExpr = generateExpression(ctx, property);\n const targetStr = resolveRes(ctx, targetExpr);\n const propertyStr = resolveRes(ctx, propertyExpr);\n\n return {\n value: `${targetStr}[${propertyStr}]`,\n dataType: getTypeForIndexAccess(targetExpr.dataType as d.AnyWgslData),\n };\n }\n\n if ('n' in expression) {\n // Numeric Literal\n const type = numericLiteralToResource(expression.n);\n if (!type) {\n throw new Error(`Invalid numeric literal ${expression.n}`);\n }\n return type;\n }\n\n if ('f' in expression) {\n // Function Call\n const [callee, args] = expression.f;\n const id = generateExpression(ctx, callee);\n const idValue = id.value;\n\n ctx.callStack.push(idValue);\n\n const argResources = args.map((arg) => generateExpression(ctx, arg));\n const resolvedResources = argResources.map((res) => ({\n value: resolveRes(ctx, res),\n dataType: res.dataType,\n }));\n const argValues = resolvedResources.map((res) => res.value);\n\n ctx.callStack.pop();\n\n if (typeof idValue === 'string') {\n return {\n value: `${idValue}(${argValues.join(', ')})`,\n dataType: id.dataType,\n };\n }\n\n if (wgsl.isWgslStruct(idValue)) {\n const resolvedId = ctx.resolve(idValue);\n\n return {\n value: `${resolvedId}(${argValues.join(', ')})`,\n dataType: id.dataType,\n };\n }\n\n // Assuming that `id` is callable\n return (idValue as unknown as (...args: unknown[]) => unknown)(\n ...resolvedResources,\n ) as Resource;\n }\n\n if ('o' in expression) {\n // Object Literal\n const obj = expression.o;\n const callee = ctx.callStack[ctx.callStack.length - 1];\n\n const generateEntries = (values: smol.Expression[]) =>\n values\n .map((value) => {\n const valueRes = generateExpression(ctx, value);\n return resolveRes(ctx, valueRes);\n })\n .join(', ');\n\n if (wgsl.isWgslStruct(callee)) {\n const propKeys = Object.keys(callee.propTypes);\n const values = propKeys.map((key) => {\n const val = obj[key];\n if (val === undefined) {\n throw new Error(\n `Missing property ${key} in object literal for struct ${callee}`,\n );\n }\n return val;\n });\n\n return {\n value: generateEntries(values),\n dataType: callee,\n };\n }\n\n return {\n value: generateEntries(Object.values(obj)),\n dataType: UnknownData,\n };\n }\n\n if ('s' in expression) {\n throw new Error('Cannot use string literals in TGSL.');\n }\n\n assertExhaustive(expression);\n}\n\nexport function generateStatement(\n ctx: GenerationCtx,\n statement: smol.Statement,\n): string {\n if (typeof statement === 'string') {\n return `${ctx.pre}${resolveRes(ctx, generateIdentifier(ctx, statement))};`;\n }\n\n if (typeof statement === 'boolean') {\n return `${ctx.pre}${resolveRes(ctx, generateBoolean(ctx, statement))};`;\n }\n\n if ('r' in statement) {\n // check if the thing at the top of the call stack is a struct and the statement is a plain JS object\n // if so wrap the value returned in a constructor of the struct (its resolved name)\n if (\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1]) &&\n statement.r !== null &&\n typeof statement.r === 'object' &&\n 'o' in statement.r\n ) {\n const resource = resolveRes(ctx, generateExpression(ctx, statement.r));\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}return ${resolvedStruct}(${resource});`;\n }\n\n return statement.r === null\n ? `${ctx.pre}return;`\n : `${ctx.pre}return ${resolveRes(\n ctx,\n generateExpression(ctx, statement.r),\n )};`;\n }\n\n if ('q' in statement) {\n const [cond, cons, alt] = statement.q;\n const condition = resolveRes(ctx, generateExpression(ctx, cond));\n\n ctx.indent(); // {\n const consequent = generateStatement(ctx, cons);\n ctx.dedent(); // }\n\n ctx.indent(); // {\n const alternate = alt ? generateStatement(ctx, alt) : undefined;\n ctx.dedent(); // }\n\n if (!alternate) {\n return `\\\n${ctx.pre}if (${condition})\n${consequent}`;\n }\n\n return `\\\n${ctx.pre}if (${condition})\n${consequent}\n${ctx.pre}else\n${alternate}`;\n }\n\n if ('l' in statement || 'c' in statement) {\n const [rawId, rawValue] = 'l' in statement ? statement.l : statement.c;\n const eq = rawValue ? generateExpression(ctx, rawValue) : undefined;\n\n if (!eq || !rawValue) {\n throw new Error('Cannot create variable without an initial value.');\n }\n\n registerBlockVariable(ctx, rawId, eq.dataType);\n const id = resolveRes(ctx, generateIdentifier(ctx, rawId));\n\n // If the value is a plain JS object it has to be an output struct\n if (\n typeof rawValue === 'object' &&\n 'o' in rawValue &&\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1])\n ) {\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}var ${id} = ${resolvedStruct}(${resolveRes(ctx, eq)});`;\n }\n\n return `${ctx.pre}var ${id} = ${resolveRes(ctx, eq)};`;\n }\n\n if ('b' in statement) {\n ctx.pushBlockScope();\n try {\n return generateBlock(ctx, statement);\n } finally {\n ctx.popBlockScope();\n }\n }\n\n // 'j' stands for for (trust me)\n if ('j' in statement) {\n const [init, condition, update, body] = statement.j;\n\n const initStatement = init ? generateStatement(ctx, init) : undefined;\n const initStr = initStatement ? initStatement.slice(0, -1) : '';\n\n const conditionExpr = condition\n ? generateExpression(ctx, condition)\n : undefined;\n const conditionStr = conditionExpr ? resolveRes(ctx, conditionExpr) : '';\n\n const updateStatement = update ? generateStatement(ctx, update) : undefined;\n const updateStr = updateStatement ? updateStatement.slice(0, -1) : '';\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}for (${initStr}; ${conditionStr}; ${updateStr})\n${bodyStr}`;\n }\n\n if ('w' in statement) {\n const [condition, body] = statement.w;\n const conditionStr = resolveRes(ctx, generateExpression(ctx, condition));\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}while (${conditionStr})\n${bodyStr}`;\n }\n\n if ('k' in statement) {\n return `${ctx.pre}continue;`;\n }\n\n if ('d' in statement) {\n return `${ctx.pre}break;`;\n }\n\n return `${ctx.pre}${resolveRes(ctx, generateExpression(ctx, statement))};`;\n}\n\nexport function generateFunction(ctx: GenerationCtx, body: smol.Block): string {\n return generateBlock(ctx, body);\n}\n","import { roundUp } from '../mathUtils';\nimport type { Infer } from '../shared/repr';\nimport { alignmentOf } from './alignmentOf';\nimport { isDisarray, isUnstruct } from './dataTypes';\nimport { offsetsForProps } from './offsets';\nimport { sizeOf } from './sizeOf';\nimport * as wgsl from './wgslTypes';\n\nexport const EVAL_ALLOWED_IN_ENV: boolean = (() => {\n try {\n new Function('return true');\n return true;\n } catch {\n return false;\n }\n})();\n\nconst compiledWriters = new WeakMap<\n wgsl.BaseData,\n (\n output: DataView,\n offset: number,\n value: unknown,\n littleEndian?: boolean,\n ) => void\n>();\n\nconst typeToPrimitive = {\n u32: 'u32',\n vec2u: 'u32',\n vec3u: 'u32',\n vec4u: 'u32',\n\n i32: 'i32',\n vec2i: 'i32',\n vec3i: 'i32',\n vec4i: 'i32',\n\n f32: 'f32',\n vec2f: 'f32',\n vec3f: 'f32',\n vec4f: 'f32',\n\n vec2h: 'f32',\n vec3h: 'f32',\n vec4h: 'f32',\n\n mat2x2f: 'f32',\n mat3x3f: 'f32',\n mat4x4f: 'f32',\n} as const;\n\nconst primitiveToWriteFunction = {\n u32: 'setUint32',\n i32: 'setInt32',\n f32: 'setFloat32',\n} as const;\n\nexport function buildWriter(\n node: wgsl.BaseData,\n offsetExpr: string,\n valueExpr: string,\n): string {\n if (wgsl.isWgslStruct(node) || isUnstruct(node)) {\n const propOffsets = offsetsForProps(node);\n const sortedProps = Object.entries(propOffsets).sort(\n (a, b) => a[1].offset - b[1].offset,\n );\n let code = '';\n for (const [key, propOffset] of sortedProps) {\n const subSchema = node.propTypes[key];\n if (!subSchema) continue;\n code += buildWriter(\n subSchema,\n `(${offsetExpr} + ${propOffset.offset})`,\n `${valueExpr}.${key}`,\n );\n }\n return code;\n }\n\n if (wgsl.isWgslArray(node) || isDisarray(node)) {\n const arrSchema = node as wgsl.WgslArray;\n const elementSize = roundUp(\n sizeOf(arrSchema.elementType),\n alignmentOf(arrSchema.elementType),\n );\n let code = '';\n\n code += `for (let i = 0; i < ${arrSchema.elementCount}; i++) {\\n`;\n code += buildWriter(\n arrSchema.elementType,\n `(${offsetExpr} + i * ${elementSize})`,\n `${valueExpr}[i]`,\n );\n code += '}\\n';\n\n return code;\n }\n\n if (wgsl.isVec(node)) {\n const primitive = typeToPrimitive[node.type];\n let code = '';\n const writeFunc = primitiveToWriteFunction[primitive];\n const components = ['x', 'y', 'z', 'w'];\n const count = wgsl.isVec2(node) ? 2 : wgsl.isVec3(node) ? 3 : 4;\n\n for (let i = 0; i < count; i++) {\n code += `output.${writeFunc}((${offsetExpr} + ${i * 4}), ${valueExpr}.${components[i]}, littleEndian);\\n`;\n }\n return code;\n }\n\n if (wgsl.isMat(node)) {\n const primitive = typeToPrimitive[node.type];\n const writeFunc = primitiveToWriteFunction[primitive];\n\n const matSize = wgsl.isMat2x2f(node) ? 2 : wgsl.isMat3x3f(node) ? 3 : 4;\n const elementCount = matSize * matSize;\n const rowStride = roundUp(matSize * 4, 8);\n\n let code = '';\n for (let i = 0; i < elementCount; i++) {\n const colIndex = Math.floor(i / matSize);\n const rowIndex = i % matSize;\n const byteOffset = colIndex * rowStride + rowIndex * 4;\n\n code += `output.${writeFunc}((${offsetExpr} + ${byteOffset}), ${valueExpr}.columns[${colIndex}].${['x', 'y', 'z', 'w'][rowIndex]}, littleEndian);\\n`;\n }\n\n return code;\n }\n\n const primitive = typeToPrimitive[node.type as keyof typeof typeToPrimitive];\n return `output.${primitiveToWriteFunction[primitive]}(${offsetExpr}, ${valueExpr}, littleEndian);\\n`;\n}\n\nexport function getCompiledWriterForSchema<T extends wgsl.BaseData>(\n schema: T,\n): (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n) => void {\n if (compiledWriters.has(schema)) {\n return compiledWriters.get(schema) as (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n ) => void;\n }\n\n const body = buildWriter(schema, 'offset', 'value');\n\n const fn = new Function(\n 'output',\n 'offset',\n 'value',\n 'littleEndian=true',\n body,\n ) as (\n output: DataView,\n offset: number,\n value: Infer<T> | unknown,\n littleEndian?: boolean,\n ) => void;\n\n compiledWriters.set(schema, fn);\n\n return fn;\n}\n","import type { ISerialInput, ISerialOutput } from 'typed-binary';\nimport type { Infer, InferRecord } from '../shared/repr';\nimport alignIO from './alignIO';\nimport { alignmentOf, customAlignmentOf } from './alignmentOf';\nimport type {\n AnyConcreteData,\n AnyData,\n Disarray,\n LooseDecorated,\n Unstruct,\n} from './dataTypes';\nimport { mat2x2f, mat3x3f, mat4x4f } from './matrix';\nimport { sizeOf } from './sizeOf';\nimport type { WgslStruct } from './struct';\nimport {\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n} from './vector';\nimport type * as wgsl from './wgslTypes';\n\ntype DataWriter<TSchema extends wgsl.BaseData> = (\n output: ISerialOutput,\n schema: TSchema,\n value: Infer<TSchema>,\n) => void;\n\ntype DataReader<TSchema extends wgsl.BaseData> = (\n input: ISerialInput,\n schema: TSchema,\n) => Infer<TSchema>;\n\ntype CompleteDataWriters = {\n [TType in AnyConcreteData['type']]: DataWriter<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\ntype CompleteDataReaders = {\n [TType in AnyConcreteData['type']]: DataReader<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\nconst dataWriters = {\n bool(output, _schema: wgsl.Bool, value: boolean) {\n output.writeBool(value);\n },\n\n f32(output, _schema: wgsl.F32, value: number) {\n output.writeFloat32(value);\n },\n\n f16(output, _schema: wgsl.F16, value: number) {\n output.writeFloat16(value);\n },\n\n i32(output, _schema: wgsl.I32, value: number) {\n output.writeInt32(value);\n },\n\n u32(output, _schema: wgsl.U32, value: number) {\n output.writeUint32(value);\n },\n\n vec2f(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n\n vec2h(output, _, value: wgsl.v2h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n\n vec2i(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n\n vec2u(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n\n vec3f(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n\n vec3h(output, _, value: wgsl.v3h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n },\n\n vec3i(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n\n vec3u(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n\n vec4f(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n\n vec4h(output, _, value: wgsl.v4h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n\n vec4i(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n\n vec4u(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n\n mat2x2f(output, _, value: wgsl.m2x2f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat3x3f(output, _, value: wgsl.m3x3f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat4x4f(output, _, value: wgsl.m4x4f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n struct(\n output,\n schema: WgslStruct,\n value: InferRecord<Record<string, wgsl.BaseData>>,\n ) {\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(output, alignmentOf(property));\n writeData(output, property, value[key] as wgsl.BaseData);\n }\n\n alignIO(output, alignment);\n },\n\n array(output, schema: wgsl.WgslArray, value: Infer<wgsl.BaseData>[]) {\n if (schema.elementCount === 0) {\n throw new Error('Cannot write using a runtime-sized schema.');\n }\n\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n const beginning = output.currentByteOffset;\n for (let i = 0; i < Math.min(schema.elementCount, value.length); i++) {\n alignIO(output, alignment);\n writeData(output, schema.elementType, value[i]);\n }\n output.seekTo(beginning + sizeOf(schema));\n },\n\n ptr() {\n throw new Error('Pointers are not host-shareable');\n },\n\n atomic(output, schema: wgsl.Atomic, value: number) {\n dataWriters[schema.inner.type]?.(output, schema, value);\n },\n\n decorated(output, schema: wgsl.Decorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n dataWriters[(schema.inner as AnyData)?.type]?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n },\n\n // Loose Types\n\n uint8(output, _, value: number) {\n output.writeUint8(value);\n },\n uint8x2(output, _, value: wgsl.v2u) {\n output.writeUint8(value.x);\n output.writeUint8(value.y);\n },\n uint8x4(output, _, value: wgsl.v4u) {\n output.writeUint8(value.x);\n output.writeUint8(value.y);\n output.writeUint8(value.z);\n output.writeUint8(value.w);\n },\n sint8(output, _, value: number) {\n output.writeInt8(value);\n },\n sint8x2(output, _, value: wgsl.v2i) {\n output.writeInt8(value.x);\n output.writeInt8(value.y);\n },\n sint8x4(output, _, value: wgsl.v4i) {\n output.writeInt8(value.x);\n output.writeInt8(value.y);\n output.writeInt8(value.z);\n output.writeInt8(value.w);\n },\n unorm8(output, _, value: number) {\n output.writeUint8(value * 255);\n },\n unorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(value.x * 255);\n output.writeUint8(value.y * 255);\n },\n unorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(value.x * 255);\n output.writeUint8(value.y * 255);\n output.writeUint8(value.z * 255);\n output.writeUint8(value.w * 255);\n },\n snorm8(output, _, value: number) {\n output.writeUint8(value * 127 + 128);\n },\n snorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(value.x * 127 + 128);\n output.writeUint8(value.y * 127 + 128);\n },\n snorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(value.x * 127 + 128);\n output.writeUint8(value.y * 127 + 128);\n output.writeUint8(value.z * 127 + 128);\n output.writeUint8(value.w * 127 + 128);\n },\n uint16(output, _, value: number) {\n output.writeUint16(value);\n },\n uint16x2(output, _, value: wgsl.v2u) {\n output.writeUint16(value.x);\n output.writeUint16(value.y);\n },\n uint16x4(output, _, value: wgsl.v4u) {\n output.writeUint16(value.x);\n output.writeUint16(value.y);\n output.writeUint16(value.z);\n output.writeUint16(value.w);\n },\n sint16(output, _, value: number) {\n output.writeInt16(value);\n },\n sint16x2(output, _, value: wgsl.v2i) {\n output.writeInt16(value.x);\n output.writeInt16(value.y);\n },\n sint16x4(output, _, value: wgsl.v4i) {\n output.writeInt16(value.x);\n output.writeInt16(value.y);\n output.writeInt16(value.z);\n output.writeInt16(value.w);\n },\n unorm16(output, _, value: number) {\n output.writeUint16(value * 65535);\n },\n unorm16x2(output, _, value: wgsl.v2f) {\n output.writeUint16(value.x * 65535);\n output.writeUint16(value.y * 65535);\n },\n unorm16x4(output, _, value: wgsl.v4f) {\n output.writeUint16(value.x * 65535);\n output.writeUint16(value.y * 65535);\n output.writeUint16(value.z * 65535);\n output.writeUint16(value.w * 65535);\n },\n snorm16(output, _, value: number) {\n output.writeUint16(value * 32767 + 32768);\n },\n snorm16x2(output, _, value: wgsl.v2f) {\n output.writeUint16(value.x * 32767 + 32768);\n output.writeUint16(value.y * 32767 + 32768);\n },\n snorm16x4(output, _, value: wgsl.v4f) {\n output.writeUint16(value.x * 32767 + 32768);\n output.writeUint16(value.y * 32767 + 32768);\n output.writeUint16(value.z * 32767 + 32768);\n output.writeUint16(value.w * 32767 + 32768);\n },\n float16(output, _, value: number) {\n output.writeFloat16(value);\n },\n float16x2(output, _, value: wgsl.v2f) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n float16x4(output, _, value: wgsl.v4f) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n float32(output, _, value: number) {\n output.writeFloat32(value);\n },\n float32x2(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n float32x3(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n float32x4(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n uint32(output, _, value: number) {\n output.writeUint32(value);\n },\n uint32x2(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n uint32x3(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n uint32x4(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n sint32(output, _, value: number) {\n output.writeInt32(value);\n },\n sint32x2(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n sint32x3(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n sint32x4(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n 'unorm10-10-10-2'(output, _, value: wgsl.v4f) {\n let packed = 0;\n packed |= ((value.x * 1023) & 1023) << 22; // r (10 bits)\n packed |= ((value.x * 1023) & 1023) << 12; // g (10 bits)\n packed |= ((value.y * 1023) & 1023) << 2; // b (10 bits)\n packed |= (value.z * 3) & 3; // a (2 bits)\n output.writeUint32(packed);\n },\n 'unorm8x4-bgra'(output, _, value: wgsl.v4f) {\n output.writeUint8(value.z * 255);\n output.writeUint8(value.y * 255);\n output.writeUint8(value.x * 255);\n output.writeUint8(value.w * 255);\n },\n\n disarray(output, schema: Disarray, value: unknown[]) {\n const alignment = alignmentOf(schema);\n\n alignIO(output, alignment);\n const beginning = output.currentByteOffset;\n for (let i = 0; i < Math.min(schema.elementCount, value.length); i++) {\n alignIO(output, alignment);\n dataWriters[(schema.elementType as AnyData)?.type]?.(\n output,\n schema.elementType,\n value[i],\n );\n }\n\n output.seekTo(beginning + sizeOf(schema));\n },\n\n unstruct(output, schema: Unstruct, value) {\n for (const [key, property] of Object.entries(schema.propTypes)) {\n dataWriters[property.type]?.(output, property, value[key]);\n }\n },\n\n 'loose-decorated'(output, schema: LooseDecorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n const writer = dataWriters[(schema.inner as AnyData)?.type];\n writer?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataWriters as Record<\n string,\n (output: ISerialOutput, schema: unknown, value: unknown) => void\n>;\n\nexport function writeData<TData extends wgsl.BaseData>(\n output: ISerialOutput,\n schema: TData,\n value: Infer<TData>,\n): void {\n const writer = dataWriters[schema.type];\n if (!writer) {\n throw new Error(`Cannot write data of type '${schema.type}'.`);\n }\n\n writer(output, schema, value);\n}\n\nconst dataReaders = {\n bool(input: ISerialInput): boolean {\n return input.readBool();\n },\n\n f32(input: ISerialInput): number {\n return input.readFloat32();\n },\n\n f16(input: ISerialInput): number {\n return input.readFloat16();\n },\n\n i32(input: ISerialInput): number {\n return input.readInt32();\n },\n\n u32(input: ISerialInput): number {\n return input.readUint32();\n },\n\n vec2f(input: ISerialInput): wgsl.v2f {\n return vec2f(input.readFloat32(), input.readFloat32());\n },\n\n vec3f(input: ISerialInput): wgsl.v3f {\n return vec3f(input.readFloat32(), input.readFloat32(), input.readFloat32());\n },\n\n vec4f(input: ISerialInput): wgsl.v4f {\n return vec4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n vec2h(input): wgsl.v2h {\n return vec2h(input.readFloat16(), input.readFloat16());\n },\n\n vec3h(input: ISerialInput): wgsl.v3h {\n return vec3h(input.readFloat16(), input.readFloat16(), input.readFloat16());\n },\n\n vec4h(input: ISerialInput): wgsl.v4h {\n return vec4h(\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n );\n },\n\n vec2i(input): wgsl.v2i {\n return vec2i(input.readInt32(), input.readInt32());\n },\n\n vec3i(input: ISerialInput): wgsl.v3i {\n return vec3i(input.readInt32(), input.readInt32(), input.readInt32());\n },\n\n vec4i(input: ISerialInput): wgsl.v4i {\n return vec4i(\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n );\n },\n\n vec2u(input): wgsl.v2u {\n return vec2u(input.readUint32(), input.readUint32());\n },\n\n vec3u(input: ISerialInput): wgsl.v3u {\n return vec3u(input.readUint32(), input.readUint32(), input.readUint32());\n },\n\n vec4u(input: ISerialInput): wgsl.v4u {\n return vec4u(\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n );\n },\n\n mat2x2f(input: ISerialInput): wgsl.m2x2f {\n return mat2x2f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n mat3x3f(input: ISerialInput): wgsl.m3x3f {\n const skipOneAfter = () => {\n const value = input.readFloat32();\n input.readFloat32(); // skipping;\n return value;\n };\n\n return mat3x3f(\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n );\n },\n\n mat4x4f(input: ISerialInput): wgsl.m4x4f {\n return mat4x4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n struct(input: ISerialInput, schema: WgslStruct) {\n const alignment = alignmentOf(schema);\n alignIO(input, alignment);\n const result = {} as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(input, alignmentOf(property));\n result[key] = readData(input, property);\n }\n\n alignIO(input, alignment);\n return result as InferRecord<Record<string, wgsl.BaseData>>;\n },\n\n array(input, schema) {\n if (schema.elementCount === 0) {\n throw new Error('Cannot read using a runtime-sized schema.');\n }\n\n const alignment = alignmentOf(schema);\n const elements: unknown[] = [];\n\n for (let i = 0; i < schema.elementCount; i++) {\n alignIO(input, alignment);\n const elementType = schema.elementType as wgsl.AnyWgslData;\n const value = readData(input, elementType);\n elements.push(value);\n }\n\n alignIO(input, alignment);\n return elements as never[];\n },\n\n ptr() {\n throw new Error('Pointers are not host-shareable');\n },\n\n atomic(input, schema: wgsl.Atomic): number {\n return readData(input, schema.inner);\n },\n\n decorated(input, schema: wgsl.Decorated) {\n const alignment = customAlignmentOf(schema);\n alignIO(input, alignment);\n\n const beginning = input.currentByteOffset;\n const value = readData(input, schema.inner);\n input.seekTo(beginning + sizeOf(schema));\n return value as never;\n },\n\n // Loose Types\n\n uint8: (i) => i.readUint8(),\n uint8x2: (i) => vec2u(i.readUint8(), i.readUint8()),\n uint8x4: (i) =>\n vec4u(i.readUint8(), i.readUint8(), i.readUint8(), i.readUint8()),\n sint8: (i) => i.readInt8(),\n sint8x2: (i) => {\n return vec2i(i.readInt8(), i.readInt8());\n },\n sint8x4: (i) => vec4i(i.readInt8(), i.readInt8(), i.readInt8(), i.readInt8()),\n unorm8: (i) => i.readUint8() / 255,\n unorm8x2: (i) => vec2f(i.readUint8() / 255, i.readUint8() / 255),\n unorm8x4: (i) =>\n vec4f(\n i.readUint8() / 255,\n i.readUint8() / 255,\n i.readUint8() / 255,\n i.readUint8() / 255,\n ),\n snorm8: (i) => (i.readUint8() - 128) / 127,\n snorm8x2: (i) =>\n vec2f((i.readUint8() - 128) / 127, (i.readUint8() - 128) / 127),\n snorm8x4: (i) =>\n vec4f(\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n ),\n uint16: (i) => i.readUint16(),\n uint16x2: (i) => vec2u(i.readUint16(), i.readUint16()),\n uint16x4: (i) =>\n vec4u(i.readUint16(), i.readUint16(), i.readUint16(), i.readUint16()),\n sint16: (i) => i.readInt16(),\n sint16x2: (i) => vec2i(i.readInt16(), i.readInt16()),\n sint16x4: (i) =>\n vec4i(i.readInt16(), i.readInt16(), i.readInt16(), i.readInt16()),\n unorm16: (i) => i.readUint16() / 65535,\n unorm16x2: (i) => vec2f(i.readUint16() / 65535, i.readUint16() / 65535),\n unorm16x4: (i) =>\n vec4f(\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n ),\n snorm16: (i) => (i.readUint16() - 32768) / 32767,\n snorm16x2: (i): wgsl.v2f =>\n vec2f(dataReaders.snorm16(i), dataReaders.snorm16(i)),\n snorm16x4: (i): wgsl.v4f =>\n vec4f(\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n ),\n float16(i) {\n return i.readFloat16();\n },\n float16x2: (i) => vec2f(i.readFloat16(), i.readFloat16()),\n float16x4: (i) =>\n vec4f(i.readFloat16(), i.readFloat16(), i.readFloat16(), i.readFloat16()),\n float32: (i) => i.readFloat32(),\n float32x2: (i) => vec2f(i.readFloat32(), i.readFloat32()),\n float32x3: (i) => vec3f(i.readFloat32(), i.readFloat32(), i.readFloat32()),\n float32x4: (i) =>\n vec4f(i.readFloat32(), i.readFloat32(), i.readFloat32(), i.readFloat32()),\n uint32: (i) => i.readUint32(),\n uint32x2: (i) => vec2u(i.readUint32(), i.readUint32()),\n uint32x3: (i) => vec3u(i.readUint32(), i.readUint32(), i.readUint32()),\n uint32x4: (i) =>\n vec4u(i.readUint32(), i.readUint32(), i.readUint32(), i.readUint32()),\n sint32: (i) => i.readInt32(),\n sint32x2: (i) => vec2i(i.readInt32(), i.readInt32()),\n sint32x3: (i) => vec3i(i.readInt32(), i.readInt32(), i.readInt32()),\n sint32x4: (i) =>\n vec4i(i.readInt32(), i.readInt32(), i.readInt32(), i.readInt32()),\n 'unorm10-10-10-2'(i) {\n const packed = i.readUint32();\n const r = (packed >> 22) / 1023;\n const g = ((packed >> 12) & 1023) / 1023;\n const b = ((packed >> 2) & 1023) / 1023;\n const a = (packed & 3) / 3;\n return vec4f(r, g, b, a);\n },\n 'unorm8x4-bgra'(i) {\n const b = i.readByte() / 255;\n const g = i.readByte() / 255;\n const r = i.readByte() / 255;\n const a = i.readByte() / 255;\n return vec4f(r, g, b, a);\n },\n\n unstruct(input, schema: Unstruct) {\n const result = {} as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n result[key] = readData(input, property);\n }\n\n return result as InferRecord<Record<string, wgsl.BaseData>>;\n },\n\n disarray(input, schema: Disarray) {\n const alignment = alignmentOf(schema);\n const elements: unknown[] = [];\n\n for (let i = 0; i < schema.elementCount; i++) {\n alignIO(input, alignment);\n elements.push(readData(input, schema.elementType));\n }\n\n alignIO(input, alignment);\n return elements;\n },\n\n 'loose-decorated'(input, schema: LooseDecorated) {\n alignIO(input, customAlignmentOf(schema));\n\n const beginning = input.currentByteOffset;\n const value = readData(input, schema.inner);\n input.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataReaders;\n\nexport function readData<TData extends wgsl.BaseData>(\n input: ISerialInput,\n schema: TData,\n): Infer<TData> {\n const reader = (dataReaders as Record<string, unknown>)[\n schema.type\n ] as DataReader<TData>;\n if (!reader) {\n throw new Error(`Cannot read data of type '${schema.type}'.`);\n }\n\n return reader(input, schema);\n}\n","import type { Block } from 'tinyest';\nimport { resolveData } from './core/resolve/resolveData';\nimport {\n type Eventual,\n type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n isDerived,\n isProviding,\n isSlot,\n} from './core/slot/slotTypes';\nimport { isData } from './data';\nimport { getAttributesString } from './data/attributes';\nimport {\n type AnyWgslData,\n type BaseData,\n isWgslArray,\n isWgslStruct,\n} from './data/wgslTypes';\nimport { MissingSlotValueError, ResolutionError } from './errors';\nimport { RuntimeMode, popMode, provideCtx, pushMode } from './gpuMode';\nimport type { JitTranspiler } from './jitTranspiler';\nimport type { NameRegistry } from './nameRegistry';\nimport { naturalsExcept } from './shared/generators';\nimport type { Infer } from './shared/repr';\nimport { $internal } from './shared/symbols';\nimport { generateFunction } from './smol';\nimport { getTypeFromWgsl } from './smol/generationHelpers';\nimport {\n type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n bindGroupLayout,\n} from './tgpuBindGroupLayout';\nimport type {\n FnToWgslOptions,\n ItemLayer,\n ItemStateStack,\n ResolutionCtx,\n Resource,\n Wgsl,\n} from './types';\nimport { UnknownData, isSelfResolvable, isWgsl } from './types';\n\n/**\n * Inserted into bind group entry definitions that belong\n * to the automatically generated catch-all bind group.\n *\n * A non-occupied group index can only be determined after\n * every resource has been resolved, so this acts as a placeholder\n * to be replaced with an actual numeric index at the very end\n * of the resolution process.\n */\nconst CATCHALL_BIND_GROUP_IDX_MARKER = '#CATCHALL#';\n\nexport type ResolutionCtxImplOptions = {\n readonly names: NameRegistry;\n readonly jitTranspiler?: JitTranspiler | undefined;\n};\n\ntype SlotToValueMap = Map<TgpuSlot<unknown>, unknown>;\n\ntype SlotBindingLayer = {\n type: 'slotBinding';\n bindingMap: WeakMap<TgpuSlot<unknown>, unknown>;\n};\n\ntype FunctionScopeLayer = {\n type: 'functionScope';\n args: Resource[];\n externalMap: Record<string, unknown>;\n returnType: AnyWgslData | undefined;\n};\n\ntype BlockScopeLayer = {\n type: 'blockScope';\n declarations: Map<string, AnyWgslData | UnknownData>;\n};\n\nclass ItemStateStackImpl implements ItemStateStack {\n private _stack: (\n | ItemLayer\n | SlotBindingLayer\n | FunctionScopeLayer\n | BlockScopeLayer\n )[] = [];\n private _itemDepth = 0;\n\n get itemDepth(): number {\n return this._itemDepth;\n }\n\n get topItem(): ItemLayer {\n const state = this._stack[this._stack.length - 1];\n if (!state || state.type !== 'item') {\n throw new Error('Internal error, expected item layer to be on top.');\n }\n return state;\n }\n\n pushItem() {\n this._itemDepth++;\n this._stack.push({\n type: 'item',\n usedSlots: new Set(),\n });\n }\n\n popItem() {\n this.pop('item');\n }\n\n pushSlotBindings(pairs: SlotValuePair<unknown>[]) {\n this._stack.push({\n type: 'slotBinding',\n bindingMap: new WeakMap(pairs),\n });\n }\n\n popSlotBindings() {\n this.pop('slotBinding');\n }\n\n pushFunctionScope(\n args: Resource[],\n returnType: AnyWgslData | undefined,\n externalMap: Record<string, unknown>,\n ) {\n this._stack.push({\n type: 'functionScope',\n args,\n returnType,\n externalMap,\n });\n }\n\n popFunctionScope() {\n this.pop('functionScope');\n }\n\n pushBlockScope() {\n this._stack.push({\n type: 'blockScope',\n declarations: new Map<string, AnyWgslData | UnknownData>(),\n });\n }\n\n popBlockScope() {\n this.pop('blockScope');\n }\n\n pop(type?: (typeof this._stack)[number]['type']) {\n const layer = this._stack[this._stack.length - 1];\n if (!layer || (type && layer.type !== type)) {\n throw new Error(`Internal error, expected a ${type} layer to be on top.`);\n }\n\n this._stack.pop();\n if (type === 'item') {\n this._itemDepth--;\n }\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n if (layer?.type === 'item') {\n // Binding not available yet, so this layer is dependent on the slot's value.\n layer.usedSlots.add(slot);\n } else if (layer?.type === 'slotBinding') {\n const boundValue = layer.bindingMap.get(slot);\n\n if (boundValue !== undefined) {\n return boundValue as T;\n }\n } else if (\n layer?.type === 'functionScope' ||\n layer?.type === 'blockScope'\n ) {\n // Skip\n } else {\n throw new Error('Unknown layer type.');\n }\n }\n\n return slot.defaultValue;\n }\n\n getResourceById(id: string): Resource | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'functionScope') {\n const arg = layer.args.find((a) => a.value === id);\n if (arg !== undefined) {\n return arg;\n }\n\n const external = layer.externalMap[id];\n if (external !== undefined) {\n return {\n value: external,\n dataType: isWgsl(external)\n ? getTypeFromWgsl(external)\n : UnknownData,\n };\n }\n\n // Since functions cannot access resources from the calling scope, we\n // return early here.\n return undefined;\n }\n\n if (layer?.type === 'blockScope') {\n const declarationType = layer.declarations.get(id);\n if (declarationType !== undefined) {\n return { value: id, dataType: declarationType };\n }\n } else {\n // Skip\n }\n }\n\n return undefined;\n }\n\n defineBlockVariable(id: string, type: AnyWgslData | UnknownData): Resource {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'blockScope') {\n layer.declarations.set(id, type);\n\n return { value: id, dataType: type };\n }\n }\n\n throw new Error('No block scope found to define a variable in.');\n }\n}\n\nconst INDENT = [\n '', // 0\n ' ', // 1\n ' ', // 2\n ' ', // 3\n ' ', // 4\n ' ', // 5\n ' ', // 6\n ' ', // 7\n ' ', // 8\n];\n\nconst N = INDENT.length - 1;\n\nexport class IndentController {\n private identLevel = 0;\n\n get pre(): string {\n return (\n INDENT[this.identLevel] ??\n (INDENT[N] as string).repeat(this.identLevel / N) +\n INDENT[this.identLevel % N]\n );\n }\n\n indent(): string {\n const str = this.pre;\n this.identLevel++;\n return str;\n }\n\n dedent(): string {\n this.identLevel--;\n return this.pre;\n }\n}\n\ninterface FixedBindingConfig {\n layoutEntry: TgpuLayoutEntry;\n resource: object;\n}\n\nexport class ResolutionCtxImpl implements ResolutionCtx {\n private readonly _memoizedResolves = new WeakMap<\n // WeakMap because if the item does not exist anymore,\n // apart from this map, there is no way to access the cached value anyway.\n object,\n { slotToValueMap: SlotToValueMap; result: string }[]\n >();\n private readonly _memoizedDerived = new WeakMap<\n // WeakMap because if the \"derived\" does not exist anymore,\n // apart from this map, there is no way to access the cached value anyway.\n TgpuDerived<unknown>,\n { slotToValueMap: SlotToValueMap; result: unknown }[]\n >();\n\n private readonly _indentController = new IndentController();\n private readonly _jitTranspiler: JitTranspiler | undefined;\n private readonly _itemStateStack = new ItemStateStackImpl();\n private readonly _declarations: string[] = [];\n\n readonly [$internal] = {\n itemStateStack: this._itemStateStack,\n };\n\n // -- Bindings\n /**\n * A map from registered bind group layouts to random strings put in\n * place of their group index. The whole tree has to be traversed to\n * collect every use of a typed bind group layout, since they can be\n * explicitly imposed group indices, and they cannot collide.\n */\n public readonly bindGroupLayoutsToPlaceholderMap = new Map<\n TgpuBindGroupLayout,\n string\n >();\n private _nextFreeLayoutPlaceholderIdx = 0;\n public readonly fixedBindings: FixedBindingConfig[] = [];\n // --\n\n public readonly callStack: unknown[] = [];\n public readonly names: NameRegistry;\n\n constructor(opts: ResolutionCtxImplOptions) {\n this.names = opts.names;\n this._jitTranspiler = opts.jitTranspiler;\n }\n\n get pre(): string {\n return this._indentController.pre;\n }\n\n indent(): string {\n return this._indentController.indent();\n }\n\n dedent(): string {\n return this._indentController.dedent();\n }\n\n getById(id: string): Resource | null {\n const item = this._itemStateStack.getResourceById(id);\n\n if (item === undefined) {\n return null;\n }\n\n return item;\n }\n\n defineVariable(id: string, dataType: AnyWgslData | UnknownData): Resource {\n return this._itemStateStack.defineBlockVariable(id, dataType);\n }\n\n pushBlockScope() {\n this._itemStateStack.pushBlockScope();\n }\n\n popBlockScope() {\n this._itemStateStack.popBlockScope();\n }\n\n transpileFn(fn: string): {\n argNames: string[];\n body: Block;\n externalNames: string[];\n } {\n if (!this._jitTranspiler) {\n throw new Error(\n 'Tried to execute a tgpu.fn function without providing a JIT transpiler, or transpiling at build time.',\n );\n }\n\n return this._jitTranspiler.transpileFn(fn);\n }\n\n fnToWgsl(options: FnToWgslOptions): { head: Wgsl; body: Wgsl } {\n this._itemStateStack.pushFunctionScope(\n options.args,\n options.returnType,\n options.externalMap,\n );\n\n try {\n const str = generateFunction(this, options.body);\n\n const argList = options.args\n .map(\n (arg) => `${arg.value}: ${this.resolve(arg.dataType as AnyWgslData)}`,\n )\n .join(', ');\n\n return {\n head:\n options.returnType !== undefined\n ? `(${argList}) -> ${getAttributesString(options.returnType)} ${this.resolve(options.returnType)}`\n : `(${argList})`,\n body: str,\n };\n } finally {\n this._itemStateStack.popFunctionScope();\n }\n }\n\n addDeclaration(declaration: string): void {\n this._declarations.push(declaration);\n }\n\n allocateLayoutEntry(layout: TgpuBindGroupLayout): string {\n const memoMap = this.bindGroupLayoutsToPlaceholderMap;\n let placeholderKey = memoMap.get(layout);\n\n if (!placeholderKey) {\n placeholderKey = `#BIND_GROUP_LAYOUT_${this._nextFreeLayoutPlaceholderIdx++}#`;\n memoMap.set(layout, placeholderKey);\n }\n\n return placeholderKey;\n }\n\n allocateFixedEntry(\n layoutEntry: TgpuLayoutEntry,\n resource: object,\n ): { group: string; binding: number } {\n const binding = this.fixedBindings.length;\n this.fixedBindings.push({ layoutEntry, resource });\n\n return {\n group: CATCHALL_BIND_GROUP_IDX_MARKER,\n binding,\n };\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T {\n const value = this._itemStateStack.readSlot(slot);\n\n if (value === undefined) {\n throw new MissingSlotValueError(slot);\n }\n\n return value;\n }\n\n withSlots<T>(pairs: SlotValuePair<unknown>[], callback: () => T): T {\n this._itemStateStack.pushSlotBindings(pairs);\n\n try {\n return callback();\n } finally {\n this._itemStateStack.popSlotBindings();\n }\n }\n\n unwrap<T>(eventual: Eventual<T>): T {\n if (isProviding(eventual)) {\n return this.withSlots(\n eventual['~providing'].pairs,\n () => this.unwrap(eventual['~providing'].inner) as T,\n );\n }\n\n let maybeEventual = eventual;\n\n // Unwrapping all layers of slots.\n while (true) {\n if (isSlot(maybeEventual)) {\n maybeEventual = this.readSlot(maybeEventual);\n } else if (isDerived(maybeEventual)) {\n maybeEventual = this._getOrCompute(maybeEventual);\n } else {\n break;\n }\n }\n\n return maybeEventual;\n }\n\n _getOrCompute<T>(derived: TgpuDerived<T>): T {\n // All memoized versions of `derived`\n const instances = this._memoizedDerived.get(derived) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue),\n )\n ) {\n return instance.result as T;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n // Derived computations are always done on the CPU\n pushMode(RuntimeMode.CPU);\n\n let result: T;\n try {\n result = derived['~compute']();\n } finally {\n popMode(RuntimeMode.CPU);\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedDerived.set(derived, instances);\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(derived);\n }\n\n throw new ResolutionError(err, [derived]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n /**\n * @param item The item whose resolution should be either retrieved from the cache (if there is a cache hit), or resolved.\n */\n _getOrInstantiate(item: object): string {\n // All memoized versions of `item`\n const instances = this._memoizedResolves.get(item) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue),\n )\n ) {\n return instance.result;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n let result: string;\n if (isData(item)) {\n result = resolveData(this, item);\n } else if (isDerived(item) || isSlot(item)) {\n result = this.resolve(this.unwrap(item));\n } else if (isSelfResolvable(item)) {\n result = item['~resolve'](this);\n } else {\n result = this.resolveValue(item);\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedResolves.set(item, instances);\n\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(item);\n }\n\n throw new ResolutionError(err, [item]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n resolve(item: unknown): string {\n if (isProviding(item)) {\n return this.withSlots(item['~providing'].pairs, () =>\n this.resolve(item['~providing'].inner),\n );\n }\n\n if ((item && typeof item === 'object') || typeof item === 'function') {\n if (this._itemStateStack.itemDepth === 0) {\n try {\n pushMode(RuntimeMode.GPU);\n const result = provideCtx(this, () => this._getOrInstantiate(item));\n return `${[...this._declarations].join('\\n\\n')}${result}`;\n } finally {\n popMode(RuntimeMode.GPU);\n }\n }\n\n return this._getOrInstantiate(item);\n }\n\n return String(item);\n }\n\n resolveValue<T extends BaseData>(\n value: Infer<T>,\n schema?: T | undefined,\n ): string {\n if (isWgsl(value)) {\n return this.resolve(value);\n }\n\n if (schema && isWgslArray(schema)) {\n return `array(${(value as unknown[]).map((element) => this.resolveValue(element, schema.elementType))})`;\n }\n\n if (Array.isArray(value)) {\n return `array(${value.map((element) => this.resolveValue(element))})`;\n }\n\n if (schema && isWgslStruct(schema)) {\n return `${this.resolve(schema)}(${Object.entries(schema.propTypes).map(([key, type_]) => this.resolveValue((value as Infer<typeof schema>)[key], type_))})`;\n }\n\n throw new Error(\n `Value ${value} (as json: ${JSON.stringify(value)}) of schema ${schema} is not resolvable to WGSL`,\n );\n }\n}\n\nexport interface ResolutionResult {\n code: string;\n bindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | null;\n}\n\nexport function resolve(\n item: Wgsl,\n options: ResolutionCtxImplOptions,\n): ResolutionResult {\n const ctx = new ResolutionCtxImpl(options);\n let code = ctx.resolve(item);\n\n const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;\n const bindGroupLayouts: TgpuBindGroupLayout[] = [];\n const takenIndices = new Set<number>(\n [...memoMap.keys()]\n .map((layout) => layout.index)\n .filter((v): v is number => v !== undefined),\n );\n\n const automaticIds = naturalsExcept(takenIndices);\n\n const layoutEntries = ctx.fixedBindings.map(\n (binding, idx) =>\n [String(idx), binding.layoutEntry] as [string, TgpuLayoutEntry],\n );\n\n const createCatchallGroup = () => {\n const catchallIdx = automaticIds.next().value;\n const catchallLayout = bindGroupLayout(Object.fromEntries(layoutEntries));\n bindGroupLayouts[catchallIdx] = catchallLayout;\n code = code.replaceAll(CATCHALL_BIND_GROUP_IDX_MARKER, String(catchallIdx));\n\n return [\n catchallIdx,\n new TgpuBindGroupImpl(\n catchallLayout,\n Object.fromEntries(\n ctx.fixedBindings.map(\n (binding, idx) =>\n // biome-ignore lint/suspicious/noExplicitAny: <it's fine>\n [String(idx), binding.resource] as [string, any],\n ),\n ),\n ),\n ] as [number, TgpuBindGroup];\n };\n\n // Retrieving the catch-all binding index first, because it's inherently\n // the least swapped bind group (fixed and cannot be swapped).\n const catchall = layoutEntries.length > 0 ? createCatchallGroup() : null;\n\n for (const [layout, placeholder] of memoMap.entries()) {\n const idx = layout.index ?? automaticIds.next().value;\n bindGroupLayouts[idx] = layout;\n code = code.replaceAll(placeholder, String(idx));\n }\n\n return {\n code,\n bindGroupLayouts,\n catchall,\n };\n}\n","import { MissingBindGroupsError } from '../../errors';\nimport type { TgpuNamable } from '../../namable';\nimport { resolve } from '../../resolutionCtx';\nimport type {\n TgpuBindGroup,\n TgpuBindGroupLayout,\n} from '../../tgpuBindGroupLayout';\nimport type { TgpuComputeFn } from '../function/tgpuComputeFn';\nimport type { ExperimentalTgpuRoot } from '../root/rootTypes';\nimport type { TgpuSlot } from '../slot/slotTypes';\n\n// ----------\n// Public API\n// ----------\n\nexport interface TgpuComputePipeline extends TgpuNamable {\n readonly resourceType: 'compute-pipeline';\n readonly label: string | undefined;\n\n with(\n bindGroupLayout: TgpuBindGroupLayout,\n bindGroup: TgpuBindGroup,\n ): TgpuComputePipeline;\n\n dispatchWorkgroups(\n x: number,\n y?: number | undefined,\n z?: number | undefined,\n ): void;\n}\n\nexport interface INTERNAL_TgpuComputePipeline {\n readonly rawPipeline: GPUComputePipeline;\n}\n\nexport function INTERNAL_createComputePipeline(\n branch: ExperimentalTgpuRoot,\n slotBindings: [TgpuSlot<unknown>, unknown][],\n entryFn: TgpuComputeFn,\n) {\n return new TgpuComputePipelineImpl(\n new ComputePipelineCore(branch, slotBindings, entryFn),\n {},\n );\n}\n\nexport function isComputePipeline(\n value: unknown,\n): value is TgpuComputePipeline {\n return (value as TgpuComputePipeline)?.resourceType === 'compute-pipeline';\n}\n\n// --------------\n// Implementation\n// --------------\n\ntype TgpuComputePipelinePriors = {\n readonly bindGroupLayoutMap?: Map<TgpuBindGroupLayout, TgpuBindGroup>;\n};\n\ntype Memo = {\n pipeline: GPUComputePipeline;\n bindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | null;\n};\n\nclass TgpuComputePipelineImpl\n implements TgpuComputePipeline, INTERNAL_TgpuComputePipeline\n{\n public readonly resourceType = 'compute-pipeline';\n\n constructor(\n private readonly _core: ComputePipelineCore,\n private readonly _priors: TgpuComputePipelinePriors,\n ) {}\n\n get label(): string | undefined {\n return this._core.label;\n }\n\n get rawPipeline(): GPUComputePipeline {\n return this._core.unwrap().pipeline;\n }\n\n with(\n bindGroupLayout: TgpuBindGroupLayout,\n bindGroup: TgpuBindGroup,\n ): TgpuComputePipeline {\n return new TgpuComputePipelineImpl(this._core, {\n bindGroupLayoutMap: new Map([\n ...(this._priors.bindGroupLayoutMap ?? []),\n [bindGroupLayout, bindGroup],\n ]),\n });\n }\n\n dispatchWorkgroups(\n x: number,\n y?: number | undefined,\n z?: number | undefined,\n ): void {\n const memo = this._core.unwrap();\n const { branch, label } = this._core;\n\n const pass = branch.commandEncoder.beginComputePass({\n label: label ?? '<unnamed>',\n });\n\n pass.setPipeline(memo.pipeline);\n\n const missingBindGroups = new Set(memo.bindGroupLayouts);\n\n memo.bindGroupLayouts.forEach((layout, idx) => {\n if (memo.catchall && idx === memo.catchall[0]) {\n // Catch-all\n pass.setBindGroup(idx, branch.unwrap(memo.catchall[1]));\n missingBindGroups.delete(layout);\n } else {\n const bindGroup = this._priors.bindGroupLayoutMap?.get(layout);\n if (bindGroup !== undefined) {\n missingBindGroups.delete(layout);\n pass.setBindGroup(idx, branch.unwrap(bindGroup));\n }\n }\n });\n\n if (missingBindGroups.size > 0) {\n throw new MissingBindGroupsError(missingBindGroups);\n }\n\n pass.dispatchWorkgroups(x, y, z);\n pass.end();\n }\n\n $name(label?: string | undefined): this {\n this._core.label = label;\n return this;\n }\n}\n\nclass ComputePipelineCore {\n public label: string | undefined;\n private _memo: Memo | undefined;\n\n constructor(\n public readonly branch: ExperimentalTgpuRoot,\n private readonly _slotBindings: [TgpuSlot<unknown>, unknown][],\n private readonly _entryFn: TgpuComputeFn,\n ) {}\n\n public unwrap(): Memo {\n if (this._memo === undefined) {\n const device = this.branch.device;\n\n // Resolving code\n const { code, bindGroupLayouts, catchall } = resolve(\n {\n '~resolve': (ctx) => {\n ctx.withSlots(this._slotBindings, () => {\n ctx.resolve(this._entryFn);\n });\n return '';\n },\n\n toString: () => `computePipeline:${this.label ?? '<unnamed>'}`,\n },\n {\n names: this.branch.nameRegistry,\n jitTranspiler: this.branch.jitTranspiler,\n },\n );\n\n if (catchall !== null) {\n bindGroupLayouts[catchall[0]]?.$name(\n `${this.label ?? '<unnamed>'} - Automatic Bind Group & Layout`,\n );\n }\n\n this._memo = {\n pipeline: device.createComputePipeline({\n label: this.label ?? '<unnamed>',\n layout: device.createPipelineLayout({\n label: `${this.label ?? '<unnamed>'} - Pipeline Layout`,\n bindGroupLayouts: bindGroupLayouts.map((l) =>\n this.branch.unwrap(l),\n ),\n }),\n compute: {\n module: device.createShaderModule({\n label: `${this.label ?? '<unnamed>'} - Shader`,\n code,\n }),\n },\n }),\n bindGroupLayouts,\n catchall,\n };\n }\n\n return this._memo;\n }\n}\n"]}
|
1
|
+
{"version":3,"sources":["/Users/marcinhawryluk/Documents/wigsill/packages/typegpu/dist/index.cjs","../src/smol/generationHelpers.ts","../src/core/resolve/resolveData.ts","../src/smol/wgslGenerator.ts","../src/data/compiledIO.ts","../src/data/dataIO.ts","../src/resolutionCtx.ts","../src/core/pipeline/computePipeline.ts"],"names":["swizzleableTypes","swizzleLenToType","f32","vec2f","vec3f","vec4f","f16","vec2h","vec3h","vec4h","i32","vec2i","vec3i","vec4i","u32","vec2u","vec3u","vec4u","kindToSchema","mat2x2f","mat3x3f","mat4x4f","indexableTypeToResult","getTypeForPropAccess","targetType","propName","_a","UnknownData","isDerived","isSlot","ctx","getResolutionCtx","unwrapped","getTypeFromWgsl","target","hasInternalDataType","$internal","isDecorated","targetTypeStr","propLength","swizzleTypeChar","swizzleType","isWgslData","getTypeForIndexAccess","resource","isWgslArray","_b","numericLiteralToResource","bool","kind","value","abstractInt"],"mappings":"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","file":"/Users/marcinhawryluk/Documents/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import { isDerived, isSlot } from '../core/slot/slotTypes';\nimport { mat2x2f, mat3x3f, mat4x4f } from '../data/matrix';\nimport {\n abstractFloat,\n abstractInt,\n bool,\n f16,\n f32,\n i32,\n u32,\n} from '../data/numeric';\nimport type { WgslStruct } from '../data/struct';\nimport {\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n} from '../data/vector';\nimport {\n type AnyWgslData,\n type BaseData,\n isDecorated,\n isWgslArray,\n isWgslData,\n} from '../data/wgslTypes';\nimport { getResolutionCtx } from '../gpuMode';\nimport { $internal } from '../shared/symbols';\nimport {\n type Resource,\n UnknownData,\n type Wgsl,\n hasInternalDataType,\n} from '../types';\n\nconst swizzleableTypes = [\n 'vec2f',\n 'vec2h',\n 'vec2i',\n 'vec2u',\n 'vec3f',\n 'vec3h',\n 'vec3i',\n 'vec3u',\n 'vec4f',\n 'vec4h',\n 'vec4i',\n 'vec4u',\n 'struct',\n] as const;\n\ntype SwizzleableType = 'f' | 'h' | 'i' | 'u';\ntype SwizzleLength = 1 | 2 | 3 | 4;\n\nconst swizzleLenToType: Record<\n SwizzleableType,\n Record<SwizzleLength, AnyWgslData>\n> = {\n f: {\n 1: f32,\n 2: vec2f,\n 3: vec3f,\n 4: vec4f,\n },\n h: {\n 1: f16,\n 2: vec2h,\n 3: vec3h,\n 4: vec4h,\n },\n i: {\n 1: i32,\n 2: vec2i,\n 3: vec3i,\n 4: vec4i,\n },\n u: {\n 1: u32,\n 2: vec2u,\n 3: vec3u,\n 4: vec4u,\n },\n} as const;\n\nconst kindToSchema = {\n vec2f: vec2f,\n vec2h: vec2h,\n vec2i: vec2i,\n vec2u: vec2u,\n vec3f: vec3f,\n vec3h: vec3h,\n vec3i: vec3i,\n vec3u: vec3u,\n vec4f: vec4f,\n vec4h: vec4h,\n vec4i: vec4i,\n vec4u: vec4u,\n mat2x2f: mat2x2f,\n mat3x3f: mat3x3f,\n mat4x4f: mat4x4f,\n} as const;\n\nconst indexableTypeToResult = {\n vec2f: f32,\n vec2h: f16,\n vec2i: i32,\n vec2u: u32,\n vec3f: f32,\n vec3h: f16,\n vec3i: i32,\n vec3u: u32,\n vec4f: f32,\n vec4h: f16,\n vec4i: i32,\n vec4u: u32,\n mat2x2f: vec2f,\n mat3x3f: vec3f,\n mat4x4f: vec4f,\n} as const;\n\nexport function getTypeForPropAccess(\n targetType: Wgsl,\n propName: string,\n): BaseData | UnknownData {\n if (\n typeof targetType === 'string' ||\n typeof targetType === 'number' ||\n typeof targetType === 'boolean'\n ) {\n return UnknownData;\n }\n\n if (isDerived(targetType) || isSlot(targetType)) {\n const ctx = getResolutionCtx();\n if (!ctx) {\n throw new Error(\n 'Resolution context not found when unwrapping slot or derived',\n );\n }\n const unwrapped = ctx.unwrap(targetType);\n\n return getTypeFromWgsl(unwrapped as Wgsl) as BaseData;\n }\n\n let target = targetType as BaseData;\n\n if (hasInternalDataType(target)) {\n target = target[$internal].dataType;\n }\n while (isDecorated(target)) {\n target = target.inner;\n }\n\n const targetTypeStr =\n 'kind' in target ? (target.kind as string) : target.type;\n\n if (targetTypeStr === 'struct') {\n return (target as WgslStruct).propTypes[propName] ?? UnknownData;\n }\n\n const propLength = propName.length;\n if (\n swizzleableTypes.includes(\n targetTypeStr as (typeof swizzleableTypes)[number],\n ) &&\n propLength >= 1 &&\n propLength <= 4\n ) {\n const swizzleTypeChar = targetTypeStr[4] as SwizzleableType;\n const swizzleType =\n swizzleLenToType[swizzleTypeChar][propLength as SwizzleLength];\n if (swizzleType) {\n return swizzleType;\n }\n }\n\n return isWgslData(target) ? target : UnknownData;\n}\n\nexport function getTypeForIndexAccess(resource: Wgsl): BaseData | UnknownData {\n if (isWgslData(resource)) {\n // array\n if (isWgslArray(resource)) {\n return resource.elementType;\n }\n\n // vector or matrix\n if (resource.type in indexableTypeToResult) {\n return indexableTypeToResult[\n resource.type as keyof typeof indexableTypeToResult\n ];\n }\n }\n\n return UnknownData;\n}\n\nexport function getTypeFromWgsl(resource: Wgsl): BaseData | UnknownData {\n if (isDerived(resource) || isSlot(resource)) {\n return getTypeFromWgsl(resource.value as Wgsl);\n }\n\n if (typeof resource === 'string') {\n return UnknownData;\n }\n if (typeof resource === 'number') {\n return numericLiteralToResource(String(resource))?.dataType ?? UnknownData;\n }\n if (typeof resource === 'boolean') {\n return bool;\n }\n\n if ('kind' in resource) {\n const kind = resource.kind;\n if (kind in kindToSchema) {\n return kindToSchema[kind];\n }\n }\n\n return isWgslData(resource) ? resource : UnknownData;\n}\n\nexport function numericLiteralToResource(value: string): Resource | undefined {\n // Hex literals\n if (/^0x[0-9a-f]+$/i.test(value)) {\n return { value, dataType: abstractInt };\n }\n\n // Binary literals\n if (/^0b[01]+$/i.test(value)) {\n return {\n value: `${Number.parseInt(value.slice(2), 2)}`,\n dataType: abstractInt,\n };\n }\n\n // Floating point literals\n if (/^-?(?:\\d+\\.\\d*|\\d*\\.\\d+)$/i.test(value)) {\n return { value, dataType: abstractFloat };\n }\n\n // Floating point literals with scientific notation\n if (/^-?\\d+(?:\\.\\d+)?e-?\\d+$/i.test(value)) {\n return { value, dataType: abstractFloat };\n }\n\n // Integer literals\n if (/^-?\\d+$/i.test(value)) {\n return { value, dataType: abstractInt };\n }\n\n return undefined;\n}\n","import { getAttributesString } from '../../data/attributes';\nimport {\n type AnyData,\n type Disarray,\n type Unstruct,\n isLooseData,\n} from '../../data/dataTypes';\nimport type { WgslStruct } from '../../data/struct';\nimport { formatToWGSLType } from '../../data/vertexFormatData';\nimport type {\n AnyWgslData,\n BaseData,\n Bool,\n F16,\n F32,\n I32,\n Mat2x2f,\n Mat3x3f,\n Mat4x4f,\n U32,\n Vec2f,\n Vec2h,\n Vec2i,\n Vec2u,\n Vec3f,\n Vec3h,\n Vec3i,\n Vec3u,\n Vec4f,\n Vec4h,\n Vec4i,\n Vec4u,\n WgslArray,\n} from '../../data/wgslTypes';\nimport { assertExhaustive } from '../../shared/utilityTypes';\nimport type { ResolutionCtx } from '../../types';\nimport { isAttribute } from '../vertexLayout/connectAttributesToShader';\n\n/**\n * Schemas for which their `type` property directly\n * translates to the resulting WGSL code.\n */\nconst identityTypes = [\n 'bool',\n 'f32',\n 'f16',\n 'i32',\n 'u32',\n 'vec2f',\n 'vec3f',\n 'vec4f',\n 'vec2h',\n 'vec3h',\n 'vec4h',\n 'vec2i',\n 'vec3i',\n 'vec4i',\n 'vec2u',\n 'vec3u',\n 'vec4u',\n 'mat2x2f',\n 'mat3x3f',\n 'mat4x4f',\n];\n\ntype IdentityType =\n | Bool\n | F32\n | F16\n | I32\n | U32\n | Vec2f\n | Vec3f\n | Vec4f\n | Vec2h\n | Vec3h\n | Vec4h\n | Vec2i\n | Vec3i\n | Vec4i\n | Vec2u\n | Vec3u\n | Vec4u\n | Mat2x2f\n | Mat3x3f\n | Mat4x4f;\n\nfunction isIdentityType(data: AnyWgslData): data is IdentityType {\n return identityTypes.includes(data.type);\n}\n\n/**\n * Resolves a single property of a struct.\n * @param ctx - The resolution context.\n * @param key - The key of the property.\n * @param property - The property itself.\n *\n * @returns The resolved property string.\n */\nfunction resolveStructProperty(\n ctx: ResolutionCtx,\n [key, property]: [string, BaseData],\n) {\n return ` ${getAttributesString(property)}${key}: ${ctx.resolve(property as AnyWgslData)},\\n`;\n}\n\n/**\n * Resolves a struct and adds its declaration to the resolution context.\n * @param ctx - The resolution context.\n * @param struct - The struct to resolve.\n *\n * @returns The resolved struct name.\n */\nfunction resolveStruct(ctx: ResolutionCtx, struct: WgslStruct) {\n const id = ctx.names.makeUnique(struct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(struct.propTypes)\n .map((prop) => resolveStructProperty(ctx, prop))\n .join('')}\\\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an unstruct (struct that does not align data by default) to its struct data counterpart.\n * @param ctx - The resolution context.\n * @param unstruct - The unstruct to resolve.\n *\n * @returns The resolved unstruct name.\n *\n * @example\n * ```ts\n * resolveUnstruct(ctx, {\n * uv: d.float16x2, // -> d.vec2f after resolution\n * color: d.snorm8x4, -> d.vec4f after resolution\n * });\n * ```\n */\nfunction resolveUnstruct(ctx: ResolutionCtx, unstruct: Unstruct) {\n const id = ctx.names.makeUnique(unstruct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(unstruct.propTypes)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [prop[0], formatToWGSLType[prop[1].format]])\n : resolveStructProperty(ctx, prop),\n )\n .join('')}\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an array.\n * @param ctx - The resolution context.\n * @param array - The array to resolve.\n *\n * @returns The resolved array name along with its element type and count (if not runtime-sized).\n *\n * @example\n * ```ts\n * resolveArray(ctx, d.arrayOf(d.u32, 0)); // 'array<u32>' (not a real pattern, a function is preferred)\n * resolveArray(ctx, d.arrayOf(d.u32, 5)); // 'array<u32, 5>'\n * ```\n */\nfunction resolveArray(ctx: ResolutionCtx, array: WgslArray) {\n const element = ctx.resolve(array.elementType as AnyWgslData);\n\n return array.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${array.elementCount}>`;\n}\n\nfunction resolveDisarray(ctx: ResolutionCtx, disarray: Disarray) {\n const element = ctx.resolve(\n isAttribute(disarray.elementType)\n ? formatToWGSLType[disarray.elementType.format]\n : (disarray.elementType as AnyWgslData),\n );\n\n return disarray.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${disarray.elementCount}>`;\n}\n\n/**\n * Resolves a WGSL data-type schema to a string.\n * @param ctx - The resolution context.\n * @param data - The data-type to resolve.\n *\n * @returns The resolved data-type string.\n */\nexport function resolveData(ctx: ResolutionCtx, data: AnyData): string {\n if (isLooseData(data)) {\n if (data.type === 'unstruct') {\n return resolveUnstruct(ctx, data);\n }\n\n if (data.type === 'disarray') {\n return resolveDisarray(ctx, data);\n }\n\n if (data.type === 'loose-decorated') {\n return ctx.resolve(\n isAttribute(data.inner)\n ? formatToWGSLType[data.inner.format]\n : data.inner,\n );\n }\n\n return ctx.resolve(formatToWGSLType[data.type]);\n }\n\n if (isIdentityType(data)) {\n return data.type;\n }\n\n if (data.type === 'struct') {\n return resolveStruct(ctx, data);\n }\n\n if (data.type === 'array') {\n return resolveArray(ctx, data);\n }\n\n if (data.type === 'atomic') {\n return `atomic<${resolveData(ctx, data.inner)}>`;\n }\n\n if (data.type === 'decorated') {\n return ctx.resolve(data.inner as AnyWgslData);\n }\n\n if (data.type === 'ptr') {\n if (data.addressSpace === 'storage') {\n return `ptr<storage, ${ctx.resolve(data.inner)}, ${data.access === 'read-write' ? 'read_write' : data.access}>`;\n }\n return `ptr<${data.addressSpace}, ${ctx.resolve(data.inner)}>`;\n }\n\n if (data.type === 'abstractInt' || data.type === 'abstractFloat') {\n throw new Error('Abstract types have no concrete representation in WGSL');\n }\n\n assertExhaustive(data, 'resolveData');\n}\n","import type * as smol from 'tinyest';\nimport * as d from '../data';\nimport * as wgsl from '../data/wgslTypes';\nimport {\n type ResolutionCtx,\n type Resource,\n UnknownData,\n type Wgsl,\n isWgsl,\n} from '../types';\nimport {\n getTypeForIndexAccess,\n getTypeForPropAccess,\n getTypeFromWgsl,\n numericLiteralToResource,\n} from './generationHelpers';\n\nconst parenthesizedOps = [\n '==',\n '!=',\n '<',\n '<=',\n '>',\n '>=',\n '<<',\n '>>',\n '+',\n '-',\n '*',\n '/',\n '%',\n '|',\n '^',\n '&',\n '&&',\n '||',\n];\n\nconst binaryLogicalOps = ['&&', '||', '==', '!=', '<', '<=', '>', '>='];\n\ntype Operator =\n | smol.BinaryOperator\n | smol.AssignmentOperator\n | smol.LogicalOperator\n | smol.UnaryOperator;\n\nfunction operatorToType<\n TL extends wgsl.AnyWgslData | UnknownData,\n TR extends wgsl.AnyWgslData | UnknownData,\n>(lhs: TL, op: Operator, rhs?: TR): TL | TR | wgsl.Bool {\n if (!rhs) {\n if (op === '!' || op === '~') {\n return d.bool;\n }\n\n return lhs;\n }\n\n if (binaryLogicalOps.includes(op)) {\n return d.bool;\n }\n\n if (op === '=') {\n return rhs;\n }\n\n return lhs;\n}\n\nexport type GenerationCtx = ResolutionCtx & {\n readonly pre: string;\n readonly callStack: unknown[];\n indent(): string;\n dedent(): string;\n pushBlockScope(): void;\n popBlockScope(): void;\n getById(id: string): Resource | null;\n defineVariable(\n id: string,\n dataType: wgsl.AnyWgslData | UnknownData,\n ): Resource;\n};\n\nexport function resolveRes(ctx: GenerationCtx, res: Resource): string {\n if (isWgsl(res.value)) {\n return ctx.resolve(res.value);\n }\n\n return String(res.value);\n}\n\nfunction assertExhaustive(value: never): never {\n throw new Error(\n `'${JSON.stringify(value)}' was not handled by the WGSL generator.`,\n );\n}\n\nexport function generateBoolean(ctx: GenerationCtx, value: boolean): Resource {\n return { value: value ? 'true' : 'false', dataType: d.bool };\n}\n\nexport function generateBlock(ctx: GenerationCtx, value: smol.Block): string {\n ctx.pushBlockScope();\n try {\n return `${ctx.indent()}{\n${value.b.map((statement) => generateStatement(ctx, statement)).join('\\n')}\n${ctx.dedent()}}`;\n } finally {\n ctx.popBlockScope();\n }\n}\n\nexport function registerBlockVariable(\n ctx: GenerationCtx,\n id: string,\n dataType: wgsl.AnyWgslData | UnknownData,\n): Resource {\n return ctx.defineVariable(id, dataType);\n}\n\nexport function generateIdentifier(ctx: GenerationCtx, id: string): Resource {\n const res = ctx.getById(id);\n if (!res) {\n throw new Error(`Identifier ${id} not found`);\n }\n\n return res;\n}\n\nexport function generateExpression(\n ctx: GenerationCtx,\n expression: smol.Expression,\n): Resource {\n if (typeof expression === 'string') {\n return generateIdentifier(ctx, expression);\n }\n if (typeof expression === 'boolean') {\n return generateBoolean(ctx, expression);\n }\n\n if ('x' in expression) {\n // Logical/Binary/Assignment Expression\n const [lhs, op, rhs] = expression.x;\n const lhsExpr = generateExpression(ctx, lhs);\n const rhsExpr = generateExpression(ctx, rhs);\n\n const lhsStr = resolveRes(ctx, lhsExpr);\n const rhsStr = resolveRes(ctx, rhsExpr);\n const type = operatorToType(lhsExpr.dataType, op, rhsExpr.dataType);\n\n return {\n value: parenthesizedOps.includes(op)\n ? `(${lhsStr} ${op} ${rhsStr})`\n : `${lhsStr} ${op} ${rhsStr}`,\n dataType: type,\n };\n }\n\n if ('p' in expression) {\n // Update Expression\n const [op, arg] = expression.p;\n const argExpr = generateExpression(ctx, arg);\n const argStr = resolveRes(ctx, argExpr);\n\n return {\n value: `${argStr}${op}`,\n dataType: argExpr.dataType,\n };\n }\n\n if ('u' in expression) {\n // Unary Expression\n const [op, arg] = expression.u;\n const argExpr = generateExpression(ctx, arg);\n const argStr = resolveRes(ctx, argExpr);\n\n const type = operatorToType(argExpr.dataType, op);\n return {\n value: `${op}${argStr}`,\n dataType: type,\n };\n }\n\n if ('a' in expression) {\n // Member Access\n const [targetId, property] = expression.a;\n const target = generateExpression(ctx, targetId);\n\n if (typeof target.value === 'string') {\n return {\n value: `${target.value}.${property}`,\n dataType: getTypeForPropAccess(target.dataType as Wgsl, property),\n };\n }\n // biome-ignore lint/suspicious/noExplicitAny: <sorry TypeScript>\n const propValue = (target.value as any)[property];\n\n if (isWgsl(target.value)) {\n return {\n value: propValue,\n dataType: getTypeForPropAccess(target.value as d.AnyWgslData, property),\n };\n }\n\n if (typeof target.value === 'object') {\n const dataType = isWgsl(propValue)\n ? getTypeFromWgsl(propValue)\n : UnknownData;\n\n return {\n value: propValue,\n dataType,\n };\n }\n\n throw new Error(`Cannot access member ${property} of ${target.value}`);\n }\n\n if ('i' in expression) {\n // Index Access\n const [target, property] = expression.i;\n const targetExpr = generateExpression(ctx, target);\n const propertyExpr = generateExpression(ctx, property);\n const targetStr = resolveRes(ctx, targetExpr);\n const propertyStr = resolveRes(ctx, propertyExpr);\n\n return {\n value: `${targetStr}[${propertyStr}]`,\n dataType: getTypeForIndexAccess(targetExpr.dataType as d.AnyWgslData),\n };\n }\n\n if ('n' in expression) {\n // Numeric Literal\n const type = numericLiteralToResource(expression.n);\n if (!type) {\n throw new Error(`Invalid numeric literal ${expression.n}`);\n }\n return type;\n }\n\n if ('f' in expression) {\n // Function Call\n const [callee, args] = expression.f;\n const id = generateExpression(ctx, callee);\n const idValue = id.value;\n\n ctx.callStack.push(idValue);\n\n const argResources = args.map((arg) => generateExpression(ctx, arg));\n const resolvedResources = argResources.map((res) => ({\n value: resolveRes(ctx, res),\n dataType: res.dataType,\n }));\n const argValues = resolvedResources.map((res) => res.value);\n\n ctx.callStack.pop();\n\n if (typeof idValue === 'string') {\n return {\n value: `${idValue}(${argValues.join(', ')})`,\n dataType: id.dataType,\n };\n }\n\n if (wgsl.isWgslStruct(idValue)) {\n const resolvedId = ctx.resolve(idValue);\n\n return {\n value: `${resolvedId}(${argValues.join(', ')})`,\n dataType: id.dataType,\n };\n }\n\n // Assuming that `id` is callable\n return (idValue as unknown as (...args: unknown[]) => unknown)(\n ...resolvedResources,\n ) as Resource;\n }\n\n if ('o' in expression) {\n // Object Literal\n const obj = expression.o;\n const callee = ctx.callStack[ctx.callStack.length - 1];\n\n const generateEntries = (values: smol.Expression[]) =>\n values\n .map((value) => {\n const valueRes = generateExpression(ctx, value);\n return resolveRes(ctx, valueRes);\n })\n .join(', ');\n\n if (wgsl.isWgslStruct(callee)) {\n const propKeys = Object.keys(callee.propTypes);\n const values = propKeys.map((key) => {\n const val = obj[key];\n if (val === undefined) {\n throw new Error(\n `Missing property ${key} in object literal for struct ${callee}`,\n );\n }\n return val;\n });\n\n return {\n value: generateEntries(values),\n dataType: callee,\n };\n }\n\n return {\n value: generateEntries(Object.values(obj)),\n dataType: UnknownData,\n };\n }\n\n if ('y' in expression) {\n // Array Expression\n const values = expression.y.map((value) => {\n return generateExpression(ctx, value);\n });\n if (values.length === 0) {\n throw new Error('Cannot create empty array literal.');\n }\n\n let type = values[0]?.dataType;\n const mismatchedType = values.find((value) => value.dataType !== type);\n if (mismatchedType) {\n throw new Error(\n `Cannot mix types in array literal. Type ${mismatchedType.dataType.type} does not match expected type ${type?.type}.`,\n );\n }\n\n if (!wgsl.isWgslData(type)) {\n throw new Error('Cannot use non-WGSL data types in array literals.');\n }\n\n type =\n type.type === 'abstractInt'\n ? d.u32\n : type.type === 'abstractFloat'\n ? d.f32\n : type;\n\n const typeId = ctx.resolve(type);\n\n const arrayType = `array<${typeId}, ${values.length}>`;\n const arrayValues = values.map((value) => resolveRes(ctx, value));\n\n return {\n value: `${arrayType}( ${arrayValues.join(', ')} )`,\n dataType: d.arrayOf(type as d.AnyWgslData, values.length),\n };\n }\n\n if ('s' in expression) {\n throw new Error('Cannot use string literals in TGSL.');\n }\n\n assertExhaustive(expression);\n}\n\nexport function generateStatement(\n ctx: GenerationCtx,\n statement: smol.Statement,\n): string {\n if (typeof statement === 'string') {\n return `${ctx.pre}${resolveRes(ctx, generateIdentifier(ctx, statement))};`;\n }\n\n if (typeof statement === 'boolean') {\n return `${ctx.pre}${resolveRes(ctx, generateBoolean(ctx, statement))};`;\n }\n\n if ('r' in statement) {\n // check if the thing at the top of the call stack is a struct and the statement is a plain JS object\n // if so wrap the value returned in a constructor of the struct (its resolved name)\n if (\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1]) &&\n statement.r !== null &&\n typeof statement.r === 'object' &&\n 'o' in statement.r\n ) {\n const resource = resolveRes(ctx, generateExpression(ctx, statement.r));\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}return ${resolvedStruct}(${resource});`;\n }\n\n return statement.r === null\n ? `${ctx.pre}return;`\n : `${ctx.pre}return ${resolveRes(\n ctx,\n generateExpression(ctx, statement.r),\n )};`;\n }\n\n if ('q' in statement) {\n const [cond, cons, alt] = statement.q;\n const condition = resolveRes(ctx, generateExpression(ctx, cond));\n\n ctx.indent(); // {\n const consequent = generateStatement(ctx, cons);\n ctx.dedent(); // }\n\n ctx.indent(); // {\n const alternate = alt ? generateStatement(ctx, alt) : undefined;\n ctx.dedent(); // }\n\n if (!alternate) {\n return `\\\n${ctx.pre}if (${condition})\n${consequent}`;\n }\n\n return `\\\n${ctx.pre}if (${condition})\n${consequent}\n${ctx.pre}else\n${alternate}`;\n }\n\n if ('l' in statement || 'c' in statement) {\n const [rawId, rawValue] = 'l' in statement ? statement.l : statement.c;\n const eq = rawValue ? generateExpression(ctx, rawValue) : undefined;\n\n if (!eq || !rawValue) {\n throw new Error('Cannot create variable without an initial value.');\n }\n\n registerBlockVariable(ctx, rawId, eq.dataType);\n const id = resolveRes(ctx, generateIdentifier(ctx, rawId));\n\n // If the value is a plain JS object it has to be an output struct\n if (\n typeof rawValue === 'object' &&\n 'o' in rawValue &&\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1])\n ) {\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}var ${id} = ${resolvedStruct}(${resolveRes(ctx, eq)});`;\n }\n\n return `${ctx.pre}var ${id} = ${resolveRes(ctx, eq)};`;\n }\n\n if ('b' in statement) {\n ctx.pushBlockScope();\n try {\n return generateBlock(ctx, statement);\n } finally {\n ctx.popBlockScope();\n }\n }\n\n // 'j' stands for for (trust me)\n if ('j' in statement) {\n const [init, condition, update, body] = statement.j;\n\n const initStatement = init ? generateStatement(ctx, init) : undefined;\n const initStr = initStatement ? initStatement.slice(0, -1) : '';\n\n const conditionExpr = condition\n ? generateExpression(ctx, condition)\n : undefined;\n const conditionStr = conditionExpr ? resolveRes(ctx, conditionExpr) : '';\n\n const updateStatement = update ? generateStatement(ctx, update) : undefined;\n const updateStr = updateStatement ? updateStatement.slice(0, -1) : '';\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}for (${initStr}; ${conditionStr}; ${updateStr})\n${bodyStr}`;\n }\n\n if ('w' in statement) {\n const [condition, body] = statement.w;\n const conditionStr = resolveRes(ctx, generateExpression(ctx, condition));\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}while (${conditionStr})\n${bodyStr}`;\n }\n\n if ('k' in statement) {\n return `${ctx.pre}continue;`;\n }\n\n if ('d' in statement) {\n return `${ctx.pre}break;`;\n }\n\n return `${ctx.pre}${resolveRes(ctx, generateExpression(ctx, statement))};`;\n}\n\nexport function generateFunction(ctx: GenerationCtx, body: smol.Block): string {\n return generateBlock(ctx, body);\n}\n","import { roundUp } from '../mathUtils';\nimport type { Infer } from '../shared/repr';\nimport { alignmentOf } from './alignmentOf';\nimport { isDisarray, isUnstruct } from './dataTypes';\nimport { offsetsForProps } from './offsets';\nimport { sizeOf } from './sizeOf';\nimport * as wgsl from './wgslTypes';\n\nexport const EVAL_ALLOWED_IN_ENV: boolean = (() => {\n try {\n new Function('return true');\n return true;\n } catch {\n return false;\n }\n})();\n\nconst compiledWriters = new WeakMap<\n wgsl.BaseData,\n (\n output: DataView,\n offset: number,\n value: unknown,\n littleEndian?: boolean,\n ) => void\n>();\n\nconst typeToPrimitive = {\n u32: 'u32',\n vec2u: 'u32',\n vec3u: 'u32',\n vec4u: 'u32',\n\n i32: 'i32',\n vec2i: 'i32',\n vec3i: 'i32',\n vec4i: 'i32',\n\n f32: 'f32',\n vec2f: 'f32',\n vec3f: 'f32',\n vec4f: 'f32',\n\n vec2h: 'f32',\n vec3h: 'f32',\n vec4h: 'f32',\n\n mat2x2f: 'f32',\n mat3x3f: 'f32',\n mat4x4f: 'f32',\n} as const;\n\nconst primitiveToWriteFunction = {\n u32: 'setUint32',\n i32: 'setInt32',\n f32: 'setFloat32',\n} as const;\n\nexport function buildWriter(\n node: wgsl.BaseData,\n offsetExpr: string,\n valueExpr: string,\n): string {\n if (wgsl.isWgslStruct(node) || isUnstruct(node)) {\n const propOffsets = offsetsForProps(node);\n const sortedProps = Object.entries(propOffsets).sort(\n (a, b) => a[1].offset - b[1].offset,\n );\n let code = '';\n for (const [key, propOffset] of sortedProps) {\n const subSchema = node.propTypes[key];\n if (!subSchema) continue;\n code += buildWriter(\n subSchema,\n `(${offsetExpr} + ${propOffset.offset})`,\n `${valueExpr}.${key}`,\n );\n }\n return code;\n }\n\n if (wgsl.isWgslArray(node) || isDisarray(node)) {\n const arrSchema = node as wgsl.WgslArray;\n const elementSize = roundUp(\n sizeOf(arrSchema.elementType),\n alignmentOf(arrSchema.elementType),\n );\n let code = '';\n\n code += `for (let i = 0; i < ${arrSchema.elementCount}; i++) {\\n`;\n code += buildWriter(\n arrSchema.elementType,\n `(${offsetExpr} + i * ${elementSize})`,\n `${valueExpr}[i]`,\n );\n code += '}\\n';\n\n return code;\n }\n\n if (wgsl.isVec(node)) {\n const primitive = typeToPrimitive[node.type];\n let code = '';\n const writeFunc = primitiveToWriteFunction[primitive];\n const components = ['x', 'y', 'z', 'w'];\n const count = wgsl.isVec2(node) ? 2 : wgsl.isVec3(node) ? 3 : 4;\n\n for (let i = 0; i < count; i++) {\n code += `output.${writeFunc}((${offsetExpr} + ${i * 4}), ${valueExpr}.${components[i]}, littleEndian);\\n`;\n }\n return code;\n }\n\n if (wgsl.isMat(node)) {\n const primitive = typeToPrimitive[node.type];\n const writeFunc = primitiveToWriteFunction[primitive];\n\n const matSize = wgsl.isMat2x2f(node) ? 2 : wgsl.isMat3x3f(node) ? 3 : 4;\n const elementCount = matSize * matSize;\n const rowStride = roundUp(matSize * 4, 8);\n\n let code = '';\n for (let i = 0; i < elementCount; i++) {\n const colIndex = Math.floor(i / matSize);\n const rowIndex = i % matSize;\n const byteOffset = colIndex * rowStride + rowIndex * 4;\n\n code += `output.${writeFunc}((${offsetExpr} + ${byteOffset}), ${valueExpr}.columns[${colIndex}].${['x', 'y', 'z', 'w'][rowIndex]}, littleEndian);\\n`;\n }\n\n return code;\n }\n\n let unwrappedNode = node;\n\n while (wgsl.isAtomic(unwrappedNode) || wgsl.isDecorated(unwrappedNode)) {\n unwrappedNode = unwrappedNode.inner;\n }\n\n const primitive =\n typeToPrimitive[unwrappedNode.type as keyof typeof typeToPrimitive];\n return `output.${primitiveToWriteFunction[primitive]}(${offsetExpr}, ${valueExpr}, littleEndian);\\n`;\n}\n\nexport function getCompiledWriterForSchema<T extends wgsl.BaseData>(\n schema: T,\n): (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n) => void {\n if (compiledWriters.has(schema)) {\n return compiledWriters.get(schema) as (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n ) => void;\n }\n\n const body = buildWriter(schema, 'offset', 'value');\n\n const fn = new Function(\n 'output',\n 'offset',\n 'value',\n 'littleEndian=true',\n body,\n ) as (\n output: DataView,\n offset: number,\n value: Infer<T> | unknown,\n littleEndian?: boolean,\n ) => void;\n\n compiledWriters.set(schema, fn);\n\n return fn;\n}\n","import type { ISerialInput, ISerialOutput } from 'typed-binary';\nimport type { Infer, InferRecord } from '../shared/repr';\nimport alignIO from './alignIO';\nimport { alignmentOf, customAlignmentOf } from './alignmentOf';\nimport type {\n AnyConcreteData,\n AnyData,\n Disarray,\n LooseDecorated,\n Unstruct,\n} from './dataTypes';\nimport { mat2x2f, mat3x3f, mat4x4f } from './matrix';\nimport { sizeOf } from './sizeOf';\nimport type { WgslStruct } from './struct';\nimport {\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n} from './vector';\nimport type * as wgsl from './wgslTypes';\n\ntype DataWriter<TSchema extends wgsl.BaseData> = (\n output: ISerialOutput,\n schema: TSchema,\n value: Infer<TSchema>,\n) => void;\n\ntype DataReader<TSchema extends wgsl.BaseData> = (\n input: ISerialInput,\n schema: TSchema,\n) => Infer<TSchema>;\n\ntype CompleteDataWriters = {\n [TType in AnyConcreteData['type']]: DataWriter<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\ntype CompleteDataReaders = {\n [TType in AnyConcreteData['type']]: DataReader<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\nconst dataWriters = {\n bool(output, _schema: wgsl.Bool, value: boolean) {\n output.writeBool(value);\n },\n\n f32(output, _schema: wgsl.F32, value: number) {\n output.writeFloat32(value);\n },\n\n f16(output, _schema: wgsl.F16, value: number) {\n output.writeFloat16(value);\n },\n\n i32(output, _schema: wgsl.I32, value: number) {\n output.writeInt32(value);\n },\n\n u32(output, _schema: wgsl.U32, value: number) {\n output.writeUint32(value);\n },\n\n vec2f(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n\n vec2h(output, _, value: wgsl.v2h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n\n vec2i(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n\n vec2u(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n\n vec3f(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n\n vec3h(output, _, value: wgsl.v3h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n },\n\n vec3i(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n\n vec3u(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n\n vec4f(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n\n vec4h(output, _, value: wgsl.v4h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n\n vec4i(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n\n vec4u(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n\n mat2x2f(output, _, value: wgsl.m2x2f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat3x3f(output, _, value: wgsl.m3x3f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat4x4f(output, _, value: wgsl.m4x4f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n struct(\n output,\n schema: WgslStruct,\n value: InferRecord<Record<string, wgsl.BaseData>>,\n ) {\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(output, alignmentOf(property));\n writeData(output, property, value[key] as wgsl.BaseData);\n }\n\n alignIO(output, alignment);\n },\n\n array(output, schema: wgsl.WgslArray, value: Infer<wgsl.BaseData>[]) {\n if (schema.elementCount === 0) {\n throw new Error('Cannot write using a runtime-sized schema.');\n }\n\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n const beginning = output.currentByteOffset;\n for (let i = 0; i < Math.min(schema.elementCount, value.length); i++) {\n alignIO(output, alignment);\n writeData(output, schema.elementType, value[i]);\n }\n output.seekTo(beginning + sizeOf(schema));\n },\n\n ptr() {\n throw new Error('Pointers are not host-shareable');\n },\n\n atomic(output, schema: wgsl.Atomic, value: number) {\n dataWriters[schema.inner.type]?.(output, schema, value);\n },\n\n decorated(output, schema: wgsl.Decorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n dataWriters[(schema.inner as AnyData)?.type]?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n },\n\n // Loose Types\n\n uint8(output, _, value: number) {\n output.writeUint8(value);\n },\n uint8x2(output, _, value: wgsl.v2u) {\n output.writeUint8(value.x);\n output.writeUint8(value.y);\n },\n uint8x4(output, _, value: wgsl.v4u) {\n output.writeUint8(value.x);\n output.writeUint8(value.y);\n output.writeUint8(value.z);\n output.writeUint8(value.w);\n },\n sint8(output, _, value: number) {\n output.writeInt8(value);\n },\n sint8x2(output, _, value: wgsl.v2i) {\n output.writeInt8(value.x);\n output.writeInt8(value.y);\n },\n sint8x4(output, _, value: wgsl.v4i) {\n output.writeInt8(value.x);\n output.writeInt8(value.y);\n output.writeInt8(value.z);\n output.writeInt8(value.w);\n },\n unorm8(output, _, value: number) {\n output.writeUint8(value * 255);\n },\n unorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(value.x * 255);\n output.writeUint8(value.y * 255);\n },\n unorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(value.x * 255);\n output.writeUint8(value.y * 255);\n output.writeUint8(value.z * 255);\n output.writeUint8(value.w * 255);\n },\n snorm8(output, _, value: number) {\n output.writeUint8(value * 127 + 128);\n },\n snorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(value.x * 127 + 128);\n output.writeUint8(value.y * 127 + 128);\n },\n snorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(value.x * 127 + 128);\n output.writeUint8(value.y * 127 + 128);\n output.writeUint8(value.z * 127 + 128);\n output.writeUint8(value.w * 127 + 128);\n },\n uint16(output, _, value: number) {\n output.writeUint16(value);\n },\n uint16x2(output, _, value: wgsl.v2u) {\n output.writeUint16(value.x);\n output.writeUint16(value.y);\n },\n uint16x4(output, _, value: wgsl.v4u) {\n output.writeUint16(value.x);\n output.writeUint16(value.y);\n output.writeUint16(value.z);\n output.writeUint16(value.w);\n },\n sint16(output, _, value: number) {\n output.writeInt16(value);\n },\n sint16x2(output, _, value: wgsl.v2i) {\n output.writeInt16(value.x);\n output.writeInt16(value.y);\n },\n sint16x4(output, _, value: wgsl.v4i) {\n output.writeInt16(value.x);\n output.writeInt16(value.y);\n output.writeInt16(value.z);\n output.writeInt16(value.w);\n },\n unorm16(output, _, value: number) {\n output.writeUint16(value * 65535);\n },\n unorm16x2(output, _, value: wgsl.v2f) {\n output.writeUint16(value.x * 65535);\n output.writeUint16(value.y * 65535);\n },\n unorm16x4(output, _, value: wgsl.v4f) {\n output.writeUint16(value.x * 65535);\n output.writeUint16(value.y * 65535);\n output.writeUint16(value.z * 65535);\n output.writeUint16(value.w * 65535);\n },\n snorm16(output, _, value: number) {\n output.writeUint16(value * 32767 + 32768);\n },\n snorm16x2(output, _, value: wgsl.v2f) {\n output.writeUint16(value.x * 32767 + 32768);\n output.writeUint16(value.y * 32767 + 32768);\n },\n snorm16x4(output, _, value: wgsl.v4f) {\n output.writeUint16(value.x * 32767 + 32768);\n output.writeUint16(value.y * 32767 + 32768);\n output.writeUint16(value.z * 32767 + 32768);\n output.writeUint16(value.w * 32767 + 32768);\n },\n float16(output, _, value: number) {\n output.writeFloat16(value);\n },\n float16x2(output, _, value: wgsl.v2f) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n float16x4(output, _, value: wgsl.v4f) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n float32(output, _, value: number) {\n output.writeFloat32(value);\n },\n float32x2(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n float32x3(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n float32x4(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n uint32(output, _, value: number) {\n output.writeUint32(value);\n },\n uint32x2(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n uint32x3(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n uint32x4(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n sint32(output, _, value: number) {\n output.writeInt32(value);\n },\n sint32x2(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n sint32x3(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n sint32x4(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n 'unorm10-10-10-2'(output, _, value: wgsl.v4f) {\n let packed = 0;\n packed |= ((value.x * 1023) & 1023) << 22; // r (10 bits)\n packed |= ((value.x * 1023) & 1023) << 12; // g (10 bits)\n packed |= ((value.y * 1023) & 1023) << 2; // b (10 bits)\n packed |= (value.z * 3) & 3; // a (2 bits)\n output.writeUint32(packed);\n },\n 'unorm8x4-bgra'(output, _, value: wgsl.v4f) {\n output.writeUint8(value.z * 255);\n output.writeUint8(value.y * 255);\n output.writeUint8(value.x * 255);\n output.writeUint8(value.w * 255);\n },\n\n disarray(output, schema: Disarray, value: unknown[]) {\n const alignment = alignmentOf(schema);\n\n alignIO(output, alignment);\n const beginning = output.currentByteOffset;\n for (let i = 0; i < Math.min(schema.elementCount, value.length); i++) {\n alignIO(output, alignment);\n dataWriters[(schema.elementType as AnyData)?.type]?.(\n output,\n schema.elementType,\n value[i],\n );\n }\n\n output.seekTo(beginning + sizeOf(schema));\n },\n\n unstruct(output, schema: Unstruct, value) {\n for (const [key, property] of Object.entries(schema.propTypes)) {\n dataWriters[property.type]?.(output, property, value[key]);\n }\n },\n\n 'loose-decorated'(output, schema: LooseDecorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n const writer = dataWriters[(schema.inner as AnyData)?.type];\n writer?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataWriters as Record<\n string,\n (output: ISerialOutput, schema: unknown, value: unknown) => void\n>;\n\nexport function writeData<TData extends wgsl.BaseData>(\n output: ISerialOutput,\n schema: TData,\n value: Infer<TData>,\n): void {\n const writer = dataWriters[schema.type];\n if (!writer) {\n throw new Error(`Cannot write data of type '${schema.type}'.`);\n }\n\n writer(output, schema, value);\n}\n\nconst dataReaders = {\n bool(input: ISerialInput): boolean {\n return input.readBool();\n },\n\n f32(input: ISerialInput): number {\n return input.readFloat32();\n },\n\n f16(input: ISerialInput): number {\n return input.readFloat16();\n },\n\n i32(input: ISerialInput): number {\n return input.readInt32();\n },\n\n u32(input: ISerialInput): number {\n return input.readUint32();\n },\n\n vec2f(input: ISerialInput): wgsl.v2f {\n return vec2f(input.readFloat32(), input.readFloat32());\n },\n\n vec3f(input: ISerialInput): wgsl.v3f {\n return vec3f(input.readFloat32(), input.readFloat32(), input.readFloat32());\n },\n\n vec4f(input: ISerialInput): wgsl.v4f {\n return vec4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n vec2h(input): wgsl.v2h {\n return vec2h(input.readFloat16(), input.readFloat16());\n },\n\n vec3h(input: ISerialInput): wgsl.v3h {\n return vec3h(input.readFloat16(), input.readFloat16(), input.readFloat16());\n },\n\n vec4h(input: ISerialInput): wgsl.v4h {\n return vec4h(\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n );\n },\n\n vec2i(input): wgsl.v2i {\n return vec2i(input.readInt32(), input.readInt32());\n },\n\n vec3i(input: ISerialInput): wgsl.v3i {\n return vec3i(input.readInt32(), input.readInt32(), input.readInt32());\n },\n\n vec4i(input: ISerialInput): wgsl.v4i {\n return vec4i(\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n );\n },\n\n vec2u(input): wgsl.v2u {\n return vec2u(input.readUint32(), input.readUint32());\n },\n\n vec3u(input: ISerialInput): wgsl.v3u {\n return vec3u(input.readUint32(), input.readUint32(), input.readUint32());\n },\n\n vec4u(input: ISerialInput): wgsl.v4u {\n return vec4u(\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n );\n },\n\n mat2x2f(input: ISerialInput): wgsl.m2x2f {\n return mat2x2f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n mat3x3f(input: ISerialInput): wgsl.m3x3f {\n const skipOneAfter = () => {\n const value = input.readFloat32();\n input.readFloat32(); // skipping;\n return value;\n };\n\n return mat3x3f(\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n );\n },\n\n mat4x4f(input: ISerialInput): wgsl.m4x4f {\n return mat4x4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n struct(input: ISerialInput, schema: WgslStruct) {\n const alignment = alignmentOf(schema);\n alignIO(input, alignment);\n const result = {} as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(input, alignmentOf(property));\n result[key] = readData(input, property);\n }\n\n alignIO(input, alignment);\n return result as InferRecord<Record<string, wgsl.BaseData>>;\n },\n\n array(input, schema) {\n if (schema.elementCount === 0) {\n throw new Error('Cannot read using a runtime-sized schema.');\n }\n\n const alignment = alignmentOf(schema);\n const elements: unknown[] = [];\n\n for (let i = 0; i < schema.elementCount; i++) {\n alignIO(input, alignment);\n const elementType = schema.elementType as wgsl.AnyWgslData;\n const value = readData(input, elementType);\n elements.push(value);\n }\n\n alignIO(input, alignment);\n return elements as never[];\n },\n\n ptr() {\n throw new Error('Pointers are not host-shareable');\n },\n\n atomic(input, schema: wgsl.Atomic): number {\n return readData(input, schema.inner);\n },\n\n decorated(input, schema: wgsl.Decorated) {\n const alignment = customAlignmentOf(schema);\n alignIO(input, alignment);\n\n const beginning = input.currentByteOffset;\n const value = readData(input, schema.inner);\n input.seekTo(beginning + sizeOf(schema));\n return value as never;\n },\n\n // Loose Types\n\n uint8: (i) => i.readUint8(),\n uint8x2: (i) => vec2u(i.readUint8(), i.readUint8()),\n uint8x4: (i) =>\n vec4u(i.readUint8(), i.readUint8(), i.readUint8(), i.readUint8()),\n sint8: (i) => i.readInt8(),\n sint8x2: (i) => {\n return vec2i(i.readInt8(), i.readInt8());\n },\n sint8x4: (i) => vec4i(i.readInt8(), i.readInt8(), i.readInt8(), i.readInt8()),\n unorm8: (i) => i.readUint8() / 255,\n unorm8x2: (i) => vec2f(i.readUint8() / 255, i.readUint8() / 255),\n unorm8x4: (i) =>\n vec4f(\n i.readUint8() / 255,\n i.readUint8() / 255,\n i.readUint8() / 255,\n i.readUint8() / 255,\n ),\n snorm8: (i) => (i.readUint8() - 128) / 127,\n snorm8x2: (i) =>\n vec2f((i.readUint8() - 128) / 127, (i.readUint8() - 128) / 127),\n snorm8x4: (i) =>\n vec4f(\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n ),\n uint16: (i) => i.readUint16(),\n uint16x2: (i) => vec2u(i.readUint16(), i.readUint16()),\n uint16x4: (i) =>\n vec4u(i.readUint16(), i.readUint16(), i.readUint16(), i.readUint16()),\n sint16: (i) => i.readInt16(),\n sint16x2: (i) => vec2i(i.readInt16(), i.readInt16()),\n sint16x4: (i) =>\n vec4i(i.readInt16(), i.readInt16(), i.readInt16(), i.readInt16()),\n unorm16: (i) => i.readUint16() / 65535,\n unorm16x2: (i) => vec2f(i.readUint16() / 65535, i.readUint16() / 65535),\n unorm16x4: (i) =>\n vec4f(\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n ),\n snorm16: (i) => (i.readUint16() - 32768) / 32767,\n snorm16x2: (i): wgsl.v2f =>\n vec2f(dataReaders.snorm16(i), dataReaders.snorm16(i)),\n snorm16x4: (i): wgsl.v4f =>\n vec4f(\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n ),\n float16(i) {\n return i.readFloat16();\n },\n float16x2: (i) => vec2f(i.readFloat16(), i.readFloat16()),\n float16x4: (i) =>\n vec4f(i.readFloat16(), i.readFloat16(), i.readFloat16(), i.readFloat16()),\n float32: (i) => i.readFloat32(),\n float32x2: (i) => vec2f(i.readFloat32(), i.readFloat32()),\n float32x3: (i) => vec3f(i.readFloat32(), i.readFloat32(), i.readFloat32()),\n float32x4: (i) =>\n vec4f(i.readFloat32(), i.readFloat32(), i.readFloat32(), i.readFloat32()),\n uint32: (i) => i.readUint32(),\n uint32x2: (i) => vec2u(i.readUint32(), i.readUint32()),\n uint32x3: (i) => vec3u(i.readUint32(), i.readUint32(), i.readUint32()),\n uint32x4: (i) =>\n vec4u(i.readUint32(), i.readUint32(), i.readUint32(), i.readUint32()),\n sint32: (i) => i.readInt32(),\n sint32x2: (i) => vec2i(i.readInt32(), i.readInt32()),\n sint32x3: (i) => vec3i(i.readInt32(), i.readInt32(), i.readInt32()),\n sint32x4: (i) =>\n vec4i(i.readInt32(), i.readInt32(), i.readInt32(), i.readInt32()),\n 'unorm10-10-10-2'(i) {\n const packed = i.readUint32();\n const r = (packed >> 22) / 1023;\n const g = ((packed >> 12) & 1023) / 1023;\n const b = ((packed >> 2) & 1023) / 1023;\n const a = (packed & 3) / 3;\n return vec4f(r, g, b, a);\n },\n 'unorm8x4-bgra'(i) {\n const b = i.readByte() / 255;\n const g = i.readByte() / 255;\n const r = i.readByte() / 255;\n const a = i.readByte() / 255;\n return vec4f(r, g, b, a);\n },\n\n unstruct(input, schema: Unstruct) {\n const result = {} as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n result[key] = readData(input, property);\n }\n\n return result as InferRecord<Record<string, wgsl.BaseData>>;\n },\n\n disarray(input, schema: Disarray) {\n const alignment = alignmentOf(schema);\n const elements: unknown[] = [];\n\n for (let i = 0; i < schema.elementCount; i++) {\n alignIO(input, alignment);\n elements.push(readData(input, schema.elementType));\n }\n\n alignIO(input, alignment);\n return elements;\n },\n\n 'loose-decorated'(input, schema: LooseDecorated) {\n alignIO(input, customAlignmentOf(schema));\n\n const beginning = input.currentByteOffset;\n const value = readData(input, schema.inner);\n input.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataReaders;\n\nexport function readData<TData extends wgsl.BaseData>(\n input: ISerialInput,\n schema: TData,\n): Infer<TData> {\n const reader = (dataReaders as Record<string, unknown>)[\n schema.type\n ] as DataReader<TData>;\n if (!reader) {\n throw new Error(`Cannot read data of type '${schema.type}'.`);\n }\n\n return reader(input, schema);\n}\n","import type { Block } from 'tinyest';\nimport { resolveData } from './core/resolve/resolveData';\nimport {\n type Eventual,\n type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n isDerived,\n isProviding,\n isSlot,\n} from './core/slot/slotTypes';\nimport { isData } from './data';\nimport { getAttributesString } from './data/attributes';\nimport {\n type AnyWgslData,\n type BaseData,\n isWgslArray,\n isWgslStruct,\n} from './data/wgslTypes';\nimport { MissingSlotValueError, ResolutionError } from './errors';\nimport { RuntimeMode, popMode, provideCtx, pushMode } from './gpuMode';\nimport type { JitTranspiler } from './jitTranspiler';\nimport type { NameRegistry } from './nameRegistry';\nimport { naturalsExcept } from './shared/generators';\nimport type { Infer } from './shared/repr';\nimport { $internal } from './shared/symbols';\nimport { generateFunction } from './smol';\nimport { getTypeFromWgsl } from './smol/generationHelpers';\nimport {\n type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n bindGroupLayout,\n} from './tgpuBindGroupLayout';\nimport type {\n FnToWgslOptions,\n ItemLayer,\n ItemStateStack,\n ResolutionCtx,\n Resource,\n Wgsl,\n} from './types';\nimport { UnknownData, isSelfResolvable, isWgsl } from './types';\n\n/**\n * Inserted into bind group entry definitions that belong\n * to the automatically generated catch-all bind group.\n *\n * A non-occupied group index can only be determined after\n * every resource has been resolved, so this acts as a placeholder\n * to be replaced with an actual numeric index at the very end\n * of the resolution process.\n */\nconst CATCHALL_BIND_GROUP_IDX_MARKER = '#CATCHALL#';\n\nexport type ResolutionCtxImplOptions = {\n readonly names: NameRegistry;\n readonly jitTranspiler?: JitTranspiler | undefined;\n};\n\ntype SlotToValueMap = Map<TgpuSlot<unknown>, unknown>;\n\ntype SlotBindingLayer = {\n type: 'slotBinding';\n bindingMap: WeakMap<TgpuSlot<unknown>, unknown>;\n};\n\ntype FunctionScopeLayer = {\n type: 'functionScope';\n args: Resource[];\n externalMap: Record<string, unknown>;\n returnType: AnyWgslData | undefined;\n};\n\ntype BlockScopeLayer = {\n type: 'blockScope';\n declarations: Map<string, AnyWgslData | UnknownData>;\n};\n\nclass ItemStateStackImpl implements ItemStateStack {\n private _stack: (\n | ItemLayer\n | SlotBindingLayer\n | FunctionScopeLayer\n | BlockScopeLayer\n )[] = [];\n private _itemDepth = 0;\n\n get itemDepth(): number {\n return this._itemDepth;\n }\n\n get topItem(): ItemLayer {\n const state = this._stack[this._stack.length - 1];\n if (!state || state.type !== 'item') {\n throw new Error('Internal error, expected item layer to be on top.');\n }\n return state;\n }\n\n pushItem() {\n this._itemDepth++;\n this._stack.push({\n type: 'item',\n usedSlots: new Set(),\n });\n }\n\n popItem() {\n this.pop('item');\n }\n\n pushSlotBindings(pairs: SlotValuePair<unknown>[]) {\n this._stack.push({\n type: 'slotBinding',\n bindingMap: new WeakMap(pairs),\n });\n }\n\n popSlotBindings() {\n this.pop('slotBinding');\n }\n\n pushFunctionScope(\n args: Resource[],\n returnType: AnyWgslData | undefined,\n externalMap: Record<string, unknown>,\n ) {\n this._stack.push({\n type: 'functionScope',\n args,\n returnType,\n externalMap,\n });\n }\n\n popFunctionScope() {\n this.pop('functionScope');\n }\n\n pushBlockScope() {\n this._stack.push({\n type: 'blockScope',\n declarations: new Map<string, AnyWgslData | UnknownData>(),\n });\n }\n\n popBlockScope() {\n this.pop('blockScope');\n }\n\n pop(type?: (typeof this._stack)[number]['type']) {\n const layer = this._stack[this._stack.length - 1];\n if (!layer || (type && layer.type !== type)) {\n throw new Error(`Internal error, expected a ${type} layer to be on top.`);\n }\n\n this._stack.pop();\n if (type === 'item') {\n this._itemDepth--;\n }\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n if (layer?.type === 'item') {\n // Binding not available yet, so this layer is dependent on the slot's value.\n layer.usedSlots.add(slot);\n } else if (layer?.type === 'slotBinding') {\n const boundValue = layer.bindingMap.get(slot);\n\n if (boundValue !== undefined) {\n return boundValue as T;\n }\n } else if (\n layer?.type === 'functionScope' ||\n layer?.type === 'blockScope'\n ) {\n // Skip\n } else {\n throw new Error('Unknown layer type.');\n }\n }\n\n return slot.defaultValue;\n }\n\n getResourceById(id: string): Resource | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'functionScope') {\n const arg = layer.args.find((a) => a.value === id);\n if (arg !== undefined) {\n return arg;\n }\n\n const external = layer.externalMap[id];\n if (external !== undefined) {\n return {\n value: external,\n dataType: isWgsl(external)\n ? getTypeFromWgsl(external)\n : UnknownData,\n };\n }\n\n // Since functions cannot access resources from the calling scope, we\n // return early here.\n return undefined;\n }\n\n if (layer?.type === 'blockScope') {\n const declarationType = layer.declarations.get(id);\n if (declarationType !== undefined) {\n return { value: id, dataType: declarationType };\n }\n } else {\n // Skip\n }\n }\n\n return undefined;\n }\n\n defineBlockVariable(id: string, type: AnyWgslData | UnknownData): Resource {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'blockScope') {\n layer.declarations.set(id, type);\n\n return { value: id, dataType: type };\n }\n }\n\n throw new Error('No block scope found to define a variable in.');\n }\n}\n\nconst INDENT = [\n '', // 0\n ' ', // 1\n ' ', // 2\n ' ', // 3\n ' ', // 4\n ' ', // 5\n ' ', // 6\n ' ', // 7\n ' ', // 8\n];\n\nconst N = INDENT.length - 1;\n\nexport class IndentController {\n private identLevel = 0;\n\n get pre(): string {\n return (\n INDENT[this.identLevel] ??\n (INDENT[N] as string).repeat(this.identLevel / N) +\n INDENT[this.identLevel % N]\n );\n }\n\n indent(): string {\n const str = this.pre;\n this.identLevel++;\n return str;\n }\n\n dedent(): string {\n this.identLevel--;\n return this.pre;\n }\n}\n\ninterface FixedBindingConfig {\n layoutEntry: TgpuLayoutEntry;\n resource: object;\n}\n\nexport class ResolutionCtxImpl implements ResolutionCtx {\n private readonly _memoizedResolves = new WeakMap<\n // WeakMap because if the item does not exist anymore,\n // apart from this map, there is no way to access the cached value anyway.\n object,\n { slotToValueMap: SlotToValueMap; result: string }[]\n >();\n private readonly _memoizedDerived = new WeakMap<\n // WeakMap because if the \"derived\" does not exist anymore,\n // apart from this map, there is no way to access the cached value anyway.\n TgpuDerived<unknown>,\n { slotToValueMap: SlotToValueMap; result: unknown }[]\n >();\n\n private readonly _indentController = new IndentController();\n private readonly _jitTranspiler: JitTranspiler | undefined;\n private readonly _itemStateStack = new ItemStateStackImpl();\n private readonly _declarations: string[] = [];\n\n readonly [$internal] = {\n itemStateStack: this._itemStateStack,\n };\n\n // -- Bindings\n /**\n * A map from registered bind group layouts to random strings put in\n * place of their group index. The whole tree has to be traversed to\n * collect every use of a typed bind group layout, since they can be\n * explicitly imposed group indices, and they cannot collide.\n */\n public readonly bindGroupLayoutsToPlaceholderMap = new Map<\n TgpuBindGroupLayout,\n string\n >();\n private _nextFreeLayoutPlaceholderIdx = 0;\n public readonly fixedBindings: FixedBindingConfig[] = [];\n // --\n\n public readonly callStack: unknown[] = [];\n public readonly names: NameRegistry;\n\n constructor(opts: ResolutionCtxImplOptions) {\n this.names = opts.names;\n this._jitTranspiler = opts.jitTranspiler;\n }\n\n get pre(): string {\n return this._indentController.pre;\n }\n\n indent(): string {\n return this._indentController.indent();\n }\n\n dedent(): string {\n return this._indentController.dedent();\n }\n\n getById(id: string): Resource | null {\n const item = this._itemStateStack.getResourceById(id);\n\n if (item === undefined) {\n return null;\n }\n\n return item;\n }\n\n defineVariable(id: string, dataType: AnyWgslData | UnknownData): Resource {\n return this._itemStateStack.defineBlockVariable(id, dataType);\n }\n\n pushBlockScope() {\n this._itemStateStack.pushBlockScope();\n }\n\n popBlockScope() {\n this._itemStateStack.popBlockScope();\n }\n\n transpileFn(fn: string): {\n argNames: string[];\n body: Block;\n externalNames: string[];\n } {\n if (!this._jitTranspiler) {\n throw new Error(\n 'Tried to execute a tgpu.fn function without providing a JIT transpiler, or transpiling at build time.',\n );\n }\n\n return this._jitTranspiler.transpileFn(fn);\n }\n\n fnToWgsl(options: FnToWgslOptions): { head: Wgsl; body: Wgsl } {\n this._itemStateStack.pushFunctionScope(\n options.args,\n options.returnType,\n options.externalMap,\n );\n\n try {\n const str = generateFunction(this, options.body);\n\n const argList = options.args\n .map(\n (arg) => `${arg.value}: ${this.resolve(arg.dataType as AnyWgslData)}`,\n )\n .join(', ');\n\n return {\n head:\n options.returnType !== undefined\n ? `(${argList}) -> ${getAttributesString(options.returnType)} ${this.resolve(options.returnType)}`\n : `(${argList})`,\n body: str,\n };\n } finally {\n this._itemStateStack.popFunctionScope();\n }\n }\n\n addDeclaration(declaration: string): void {\n this._declarations.push(declaration);\n }\n\n allocateLayoutEntry(layout: TgpuBindGroupLayout): string {\n const memoMap = this.bindGroupLayoutsToPlaceholderMap;\n let placeholderKey = memoMap.get(layout);\n\n if (!placeholderKey) {\n placeholderKey = `#BIND_GROUP_LAYOUT_${this._nextFreeLayoutPlaceholderIdx++}#`;\n memoMap.set(layout, placeholderKey);\n }\n\n return placeholderKey;\n }\n\n allocateFixedEntry(\n layoutEntry: TgpuLayoutEntry,\n resource: object,\n ): { group: string; binding: number } {\n const binding = this.fixedBindings.length;\n this.fixedBindings.push({ layoutEntry, resource });\n\n return {\n group: CATCHALL_BIND_GROUP_IDX_MARKER,\n binding,\n };\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T {\n const value = this._itemStateStack.readSlot(slot);\n\n if (value === undefined) {\n throw new MissingSlotValueError(slot);\n }\n\n return value;\n }\n\n withSlots<T>(pairs: SlotValuePair<unknown>[], callback: () => T): T {\n this._itemStateStack.pushSlotBindings(pairs);\n\n try {\n return callback();\n } finally {\n this._itemStateStack.popSlotBindings();\n }\n }\n\n unwrap<T>(eventual: Eventual<T>): T {\n if (isProviding(eventual)) {\n return this.withSlots(\n eventual['~providing'].pairs,\n () => this.unwrap(eventual['~providing'].inner) as T,\n );\n }\n\n let maybeEventual = eventual;\n\n // Unwrapping all layers of slots.\n while (true) {\n if (isSlot(maybeEventual)) {\n maybeEventual = this.readSlot(maybeEventual);\n } else if (isDerived(maybeEventual)) {\n maybeEventual = this._getOrCompute(maybeEventual);\n } else {\n break;\n }\n }\n\n return maybeEventual;\n }\n\n _getOrCompute<T>(derived: TgpuDerived<T>): T {\n // All memoized versions of `derived`\n const instances = this._memoizedDerived.get(derived) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue),\n )\n ) {\n return instance.result as T;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n // Derived computations are always done on the CPU\n pushMode(RuntimeMode.CPU);\n\n let result: T;\n try {\n result = derived['~compute']();\n } finally {\n popMode(RuntimeMode.CPU);\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedDerived.set(derived, instances);\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(derived);\n }\n\n throw new ResolutionError(err, [derived]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n /**\n * @param item The item whose resolution should be either retrieved from the cache (if there is a cache hit), or resolved.\n */\n _getOrInstantiate(item: object): string {\n // All memoized versions of `item`\n const instances = this._memoizedResolves.get(item) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue),\n )\n ) {\n return instance.result;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n let result: string;\n if (isData(item)) {\n result = resolveData(this, item);\n } else if (isDerived(item) || isSlot(item)) {\n result = this.resolve(this.unwrap(item));\n } else if (isSelfResolvable(item)) {\n result = item['~resolve'](this);\n } else {\n result = this.resolveValue(item);\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedResolves.set(item, instances);\n\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(item);\n }\n\n throw new ResolutionError(err, [item]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n resolve(item: unknown): string {\n if (isProviding(item)) {\n return this.withSlots(item['~providing'].pairs, () =>\n this.resolve(item['~providing'].inner),\n );\n }\n\n if ((item && typeof item === 'object') || typeof item === 'function') {\n if (this._itemStateStack.itemDepth === 0) {\n try {\n pushMode(RuntimeMode.GPU);\n const result = provideCtx(this, () => this._getOrInstantiate(item));\n return `${[...this._declarations].join('\\n\\n')}${result}`;\n } finally {\n popMode(RuntimeMode.GPU);\n }\n }\n\n return this._getOrInstantiate(item);\n }\n\n return String(item);\n }\n\n resolveValue<T extends BaseData>(\n value: Infer<T>,\n schema?: T | undefined,\n ): string {\n if (isWgsl(value)) {\n return this.resolve(value);\n }\n\n if (schema && isWgslArray(schema)) {\n return `array(${(value as unknown[]).map((element) => this.resolveValue(element, schema.elementType))})`;\n }\n\n if (Array.isArray(value)) {\n return `array(${value.map((element) => this.resolveValue(element))})`;\n }\n\n if (schema && isWgslStruct(schema)) {\n return `${this.resolve(schema)}(${Object.entries(schema.propTypes).map(([key, type_]) => this.resolveValue((value as Infer<typeof schema>)[key], type_))})`;\n }\n\n throw new Error(\n `Value ${value} (as json: ${JSON.stringify(value)}) of schema ${schema} is not resolvable to WGSL`,\n );\n }\n}\n\nexport interface ResolutionResult {\n code: string;\n bindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | null;\n}\n\nexport function resolve(\n item: Wgsl,\n options: ResolutionCtxImplOptions,\n): ResolutionResult {\n const ctx = new ResolutionCtxImpl(options);\n let code = ctx.resolve(item);\n\n const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;\n const bindGroupLayouts: TgpuBindGroupLayout[] = [];\n const takenIndices = new Set<number>(\n [...memoMap.keys()]\n .map((layout) => layout.index)\n .filter((v): v is number => v !== undefined),\n );\n\n const automaticIds = naturalsExcept(takenIndices);\n\n const layoutEntries = ctx.fixedBindings.map(\n (binding, idx) =>\n [String(idx), binding.layoutEntry] as [string, TgpuLayoutEntry],\n );\n\n const createCatchallGroup = () => {\n const catchallIdx = automaticIds.next().value;\n const catchallLayout = bindGroupLayout(Object.fromEntries(layoutEntries));\n bindGroupLayouts[catchallIdx] = catchallLayout;\n code = code.replaceAll(CATCHALL_BIND_GROUP_IDX_MARKER, String(catchallIdx));\n\n return [\n catchallIdx,\n new TgpuBindGroupImpl(\n catchallLayout,\n Object.fromEntries(\n ctx.fixedBindings.map(\n (binding, idx) =>\n // biome-ignore lint/suspicious/noExplicitAny: <it's fine>\n [String(idx), binding.resource] as [string, any],\n ),\n ),\n ),\n ] as [number, TgpuBindGroup];\n };\n\n // Retrieving the catch-all binding index first, because it's inherently\n // the least swapped bind group (fixed and cannot be swapped).\n const catchall = layoutEntries.length > 0 ? createCatchallGroup() : null;\n\n for (const [layout, placeholder] of memoMap.entries()) {\n const idx = layout.index ?? automaticIds.next().value;\n bindGroupLayouts[idx] = layout;\n code = code.replaceAll(placeholder, String(idx));\n }\n\n return {\n code,\n bindGroupLayouts,\n catchall,\n };\n}\n","import { MissingBindGroupsError } from '../../errors';\nimport type { TgpuNamable } from '../../namable';\nimport { resolve } from '../../resolutionCtx';\nimport type {\n TgpuBindGroup,\n TgpuBindGroupLayout,\n} from '../../tgpuBindGroupLayout';\nimport type { TgpuComputeFn } from '../function/tgpuComputeFn';\nimport type { ExperimentalTgpuRoot } from '../root/rootTypes';\nimport type { TgpuSlot } from '../slot/slotTypes';\n\n// ----------\n// Public API\n// ----------\n\nexport interface TgpuComputePipeline extends TgpuNamable {\n readonly resourceType: 'compute-pipeline';\n readonly label: string | undefined;\n\n with(\n bindGroupLayout: TgpuBindGroupLayout,\n bindGroup: TgpuBindGroup,\n ): TgpuComputePipeline;\n\n dispatchWorkgroups(\n x: number,\n y?: number | undefined,\n z?: number | undefined,\n ): void;\n}\n\nexport interface INTERNAL_TgpuComputePipeline {\n readonly rawPipeline: GPUComputePipeline;\n}\n\nexport function INTERNAL_createComputePipeline(\n branch: ExperimentalTgpuRoot,\n slotBindings: [TgpuSlot<unknown>, unknown][],\n entryFn: TgpuComputeFn,\n) {\n return new TgpuComputePipelineImpl(\n new ComputePipelineCore(branch, slotBindings, entryFn),\n {},\n );\n}\n\nexport function isComputePipeline(\n value: unknown,\n): value is TgpuComputePipeline {\n return (value as TgpuComputePipeline)?.resourceType === 'compute-pipeline';\n}\n\n// --------------\n// Implementation\n// --------------\n\ntype TgpuComputePipelinePriors = {\n readonly bindGroupLayoutMap?: Map<TgpuBindGroupLayout, TgpuBindGroup>;\n};\n\ntype Memo = {\n pipeline: GPUComputePipeline;\n bindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | null;\n};\n\nclass TgpuComputePipelineImpl\n implements TgpuComputePipeline, INTERNAL_TgpuComputePipeline\n{\n public readonly resourceType = 'compute-pipeline';\n\n constructor(\n private readonly _core: ComputePipelineCore,\n private readonly _priors: TgpuComputePipelinePriors,\n ) {}\n\n get label(): string | undefined {\n return this._core.label;\n }\n\n get rawPipeline(): GPUComputePipeline {\n return this._core.unwrap().pipeline;\n }\n\n with(\n bindGroupLayout: TgpuBindGroupLayout,\n bindGroup: TgpuBindGroup,\n ): TgpuComputePipeline {\n return new TgpuComputePipelineImpl(this._core, {\n bindGroupLayoutMap: new Map([\n ...(this._priors.bindGroupLayoutMap ?? []),\n [bindGroupLayout, bindGroup],\n ]),\n });\n }\n\n dispatchWorkgroups(\n x: number,\n y?: number | undefined,\n z?: number | undefined,\n ): void {\n const memo = this._core.unwrap();\n const { branch, label } = this._core;\n\n const pass = branch.commandEncoder.beginComputePass({\n label: label ?? '<unnamed>',\n });\n\n pass.setPipeline(memo.pipeline);\n\n const missingBindGroups = new Set(memo.bindGroupLayouts);\n\n memo.bindGroupLayouts.forEach((layout, idx) => {\n if (memo.catchall && idx === memo.catchall[0]) {\n // Catch-all\n pass.setBindGroup(idx, branch.unwrap(memo.catchall[1]));\n missingBindGroups.delete(layout);\n } else {\n const bindGroup = this._priors.bindGroupLayoutMap?.get(layout);\n if (bindGroup !== undefined) {\n missingBindGroups.delete(layout);\n pass.setBindGroup(idx, branch.unwrap(bindGroup));\n }\n }\n });\n\n if (missingBindGroups.size > 0) {\n throw new MissingBindGroupsError(missingBindGroups);\n }\n\n pass.dispatchWorkgroups(x, y, z);\n pass.end();\n }\n\n $name(label?: string | undefined): this {\n this._core.label = label;\n return this;\n }\n}\n\nclass ComputePipelineCore {\n public label: string | undefined;\n private _memo: Memo | undefined;\n\n constructor(\n public readonly branch: ExperimentalTgpuRoot,\n private readonly _slotBindings: [TgpuSlot<unknown>, unknown][],\n private readonly _entryFn: TgpuComputeFn,\n ) {}\n\n public unwrap(): Memo {\n if (this._memo === undefined) {\n const device = this.branch.device;\n\n // Resolving code\n const { code, bindGroupLayouts, catchall } = resolve(\n {\n '~resolve': (ctx) => {\n ctx.withSlots(this._slotBindings, () => {\n ctx.resolve(this._entryFn);\n });\n return '';\n },\n\n toString: () => `computePipeline:${this.label ?? '<unnamed>'}`,\n },\n {\n names: this.branch.nameRegistry,\n jitTranspiler: this.branch.jitTranspiler,\n },\n );\n\n if (catchall !== null) {\n bindGroupLayouts[catchall[0]]?.$name(\n `${this.label ?? '<unnamed>'} - Automatic Bind Group & Layout`,\n );\n }\n\n this._memo = {\n pipeline: device.createComputePipeline({\n label: this.label ?? '<unnamed>',\n layout: device.createPipelineLayout({\n label: `${this.label ?? '<unnamed>'} - Pipeline Layout`,\n bindGroupLayouts: bindGroupLayouts.map((l) =>\n this.branch.unwrap(l),\n ),\n }),\n compute: {\n module: device.createShaderModule({\n label: `${this.label ?? '<unnamed>'} - Shader`,\n code,\n }),\n },\n }),\n bindGroupLayouts,\n catchall,\n };\n }\n\n return this._memo;\n }\n}\n"]}
|