typegpu 0.5.7 → 0.5.9
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-734H4JWF.js +2 -0
- package/chunk-734H4JWF.js.map +1 -0
- package/chunk-BJSN5UVV.cjs +41 -0
- package/chunk-BJSN5UVV.cjs.map +1 -0
- package/chunk-BZVU4Q6A.cjs +2 -0
- package/chunk-BZVU4Q6A.cjs.map +1 -0
- package/chunk-CDUKTYES.cjs +2 -0
- package/chunk-CDUKTYES.cjs.map +1 -0
- package/chunk-CGVYLGBG.js +2 -0
- package/chunk-CGVYLGBG.js.map +1 -0
- package/chunk-SWJVYRFM.js +41 -0
- package/chunk-SWJVYRFM.js.map +1 -0
- package/data/index.cjs +1 -1
- package/data/index.cjs.map +1 -1
- package/data/index.d.cts +4 -77
- package/data/index.d.ts +4 -77
- package/data/index.js +1 -1
- package/index.cjs +21 -20
- package/index.cjs.map +1 -1
- package/index.d.cts +5 -3
- package/index.d.ts +5 -3
- package/index.js +21 -20
- package/index.js.map +1 -1
- package/matrix-BULDDGYa.d.ts +122 -0
- package/matrix-BnXitNJ7.d.cts +122 -0
- package/package.json +2 -2
- package/std/index.cjs +1 -1
- package/std/index.cjs.map +1 -1
- package/std/index.d.cts +104 -17
- package/std/index.d.ts +104 -17
- package/std/index.js +1 -1
- package/std/index.js.map +1 -1
- package/{tgpuComputeFn-DH8_PcIR.d.cts → tgpuComputeFn-BxPDI5hQ.d.cts} +178 -95
- package/{tgpuComputeFn-DH8_PcIR.d.ts → tgpuComputeFn-BxPDI5hQ.d.ts} +178 -95
- package/chunk-5RYM4COI.js +0 -4
- package/chunk-5RYM4COI.js.map +0 -1
- package/chunk-FHOQ6WZE.cjs +0 -2
- package/chunk-FHOQ6WZE.cjs.map +0 -1
- package/chunk-ODKBWWHU.cjs +0 -4
- package/chunk-ODKBWWHU.cjs.map +0 -1
- package/chunk-SMTSXYNG.js +0 -2
- package/chunk-SMTSXYNG.js.map +0 -1
package/index.cjs.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","../src/core/slot/slotTypes.ts","../src/shared/utilityTypes.ts","../src/tgsl/generationHelpers.ts","../src/core/valueProxyUtils.ts","../src/core/resolve/resolveData.ts","../src/data/compiledIO.ts","../src/data/dataIO.ts","../src/tgsl/wgslGenerator.ts","../src/resolutionCtx.ts"],"names":["isSlot","value","isDerived","isProviding","isAccessor","assertExhaustive","x","location","ctx","type"],"mappings":"AAAA,y3CAAiL,wDAAmf,SC+DppBA,CAAAA,CAAUC,CAAAA,CAAoD,CAC5E,uBAAQA,CAAAA,6BAAuB,cAAA,GAAiB,MAClD,CAEO,SAASC,CAAAA,CACdD,CAAAA,CACY,CACZ,uBAAQA,CAAAA,6BAAa,cAAA,GAAiB,SACxC,CAEO,SAASE,EAAAA,CACdF,CAAAA,CACsC,CACtC,uBAAQA,CAAAA,4BAAAA,CAAwC,YAAY,GAAA,GAAM,KAAA,CACpE,CAEO,SAASG,EAAAA,CACdH,CAAAA,CAC0B,CAC1B,uBAAQA,CAAAA,6BAA2B,cAAA,GAAiB,UACtD,CCxCO,SAASI,EAAAA,CAAiBC,CAAAA,CAAUC,CAAAA,CAAyB,CAClE,MAAM,IAAI,KAAA,CAAM,CAAA,iBAAA,EAAoBD,CAAC,CAAA,IAAA,EAAOC,CAAQ,CAAA,CAAA;AC8kB1C;AAEN;AAAoC,KAAA;ACxlBN,4CAAA;AC4Ff;AACV,OAAA;AAKT;AAwBmB;AACV,OAAA;AAYT;AAAA;AAiGkB;ACzKR;AAeN;AAsBE;AASsB;ACvBhB;ACzB0D;AAAK;AAwG9D;AAiXH;AACH;AAPgB;AA4FrBC;AAwB2B;AC9gBZC;AA4a8B;AAqGK","file":"/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import type { AnyWgslData } from '../../data/wgslTypes.ts';\nimport type { TgpuNamable } from '../../name.ts';\nimport type { $repr, Infer } from '../../shared/repr.ts';\nimport type { TgpuFn } from '../function/tgpuFn.ts';\nimport type { TgpuBufferUsage } from './../buffer/bufferUsage.ts';\n\nexport interface TgpuSlot<T> extends TgpuNamable {\n readonly resourceType: 'slot';\n [$repr]: Infer<T>;\n\n readonly defaultValue: T | undefined;\n\n /**\n * Used to determine if code generated using either value `a` or `b` in place\n * of the slot will be equivalent. Defaults to `Object.is`.\n */\n areEqual(a: T, b: T): boolean;\n\n readonly value: Infer<T>;\n}\n\nexport interface TgpuDerived<T> {\n readonly resourceType: 'derived';\n readonly value: Infer<T>;\n [$repr]: Infer<T>;\n readonly '~providing'?: Providing | undefined;\n\n with<TValue>(slot: TgpuSlot<TValue>, value: Eventual<TValue>): TgpuDerived<T>;\n\n /**\n * @internal\n */\n '~compute'(): T;\n}\n\nexport interface TgpuAccessor<T extends AnyWgslData = AnyWgslData>\n extends TgpuNamable {\n readonly resourceType: 'accessor';\n [$repr]: Infer<T>;\n\n readonly schema: T;\n readonly defaultValue:\n | TgpuFn<[], T>\n | TgpuBufferUsage<T>\n | Infer<T>\n | undefined;\n readonly slot: TgpuSlot<TgpuFn<[], T> | TgpuBufferUsage<T> | Infer<T>>;\n\n readonly value: Infer<T>;\n}\n\n/**\n * Represents a value that is available at resolution time.\n */\nexport type Eventual<T> = T | TgpuSlot<T> | TgpuDerived<T>;\n\nexport type SlotValuePair<T = unknown> = [TgpuSlot<T>, T];\n\nexport type Providing = {\n inner: unknown;\n pairs: SlotValuePair[];\n};\n\nexport function isSlot<T>(value: unknown | TgpuSlot<T>): value is TgpuSlot<T> {\n return (value as TgpuSlot<T>)?.resourceType === 'slot';\n}\n\nexport function isDerived<T extends TgpuDerived<unknown>>(\n value: T | unknown,\n): value is T {\n return (value as T)?.resourceType === 'derived';\n}\n\nexport function isProviding(\n value: unknown,\n): value is { '~providing': Providing } {\n return (value as { '~providing': Providing })?.['~providing'] !== undefined;\n}\n\nexport function isAccessor<T extends AnyWgslData>(\n value: unknown | TgpuAccessor<T>,\n): value is TgpuAccessor<T> {\n return (value as TgpuAccessor<T>)?.resourceType === 'accessor';\n}\n","export type Default<T, TDefault> = unknown extends T ? TDefault\n : T extends undefined ? TDefault\n : T;\n\nexport type UnionToIntersection<U> =\n // biome-ignore lint/suspicious/noExplicitAny: <had to be done>\n (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I\n : never;\n\nexport type Prettify<T> =\n & {\n [K in keyof T]: T[K];\n }\n & {};\n\n/**\n * Removes properties from record type that extend `Prop`\n */\nexport type OmitProps<T extends Record<string, unknown>, Prop> = Pick<\n T,\n {\n [Key in keyof T]: T[Key] extends Prop ? never : Key;\n }[keyof T]\n>;\n\n/**\n * The opposite of Readonly<T>\n */\nexport type Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\n/**\n * Any typed array\n */\nexport type TypedArray =\n | Uint8Array\n | Uint16Array\n | Uint32Array\n | Int32Array\n | Float32Array\n | Float64Array;\n\nexport function assertExhaustive(x: never, location: string): never {\n throw new Error(`Failed to handle ${x} at ${location}`);\n}\n","import { isDerived, isSlot } from '../core/slot/slotTypes.ts';\nimport { arrayOf } from '../data/array.ts';\nimport type { AnyData } from '../data/dataTypes.ts';\nimport { mat2x2f, mat3x3f, mat4x4f } from '../data/matrix.ts';\nimport {\n abstractFloat,\n abstractInt,\n bool,\n f16,\n f32,\n i32,\n u32,\n} from '../data/numeric.ts';\nimport {\n vec2b,\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3b,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4b,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n vecTypeToPrimitive,\n} from '../data/vector.ts';\nimport {\n type AnyWgslData,\n type AnyWgslStruct,\n type F16,\n type F32,\n type I32,\n isDecorated,\n isMat,\n isVec,\n isWgslArray,\n isWgslData,\n type U32,\n type WgslStruct,\n} from '../data/wgslTypes.ts';\nimport { invariant } from '../errors.ts';\nimport { getResolutionCtx } from '../gpuMode.ts';\nimport { $internal } from '../shared/symbols.ts';\nimport { assertExhaustive } from '../shared/utilityTypes.ts';\nimport {\n hasInternalDataType,\n isSelfResolvable,\n isWgsl,\n type ResolutionCtx,\n type Snippet,\n UnknownData,\n type Wgsl,\n} from '../types.ts';\n\nconst swizzleableTypes = [\n 'vec2f',\n 'vec2h',\n 'vec2i',\n 'vec2u',\n 'vec2<bool>',\n 'vec3f',\n 'vec3h',\n 'vec3i',\n 'vec3u',\n 'vec3<bool>',\n 'vec4f',\n 'vec4h',\n 'vec4i',\n 'vec4u',\n 'vec4<bool>',\n 'struct',\n] as const;\n\ntype SwizzleableType = 'f' | 'h' | 'i' | 'u' | 'b';\ntype SwizzleLength = 1 | 2 | 3 | 4;\n\nconst swizzleLenToType: Record<\n SwizzleableType,\n Record<SwizzleLength, AnyData>\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 b: {\n 1: bool,\n 2: vec2b,\n 3: vec3b,\n 4: vec4b,\n },\n} as const;\n\nconst kindToSchema = {\n vec2f: vec2f,\n vec2h: vec2h,\n vec2i: vec2i,\n vec2u: vec2u,\n 'vec2<bool>': vec2b,\n vec3f: vec3f,\n vec3h: vec3h,\n vec3i: vec3i,\n vec3u: vec3u,\n 'vec3<bool>': vec3b,\n vec4f: vec4f,\n vec4h: vec4h,\n vec4i: vec4i,\n vec4u: vec4u,\n 'vec4<bool>': vec4b,\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 'vec2<bool>': bool,\n vec3f: f32,\n vec3h: f16,\n vec3i: i32,\n vec3u: u32,\n 'vec3<bool>': bool,\n vec4f: f32,\n vec4h: f16,\n vec4i: i32,\n vec4u: u32,\n 'vec4<bool>': bool,\n mat2x2f: vec2f,\n mat3x3f: vec3f,\n mat4x4f: vec4f,\n} as const;\n\nexport function getTypeForPropAccess(\n targetType: Wgsl,\n propName: string,\n): AnyData | 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);\n }\n\n let target = targetType as AnyData;\n\n if (hasInternalDataType(target)) {\n target = target[$internal].dataType as AnyData;\n }\n while (isDecorated(target)) {\n target = target.inner as AnyData;\n }\n\n const targetTypeStr = 'kind' in target\n ? (target.kind as string)\n : target.type;\n\n if (targetTypeStr === 'struct') {\n return (\n ((target as WgslStruct).propTypes[propName] as AnyData) ?? UnknownData\n );\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.includes('bool')\n ? 'b'\n : (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): AnyData | UnknownData {\n if (isWgslData(resource)) {\n // array\n if (isWgslArray(resource)) {\n return resource.elementType as AnyData;\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): AnyData | UnknownData {\n if (hasInternalDataType(resource)) {\n return resource[$internal].dataType as AnyData;\n }\n\n if (typeof resource === 'string') {\n return UnknownData;\n }\n if (typeof resource === 'number') {\n return numericLiteralToSnippet(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 // TODO: do not treat self-resolvable as wgsl data (when we have proper texture schemas)\n return isWgslData(resource) || isSelfResolvable(resource)\n ? (resource as unknown as AnyData)\n : UnknownData;\n}\n\nexport function numericLiteralToSnippet(value: string): Snippet | 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\ntype ConversionAction = 'ref' | 'deref' | 'cast' | 'none';\n\ntype ConversionRankInfo =\n | { rank: number; action: 'cast'; targetType: AnyData }\n | { rank: number; action: Exclude<ConversionAction, 'cast'> };\n\nconst INFINITE_RANK: ConversionRankInfo = {\n rank: Number.POSITIVE_INFINITY,\n action: 'none',\n};\n\nfunction unwrapDecorated(data: AnyData): AnyData {\n if (data.type === 'decorated') {\n return data.inner as AnyData;\n }\n return data;\n}\n\nfunction getVectorComponent(type: AnyData): AnyData | undefined {\n return isVec(type) ? vecTypeToPrimitive[type.type] : undefined;\n}\n\nfunction getAutoConversionRank(\n src: AnyData,\n dest: AnyData,\n): ConversionRankInfo {\n const trueSrc = unwrapDecorated(src);\n const trueDst = unwrapDecorated(dest);\n\n if (trueSrc.type === trueDst.type) {\n return { rank: 0, action: 'none' };\n }\n\n if (trueSrc.type === 'abstractFloat') {\n if (trueDst.type === 'f32') return { rank: 1, action: 'none' };\n if (trueDst.type === 'f16') return { rank: 2, action: 'none' };\n }\n\n if (trueSrc.type === 'abstractInt') {\n if (trueDst.type === 'i32') return { rank: 3, action: 'none' };\n if (trueDst.type === 'u32') return { rank: 4, action: 'none' };\n if (trueDst.type === 'abstractFloat') return { rank: 5, action: 'none' };\n if (trueDst.type === 'f32') return { rank: 6, action: 'none' };\n if (trueDst.type === 'f16') return { rank: 7, action: 'none' };\n }\n\n if (isVec(trueSrc) && isVec(trueDst)) {\n const compSrc = getVectorComponent(trueSrc);\n const compDest = getVectorComponent(trueDst);\n if (compSrc && compDest) {\n return getAutoConversionRank(compSrc, compDest);\n }\n }\n\n if (isMat(trueSrc) && isMat(trueDst)) {\n // Matrix conversion rank depends only on component type (always f32 for now)\n return { rank: 0, action: 'none' };\n }\n\n return INFINITE_RANK;\n}\n\nfunction getImplicitConversionRank(\n src: AnyData,\n dest: AnyData,\n): ConversionRankInfo {\n const trueSrc = unwrapDecorated(src);\n const trueDst = unwrapDecorated(dest);\n\n if (\n trueSrc.type === 'ptr' &&\n getAutoConversionRank(trueSrc.inner as AnyData, trueDst).rank <\n Number.POSITIVE_INFINITY\n ) {\n return { rank: 0, action: 'deref' };\n }\n\n if (\n trueDst.type === 'ptr' &&\n getAutoConversionRank(trueSrc, trueDst.inner as AnyData).rank <\n Number.POSITIVE_INFINITY\n ) {\n return { rank: 1, action: 'ref' };\n }\n\n const primitivePreference = {\n f32: 0,\n f16: 1,\n i32: 2,\n u32: 3,\n bool: 4,\n } as const;\n type PrimitiveType = keyof typeof primitivePreference;\n\n if (\n trueSrc.type in primitivePreference &&\n trueDst.type in primitivePreference\n ) {\n const srcType = trueSrc.type as PrimitiveType;\n const destType = trueDst.type as PrimitiveType;\n\n if (srcType !== destType) {\n const srcPref = primitivePreference[srcType];\n const destPref = primitivePreference[destType];\n\n const rank = destPref < srcPref ? 10 : 20;\n\n return { rank: rank, action: 'cast', targetType: trueDst };\n }\n }\n\n return INFINITE_RANK;\n}\n\nfunction getConversionRank(\n src: AnyData,\n dest: AnyData,\n allowImplicit: boolean,\n): ConversionRankInfo {\n const autoRank = getAutoConversionRank(src, dest);\n if (autoRank.rank < Number.POSITIVE_INFINITY) {\n return autoRank;\n }\n if (allowImplicit) {\n return getImplicitConversionRank(src, dest);\n }\n return INFINITE_RANK;\n}\n\nexport type ConversionResultAction = {\n sourceIndex: number;\n action: ConversionAction;\n targetType?: U32 | F32 | I32 | F16;\n};\n\nexport type ConversionResult = {\n targetType: AnyData;\n actions: ConversionResultAction[];\n hasImplicitConversions?: boolean;\n};\n\nfunction findBestType(\n types: AnyData[],\n uniqueTypes: AnyData[],\n allowImplicit: boolean,\n): ConversionResult | undefined {\n let bestType: AnyData | undefined;\n let minSum = Number.POSITIVE_INFINITY;\n const conversionDetails = new Map<AnyData, ConversionRankInfo[]>();\n\n for (const targetType of uniqueTypes) {\n let currentSum = 0;\n const currentDetails: ConversionRankInfo[] = [];\n let possible = true;\n\n for (const sourceType of types) {\n const conversion = getConversionRank(\n sourceType,\n targetType,\n allowImplicit,\n );\n if (conversion.rank === Number.POSITIVE_INFINITY) {\n possible = false;\n break;\n }\n currentSum += conversion.rank;\n currentDetails.push(conversion);\n }\n\n if (possible && currentSum < minSum) {\n minSum = currentSum;\n bestType = targetType;\n conversionDetails.set(bestType, currentDetails);\n }\n }\n\n if (!bestType) {\n return undefined;\n }\n\n const bestDetails = conversionDetails.get(bestType) as ConversionRankInfo[];\n const actions: ConversionResultAction[] = bestDetails.map(\n (detail, index) => ({\n sourceIndex: index,\n action: detail.action,\n ...(detail.action === 'cast' && {\n targetType: detail.targetType as U32 | F32 | I32 | F16,\n }),\n }),\n );\n\n const hasCasts = actions.some((action) => action.action === 'cast');\n\n return { targetType: bestType, actions, hasImplicitConversions: hasCasts };\n}\n\nexport function concretize(type: AnyWgslData): AnyWgslData {\n if (type.type === 'abstractFloat') {\n return f32;\n }\n\n if (type.type === 'abstractInt') {\n return i32;\n }\n\n return type;\n}\n\nexport function getBestConversion(\n types: AnyData[],\n targetTypes?: AnyData[],\n): ConversionResult | undefined {\n if (types.length === 0) return undefined;\n\n const uniqueTypes = [...new Set(types.map(unwrapDecorated))];\n const uniqueTargetTypes = targetTypes\n ? [...new Set(targetTypes.map(unwrapDecorated))]\n : uniqueTypes;\n\n const explicitResult = findBestType(types, uniqueTargetTypes, false);\n if (explicitResult) {\n return explicitResult;\n }\n\n const implicitResult = findBestType(types, uniqueTargetTypes, true);\n if (implicitResult) {\n implicitResult.hasImplicitConversions = implicitResult.actions.some(\n (action) => action.action === 'cast',\n );\n return implicitResult;\n }\n\n return undefined;\n}\n\nexport function convertType(\n sourceType: AnyData,\n targetType: AnyData,\n allowImplicit = true,\n): ConversionResult | undefined {\n const conversion = getConversionRank(sourceType, targetType, allowImplicit);\n\n if (conversion.rank < Number.POSITIVE_INFINITY) {\n const actionDetail: ConversionResultAction = {\n sourceIndex: 0,\n action: conversion.action,\n };\n if (conversion.action === 'cast') {\n actionDetail.targetType = conversion.targetType as U32 | F32 | I32 | F16;\n }\n return {\n targetType: unwrapDecorated(targetType),\n actions: [actionDetail],\n hasImplicitConversions: conversion.action === 'cast',\n };\n }\n\n return undefined;\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): Snippet | null;\n defineVariable(id: string, dataType: AnyWgslData | UnknownData): Snippet;\n};\n\nexport function resolveRes(ctx: GenerationCtx, res: Snippet): string {\n if (isWgsl(res.value)) {\n return ctx.resolve(res.value);\n }\n\n return String(res.value);\n}\n\nfunction applyActionToSnippet(\n ctx: GenerationCtx,\n value: Snippet,\n action: ConversionResultAction,\n targetType: AnyData,\n): Snippet {\n if (action.action === 'none') {\n return {\n value: value.value,\n dataType: targetType,\n };\n }\n\n const resolvedValue = resolveRes(ctx, value);\n\n switch (action.action) {\n case 'ref':\n return { value: `&${resolvedValue}`, dataType: targetType };\n case 'deref':\n return { value: `*${resolvedValue}`, dataType: targetType };\n case 'cast': {\n return {\n value: `${ctx.resolve(targetType)}(${resolvedValue})`,\n dataType: targetType,\n };\n }\n default: {\n assertExhaustive(action.action, 'applyActionToSnippet');\n }\n }\n}\n\nexport function convertToCommonType(\n ctx: GenerationCtx,\n values: Snippet[],\n restrictTo?: AnyData[],\n): Snippet[] | undefined {\n const types = values.map((value) => value.dataType);\n\n if (types.some((type) => type === UnknownData)) {\n return undefined;\n }\n\n const conversion = getBestConversion(types as AnyData[], restrictTo);\n if (!conversion) {\n return undefined;\n }\n\n if (conversion.hasImplicitConversions) {\n console.warn(\n `Implicit conversions from [\\n${\n values\n .map((v) => ` ${v.value}: ${v.dataType.type}`)\n .join(\n ',\\n',\n )\n }\\n] to ${conversion.targetType.type} are supported, but not recommended.\nConsider using explicit conversions instead.`,\n );\n }\n\n return values.map((value, index) => {\n const action = conversion.actions[index];\n invariant(action, 'Action should not be undefined');\n return applyActionToSnippet(ctx, value, action, conversion.targetType);\n });\n}\n\nexport function convertStructValues(\n ctx: GenerationCtx,\n structType: AnyWgslStruct,\n values: Record<string, Snippet>,\n): Snippet[] {\n const propKeys = Object.keys(structType.propTypes);\n\n return propKeys.map((key) => {\n const val = values[key];\n if (!val) {\n throw new Error(`Missing property ${key}`);\n }\n\n const targetType = structType.propTypes[key];\n const converted = convertToCommonType(ctx, [val], [targetType as AnyData]);\n return converted?.[0] ?? val;\n });\n}\n\nexport function coerceToSnippet(value: unknown): Snippet {\n if (isWgsl(value)) {\n return {\n value: value,\n dataType: getTypeFromWgsl(value),\n };\n }\n\n if (Array.isArray(value)) {\n const coerced = value.map(coerceToSnippet).filter(Boolean);\n const context = getResolutionCtx() as GenerationCtx | undefined;\n if (!context) {\n throw new Error('Tried to coerce array without a context');\n }\n\n const converted = convertToCommonType(context, coerced as Snippet[]);\n const commonType = getBestConversion(\n coerced.map((v) => v.dataType as AnyData),\n )?.targetType as AnyWgslData | undefined;\n\n if (!converted || !commonType) {\n return {\n value: value,\n dataType: UnknownData,\n };\n }\n\n return {\n value: converted.map((v) => v.value).join(', '),\n dataType: arrayOf(concretize(commonType), value.length),\n };\n }\n\n if (typeof value !== 'object') {\n const maybeNumeric = numericLiteralToSnippet(String(value));\n\n if (maybeNumeric) {\n return maybeNumeric;\n }\n }\n\n return {\n value: value,\n dataType: UnknownData,\n };\n}\n","import type { BaseData } from '../data/wgslTypes.ts';\nimport { getName } from '../name.ts';\nimport { $internal } from '../shared/symbols.ts';\nimport { getTypeForPropAccess } from '../tgsl/generationHelpers.ts';\nimport {\n isBufferUsage,\n type ResolutionCtx,\n type SelfResolvable,\n type Wgsl,\n} from '../types.ts';\nimport { isAccessor, isDerived, isSlot } from './slot/slotTypes.ts';\n\nexport const valueProxyHandler: ProxyHandler<\n & SelfResolvable\n & { readonly [$internal]: { readonly dataType: BaseData } }\n> = {\n get(target, prop) {\n if (prop in target) {\n return Reflect.get(target, prop);\n }\n\n if (prop === '~providing') {\n return undefined;\n }\n\n if (\n prop === 'toString' ||\n prop === Symbol.toStringTag ||\n prop === Symbol.toPrimitive\n ) {\n return () => target.toString();\n }\n\n return new Proxy(\n {\n '~resolve': (ctx: ResolutionCtx) =>\n `${ctx.resolve(target)}.${String(prop)}`,\n\n toString: () =>\n `.value(...).${String(prop)}:${getName(target) ?? '<unnamed>'}`,\n\n [$internal]: {\n dataType: getTypeForPropAccess(\n target[$internal].dataType as Wgsl,\n String(prop),\n ) as BaseData,\n },\n },\n valueProxyHandler,\n );\n },\n};\n\nexport function unwrapProxy<T>(value: unknown): T {\n let unwrapped = value;\n\n while (\n isSlot(unwrapped) ||\n isDerived(unwrapped) ||\n isAccessor(unwrapped) ||\n isBufferUsage(unwrapped)\n ) {\n unwrapped = unwrapped.value;\n }\n\n return unwrapped as T;\n}\n","import { getAttributesString } from '../../data/attributes.ts';\nimport {\n type AnyData,\n type Disarray,\n isLooseData,\n type Unstruct,\n} from '../../data/dataTypes.ts';\nimport { formatToWGSLType } from '../../data/vertexFormatData.ts';\nimport type {\n AnyWgslData,\n BaseData,\n Bool,\n F16,\n F32,\n I32,\n Mat2x2f,\n Mat3x3f,\n Mat4x4f,\n U32,\n Vec2b,\n Vec2f,\n Vec2h,\n Vec2i,\n Vec2u,\n Vec3b,\n Vec3f,\n Vec3h,\n Vec3i,\n Vec3u,\n Vec4b,\n Vec4f,\n Vec4h,\n Vec4i,\n Vec4u,\n WgslArray,\n WgslStruct,\n} from '../../data/wgslTypes.ts';\nimport { getName } from '../../name.ts';\nimport { assertExhaustive } from '../../shared/utilityTypes.ts';\nimport type { ResolutionCtx } from '../../types.ts';\nimport { isAttribute } from '../vertexLayout/connectAttributesToShader.ts';\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 'vec2<bool>',\n 'vec3<bool>',\n 'vec4<bool>',\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 | Vec2b\n | Vec3b\n | Vec4b\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}: ${\n ctx.resolve(property as AnyWgslData)\n },\\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(getName(struct));\n\n ctx.addDeclaration(`\nstruct ${id} {\n${\n Object.entries(struct.propTypes)\n .map((prop) => resolveStructProperty(ctx, prop))\n .join('')\n }\\\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(getName(unstruct));\n\n ctx.addDeclaration(`\nstruct ${id} {\n${\n Object.entries(unstruct.propTypes)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [\n prop[0],\n formatToWGSLType[prop[1].format],\n ])\n : resolveStructProperty(ctx, prop)\n )\n .join('')\n }\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)}, ${\n data.access === 'read-write' ? 'read_write' : data.access\n }>`;\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 if (data.type === 'void') {\n throw new Error('Void has no representation in WGSL');\n }\n\n assertExhaustive(data, 'resolveData');\n}\n","import { roundUp } from '../mathUtils.ts';\nimport type { Infer } from '../shared/repr.ts';\nimport { alignmentOf } from './alignmentOf.ts';\nimport { isDisarray, isUnstruct } from './dataTypes.ts';\nimport { offsetsForProps } from './offsets.ts';\nimport { sizeOf } from './sizeOf.ts';\nimport * as wgsl from './wgslTypes.ts';\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.isAtomic(node) || wgsl.isDecorated(node)) {\n return buildWriter(node.inner, offsetExpr, valueExpr);\n }\n\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}.${\n components[i]\n }, 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 +=\n `output.${writeFunc}((${offsetExpr} + ${byteOffset}), ${valueExpr}.columns[${colIndex}].${\n ['x', 'y', 'z', 'w'][rowIndex]\n }, littleEndian);\\n`;\n }\n\n return code;\n }\n\n const primitive = typeToPrimitive[node.type as keyof typeof typeToPrimitive];\n return `output.${\n primitiveToWriteFunction[primitive]\n }(${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.ts';\nimport alignIO from './alignIO.ts';\nimport { alignmentOf, customAlignmentOf } from './alignmentOf.ts';\nimport type {\n AnyConcreteData,\n AnyData,\n Disarray,\n LooseDecorated,\n Unstruct,\n} from './dataTypes.ts';\nimport { mat2x2f, mat3x3f, mat4x4f } from './matrix.ts';\nimport { sizeOf } from './sizeOf.ts';\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.ts';\nimport type * as wgsl from './wgslTypes.ts';\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() {\n throw new Error('Booleans are not host-shareable');\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 'vec2<bool>'() {\n throw new Error('Booleans are not host-shareable');\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 'vec3<bool>'() {\n throw new Error('Booleans are not host-shareable');\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 'vec4<bool>'() {\n throw new Error('Booleans are not host-shareable');\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: wgsl.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(): boolean {\n throw new Error('Booleans are not host-shareable');\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 'vec2<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n 'vec3<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n 'vec4<bool>'() {\n throw new Error('Booleans are not host-shareable');\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: wgsl.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 * as tinyest from 'tinyest';\nimport { arrayOf } from '../data/array.ts';\nimport { type AnyData, isData, isLooseData } from '../data/dataTypes.ts';\nimport * as d from '../data/index.ts';\nimport { abstractInt } from '../data/numeric.ts';\nimport * as wgsl from '../data/wgslTypes.ts';\nimport { $internal } from '../shared/symbols.ts';\nimport {\n type FnArgsConversionHint,\n isMarkedInternal,\n isWgsl,\n type Snippet,\n UnknownData,\n} from '../types.ts';\nimport {\n concretize,\n convertStructValues,\n convertToCommonType,\n type GenerationCtx,\n getTypeForIndexAccess,\n getTypeForPropAccess,\n getTypeFromWgsl,\n numericLiteralToSnippet,\n resolveRes,\n} from './generationHelpers.ts';\n\nconst { NodeTypeCatalog: NODE } = tinyest;\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 | tinyest.BinaryOperator\n | tinyest.AssignmentOperator\n | tinyest.LogicalOperator\n | tinyest.UnaryOperator;\n\nfunction operatorToType<\n TL extends AnyData | UnknownData,\n TR extends AnyData | 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\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): Snippet {\n return { value: value ? 'true' : 'false', dataType: d.bool };\n}\n\nexport function generateBlock(\n ctx: GenerationCtx,\n [_, statements]: tinyest.Block,\n): string {\n ctx.pushBlockScope();\n try {\n return `${ctx.indent()}{\n${statements.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): Snippet {\n return ctx.defineVariable(id, dataType);\n}\n\nexport function generateIdentifier(ctx: GenerationCtx, id: string): Snippet {\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: tinyest.Expression,\n): Snippet {\n if (typeof expression === 'string') {\n return generateIdentifier(ctx, expression);\n }\n\n if (typeof expression === 'boolean') {\n return generateBoolean(ctx, expression);\n }\n\n if (\n expression[0] === NODE.logicalExpr ||\n expression[0] === NODE.binaryExpr ||\n expression[0] === NODE.assignmentExpr\n ) {\n // Logical/Binary/Assignment Expression\n const [_, lhs, op, rhs] = expression;\n const lhsExpr = generateExpression(ctx, lhs);\n const rhsExpr = generateExpression(ctx, rhs);\n\n const converted = convertToCommonType(ctx, [lhsExpr, rhsExpr]) as\n | [Snippet, Snippet]\n | undefined;\n const [convLhs, convRhs] = converted || [lhsExpr, rhsExpr];\n\n const lhsStr = resolveRes(ctx, convLhs);\n const rhsStr = resolveRes(ctx, convRhs);\n const type = operatorToType(convLhs.dataType, op, convRhs.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 (expression[0] === NODE.postUpdate) {\n // Post-Update Expression\n const [_, op, arg] = expression;\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 (expression[0] === NODE.unaryExpr) {\n // Unary Expression\n const [_, op, arg] = expression;\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 (expression[0] === NODE.memberAccess) {\n // Member Access\n const [_, targetId, property] = expression;\n const target = generateExpression(ctx, targetId);\n\n if (wgsl.isPtr(target.dataType)) {\n return {\n value: `(*${target.value}).${property}`,\n dataType: isData(target.dataType.inner)\n ? getTypeForPropAccess(target.dataType.inner, property)\n : UnknownData,\n };\n }\n\n if (typeof target.value === 'string') {\n return {\n value: `${target.value}.${property}`,\n dataType: isData(target.dataType)\n ? getTypeForPropAccess(target.dataType, property)\n : UnknownData,\n };\n }\n\n if (wgsl.isWgslArray(target.dataType)) {\n if (property === 'length') {\n if (target.dataType.elementCount === 0) {\n // Dynamically-sized array\n return {\n value: `arrayLength(&${ctx.resolve(target.value)})`,\n dataType: d.u32,\n };\n }\n\n return {\n value: String(target.dataType.elementCount),\n dataType: abstractInt,\n };\n }\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: <sorry TypeScript>\n const propValue = (target.value as any)[property];\n\n if (target.dataType.type !== 'unknown') {\n if (wgsl.isMat(target.dataType) && property === 'columns') {\n return {\n value: target.value,\n dataType: target.dataType,\n };\n }\n\n return {\n value: propValue,\n dataType: getTypeForPropAccess(target.dataType, property),\n };\n }\n\n if (isWgsl(target.value)) {\n return {\n value: propValue,\n dataType: getTypeForPropAccess(target.value, 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 (expression[0] === NODE.indexAccess) {\n // Index Access\n const [_, target, property] = expression;\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 if (wgsl.isPtr(targetExpr.dataType)) {\n return {\n value: `(*${targetStr})[${propertyStr}]`,\n dataType: isData(targetExpr.dataType.inner)\n ? getTypeForIndexAccess(targetExpr.dataType.inner)\n : UnknownData,\n };\n }\n\n return {\n value: `${targetStr}[${propertyStr}]`,\n dataType: isData(targetExpr.dataType)\n ? getTypeForIndexAccess(targetExpr.dataType)\n : UnknownData,\n };\n }\n\n if (expression[0] === NODE.numericLiteral) {\n // Numeric Literal\n const type = numericLiteralToSnippet(expression[1]);\n if (!type) {\n throw new Error(`Invalid numeric literal ${expression[1]}`);\n }\n return type;\n }\n\n if (expression[0] === NODE.call) {\n // Function Call\n const [_, callee, args] = expression;\n const id = generateExpression(ctx, callee);\n const idValue = id.value;\n\n ctx.callStack.push(idValue);\n\n const argSnippets = args.map((arg) => generateExpression(ctx, arg));\n const resolvedSnippets = argSnippets.map((res) => ({\n value: resolveRes(ctx, res),\n dataType: res.dataType,\n }));\n const argValues = resolvedSnippets.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 if (!isMarkedInternal(idValue)) {\n throw new Error(\n `Function ${\n String(idValue)\n } has not been created using TypeGPU APIs. Did you mean to wrap the function with tgpu.fn(args, return)(...) ?`,\n );\n }\n\n const argTypes = idValue[$internal]?.argTypes as\n | FnArgsConversionHint\n | undefined;\n let convertedResources: Snippet[];\n\n if (!argTypes || argTypes === 'keep') {\n convertedResources = resolvedSnippets;\n } else if (argTypes === 'coerce') {\n convertedResources = convertToCommonType(ctx, resolvedSnippets) ??\n resolvedSnippets;\n } else {\n const pairs: [wgsl.AnyWgslData, Snippet][] = Array.isArray(argTypes)\n ? (argTypes\n .map((type, i) => [type, resolvedSnippets[i]])\n .filter(([, sn]) => !!sn) as [wgsl.AnyWgslData, Snippet][])\n : typeof argTypes === 'function'\n ? ((argTypes(...resolvedSnippets) as wgsl.AnyWgslData[])\n .map((type, i) => [type, resolvedSnippets[i]])\n .filter(([, sn]) => !!sn) as [wgsl.AnyWgslData, Snippet][])\n : Object.entries(argTypes).map(([key, type]) => {\n const sn = (argSnippets[0]?.value as Record<string, Snippet>)[\n key\n ];\n if (!sn) {\n throw new Error(`Missing argument ${key} in function call`);\n }\n return [type, sn] as [wgsl.AnyWgslData, Snippet];\n });\n\n convertedResources = pairs.map(([type, sn]) => {\n const conv = convertToCommonType(ctx, [sn], [type])?.[0];\n if (!conv) {\n throw new Error(\n `Cannot convert ${ctx.resolve(sn.dataType)} to ${\n ctx.resolve(type)\n }`,\n );\n }\n return conv;\n });\n }\n\n // Assuming that `id` is callable\n const fnRes = (idValue as unknown as (...args: unknown[]) => unknown)(\n ...convertedResources,\n ) as Snippet;\n\n return {\n value: resolveRes(ctx, fnRes),\n dataType: fnRes.dataType,\n };\n }\n\n if (expression[0] === NODE.objectExpr) {\n // Object Literal\n const obj = expression[1];\n const callee = ctx.callStack[ctx.callStack.length - 1];\n\n if (wgsl.isWgslStruct(callee)) {\n const propKeys = Object.keys(callee.propTypes);\n const entries = Object.fromEntries(\n 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 [key, generateExpression(ctx, val)];\n }),\n );\n\n const convertedValues = convertStructValues(ctx, callee, entries);\n\n return {\n value: convertedValues.map((v) => resolveRes(ctx, v)).join(', '),\n dataType: callee,\n };\n }\n\n if (isMarkedInternal(callee)) {\n const argTypes = callee[$internal]?.argTypes;\n\n if (typeof argTypes === 'object' && argTypes !== null) {\n const propKeys = Object.keys(argTypes);\n const snippets: Record<string, Snippet> = {};\n\n for (const key of propKeys) {\n const val = obj[key];\n if (val === undefined) {\n throw new Error(\n `Missing property ${key} in object literal for function ${callee}`,\n );\n }\n const expr = generateExpression(ctx, val);\n const targetType = argTypes[key as keyof typeof argTypes];\n const converted = convertToCommonType(ctx, [expr], [targetType]);\n snippets[key] = converted?.[0] ?? expr;\n }\n\n return {\n value: snippets,\n dataType: UnknownData,\n };\n }\n }\n\n throw new Error(\n 'Object expressions are only allowed as return values of functions or as arguments to structs.',\n );\n }\n\n if (expression[0] === NODE.arrayExpr) {\n const [_, valuesRaw] = expression;\n // Array Expression\n const values = valuesRaw.map((value) =>\n generateExpression(ctx, value as tinyest.Expression)\n );\n if (values.length === 0) {\n throw new Error('Cannot create empty array literal.');\n }\n\n const convertedValues = convertToCommonType(ctx, values);\n if (!convertedValues) {\n throw new Error(\n 'The given values cannot be automatically converted to a common type. Consider explicitly casting them.',\n );\n }\n\n const targetType = convertedValues[0]?.dataType as AnyData;\n const type = targetType.type === 'abstractFloat'\n ? d.f32\n : targetType.type === 'abstractInt'\n ? d.i32\n : targetType;\n\n const typeId = ctx.resolve(type);\n\n const arrayType = `array<${typeId}, ${values.length}>`;\n const arrayValues = convertedValues.map((value) => resolveRes(ctx, value));\n\n return {\n value: `${arrayType}( ${arrayValues.join(', ')} )`,\n dataType: arrayOf(\n type as wgsl.AnyWgslData,\n values.length,\n ) as wgsl.AnyWgslData,\n };\n }\n\n if (expression[0] === NODE.stringLiteral) {\n throw new Error('Cannot use string literals in TGSL.');\n }\n\n if (expression[0] === NODE.preUpdate) {\n throw new Error('Cannot use pre-updates in TGSL.');\n }\n\n assertExhaustive(expression);\n}\n\nfunction blockifySingleStatement(statement: tinyest.Statement): tinyest.Block {\n return typeof statement !== 'object' ||\n statement[0] !== NODE.block\n ? [NODE.block, [statement]]\n : statement;\n}\n\nexport function generateStatement(\n ctx: GenerationCtx,\n statement: tinyest.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 (statement[0] === NODE.return) {\n const returnNode = statement[1];\n const returnValue = returnNode !== undefined\n ? resolveRes(ctx, generateExpression(ctx, returnNode))\n : undefined;\n\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 typeof returnNode === 'object' &&\n returnNode[0] === NODE.objectExpr\n ) {\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}return ${resolvedStruct}(${returnValue});`;\n }\n\n return returnValue\n ? `${ctx.pre}return ${returnValue};`\n : `${ctx.pre}return;`;\n }\n\n if (statement[0] === NODE.if) {\n const [_, cond, cons, alt] = statement;\n const condExpr = generateExpression(ctx, cond);\n let condSnippet = condExpr;\n const converted = convertToCommonType(ctx, [condExpr], [d.bool]);\n if (converted?.[0]) {\n [condSnippet] = converted;\n }\n const condition = resolveRes(ctx, condSnippet);\n\n ctx.indent(); // {\n const consequent = generateStatement(ctx, blockifySingleStatement(cons));\n ctx.dedent(); // }\n\n ctx.indent(); // {\n const alternate = alt\n ? generateStatement(ctx, blockifySingleStatement(alt))\n : 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 (statement[0] === NODE.let || statement[0] === NODE.const) {\n const [_, rawId, rawValue] = statement;\n const eq = rawValue !== undefined\n ? generateExpression(ctx, rawValue)\n : undefined;\n\n if (!eq) {\n throw new Error(\n `Cannot create variable '${rawId}' without an initial value.`,\n );\n }\n\n if (isLooseData(eq.dataType)) {\n throw new Error(\n `Cannot create variable '${rawId}' with loose data type.`,\n );\n }\n\n registerBlockVariable(\n ctx,\n rawId,\n concretize(eq.dataType as wgsl.AnyWgslData),\n );\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 rawValue[0] === NODE.objectExpr &&\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1])\n ) {\n const structType = ctx.callStack[\n ctx.callStack.length - 1\n ] as wgsl.WgslStruct;\n const obj = rawValue[1];\n\n const entries: Record<string, Snippet> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (!value) {\n throw new Error(`Missing property ${key} in object literal`);\n }\n entries[key] = generateExpression(ctx, value);\n }\n\n const convertedValues = convertStructValues(ctx, structType, entries);\n const resolvedStruct = ctx.resolve(structType);\n return `${ctx.pre}var ${id} = ${resolvedStruct}(${\n convertedValues.map((v) => resolveRes(ctx, v)).join(', ')\n });`;\n }\n\n return `${ctx.pre}var ${id} = ${resolveRes(ctx, eq)};`;\n }\n\n if (statement[0] === NODE.block) {\n return generateBlock(ctx, statement);\n }\n\n if (statement[0] === NODE.for) {\n const [_, init, condition, update, body] = statement;\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 let condSnippet = conditionExpr;\n if (conditionExpr) {\n const converted = convertToCommonType(ctx, [conditionExpr], [d.bool]);\n if (converted?.[0]) {\n [condSnippet] = converted;\n }\n }\n const conditionStr = condSnippet ? resolveRes(ctx, condSnippet) : '';\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, blockifySingleStatement(body));\n ctx.dedent();\n\n return `\\\n${ctx.pre}for (${initStr}; ${conditionStr}; ${updateStr})\n${bodyStr}`;\n }\n\n if (statement[0] === NODE.while) {\n const [_, condition, body] = statement;\n const condExpr = generateExpression(ctx, condition);\n let condSnippet = condExpr;\n if (condExpr) {\n const converted = convertToCommonType(ctx, [condExpr], [d.bool]);\n if (converted?.[0]) {\n [condSnippet] = converted;\n }\n }\n const conditionStr = resolveRes(ctx, condSnippet);\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, blockifySingleStatement(body));\n ctx.dedent();\n\n return `\\\n${ctx.pre}while (${conditionStr})\n${bodyStr}`;\n }\n\n if (statement[0] === NODE.continue) {\n return `${ctx.pre}continue;`;\n }\n\n if (statement[0] === NODE.break) {\n return `${ctx.pre}break;`;\n }\n\n return `${ctx.pre}${resolveRes(ctx, generateExpression(ctx, statement))};`;\n}\n\nexport function generateFunction(\n ctx: GenerationCtx,\n body: tinyest.Block,\n): string {\n return generateBlock(ctx, body);\n}\n","import type { ArgNames, Block } from 'tinyest';\nimport { resolveData } from './core/resolve/resolveData.ts';\nimport {\n type Eventual,\n isDerived,\n isProviding,\n isSlot,\n type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n} from './core/slot/slotTypes.ts';\nimport { getAttributesString } from './data/attributes.ts';\nimport { isData } from './data/dataTypes.ts';\nimport {\n type AnyWgslData,\n type BaseData,\n isWgslArray,\n isWgslStruct,\n} from './data/wgslTypes.ts';\nimport { MissingSlotValueError, ResolutionError } from './errors.ts';\nimport { popMode, provideCtx, pushMode, RuntimeMode } from './gpuMode.ts';\nimport type { JitTranspiler } from './jitTranspiler.ts';\nimport type { NameRegistry } from './nameRegistry.ts';\nimport { naturalsExcept } from './shared/generators.ts';\nimport type { Infer } from './shared/repr.ts';\nimport { $internal } from './shared/symbols.ts';\nimport {\n bindGroupLayout,\n type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n} from './tgpuBindGroupLayout.ts';\nimport { coerceToSnippet } from './tgsl/generationHelpers.ts';\nimport { generateFunction } from './tgsl/wgslGenerator.ts';\nimport type {\n FnToWgslOptions,\n ItemLayer,\n ItemStateStack,\n ResolutionCtx,\n Snippet,\n Wgsl,\n} from './types.ts';\nimport { isSelfResolvable, isWgsl, type UnknownData } from './types.ts';\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: Snippet[];\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: Snippet[],\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 getSnippetById(id: string): Snippet | 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 && external !== null) {\n return coerceToSnippet(external);\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): Snippet {\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): Snippet | null {\n const item = this._itemStateStack.getSnippetById(id);\n\n if (item === undefined) {\n return null;\n }\n\n return item;\n }\n\n defineVariable(id: string, dataType: AnyWgslData | UnknownData): Snippet {\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: ArgNames;\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 return {\n head: resolveFunctionHeader(this, options.args, options.returnType),\n body: generateFunction(this, options.body),\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\n ._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(\n 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(${\n (value as unknown[]).map((element) =>\n this.resolveValue(element, schema.elementType)\n )\n })`;\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)}(${\n Object.entries(schema.propTypes).map(([key, type_]) =>\n this.resolveValue((value as Infer<typeof schema>)[key], type_)\n )\n })`;\n }\n\n throw new Error(\n `Value ${value} (as json: ${\n JSON.stringify(value)\n }) 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\nexport function resolveFunctionHeader(\n ctx: ResolutionCtx,\n args: Snippet[],\n returnType: AnyWgslData,\n) {\n const argList = args\n .map((arg) => `${arg.value}: ${ctx.resolve(arg.dataType as AnyWgslData)}`)\n .join(', ');\n\n return returnType !== undefined\n ? `(${argList}) -> ${getAttributesString(returnType)} ${\n ctx.resolve(returnType)\n }`\n : `(${argList})`;\n}\n"]}
|
1
|
+
{"version":3,"sources":["/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","../src/extractGpuValueGetter.ts","../src/shared/utilityTypes.ts","../src/tgsl/generationHelpers.ts","../src/core/valueProxyUtils.ts","../src/core/function/fnCore.ts","../src/core/resolve/resolveData.ts","../src/data/compiledIO.ts","../src/data/dataIO.ts","../src/tgsl/wgslGenerator.ts","../src/resolutionCtx.ts"],"names":["extractGpuValueGetter","object","$gpuValueOf","assertExhaustive","x","location"],"mappings":"AAAA,u4CAAgH,wDAA0C,wDAA6pB,SCKvyBA,EAAAA,CACdC,CAAAA,CAC4B,CAE5B,EAAA,CAAI,uBAAQA,CAAAA,4BAAAA,CAAiBC,mBAAW,GAAA,EAAM,UAAA,CAC5C,OAAQD,CAAAA,CAA6CC,mBAAW,CAAA,CAAE,IAAA,CAChED,CACF,CAGJ,CC4BO,SAASE,EAAAA,CAAiBC,CAAAA,CAAUC,CAAAA,CAAyB,CAClE,MAAM,IAAI,KAAA,CAAM,CAAA,iBAAA,EAAoBD,CAAC,CAAA,IAAA,EAAOC,CAAQ,CAAA,CAAA;AC+e1C;AAEN;AAAoC,KAAA;ACzgBzB,4CAAA;ACgGH;ACYO;AACV,OAAA;AAKT;AAwBmB;AACV,OAAA;AAYT;AAAA;AAiGkB;ACzKR;AAeN;AAsBE;AASsB;ACvBV;AC3BoD;AAAK;AAoGhC;AAyWjC;AACH;AAPgB;AA0FjB;AA0BuB;AC9foB;AAubF;AAsG1B","file":"/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import { $gpuValueOf } from './shared/symbols.ts';\nimport type { ResolutionCtx } from './types.ts';\n\nexport type GpuValueGetter = (ctx: ResolutionCtx) => unknown;\n\nexport function extractGpuValueGetter(\n object: unknown,\n): GpuValueGetter | undefined {\n // biome-ignore lint/suspicious/noExplicitAny: we're inspecting the value\n if (typeof (object as any)?.[$gpuValueOf] === 'function') {\n return (object as { [$gpuValueOf]: GpuValueGetter })[$gpuValueOf].bind(\n object,\n );\n }\n return undefined;\n}\n","export type Default<T, TDefault> = unknown extends T ? TDefault\n : T extends undefined ? TDefault\n : T;\n\nexport type UnionToIntersection<U> =\n // biome-ignore lint/suspicious/noExplicitAny: <had to be done>\n (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I\n : never;\n\nexport type Prettify<T> =\n & {\n [K in keyof T]: T[K];\n }\n & {};\n\n/**\n * Removes properties from record type that extend `Prop`\n */\nexport type OmitProps<T extends Record<string, unknown>, Prop> = Pick<\n T,\n {\n [Key in keyof T]: T[Key] extends Prop ? never : Key;\n }[keyof T]\n>;\n\n/**\n * The opposite of Readonly<T>\n */\nexport type Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\n/**\n * Any typed array\n */\nexport type TypedArray =\n | Uint8Array\n | Uint16Array\n | Uint32Array\n | Int32Array\n | Float32Array\n | Float64Array;\n\nexport function assertExhaustive(x: never, location: string): never {\n throw new Error(`Failed to handle ${x} at ${location}`);\n}\n","import { arrayOf } from '../data/array.ts';\nimport {\n type AnyData,\n isDisarray,\n isSnippet,\n isUnstruct,\n snip,\n type Snippet,\n UnknownData,\n} from '../data/dataTypes.ts';\nimport { mat2x2f, mat3x3f, mat4x4f } from '../data/matrix.ts';\nimport {\n abstractFloat,\n abstractInt,\n bool,\n f16,\n f32,\n i32,\n u32,\n} from '../data/numeric.ts';\nimport {\n vec2b,\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3b,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4b,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n vecTypeToPrimitive,\n} from '../data/vector.ts';\nimport {\n type AnyWgslData,\n type AnyWgslStruct,\n type F16,\n type F32,\n hasInternalDataType,\n type I32,\n isMat,\n isMatInstance,\n isVec,\n isVecInstance,\n isWgslArray,\n isWgslStruct,\n type U32,\n} from '../data/wgslTypes.ts';\nimport { invariant } from '../errors.ts';\nimport { getResolutionCtx } from '../gpuMode.ts';\nimport { $wgslDataType } from '../shared/symbols.ts';\nimport { assertExhaustive } from '../shared/utilityTypes.ts';\nimport { isNumericSchema } from '../std/numeric.ts';\nimport type { ResolutionCtx } from '../types.ts';\n\ntype SwizzleableType = 'f' | 'h' | 'i' | 'u' | 'b';\ntype SwizzleLength = 1 | 2 | 3 | 4;\n\nconst swizzleLenToType: Record<\n SwizzleableType,\n Record<SwizzleLength, AnyData>\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 b: {\n 1: bool,\n 2: vec2b,\n 3: vec3b,\n 4: vec4b,\n },\n} as const;\n\nconst kindToSchema = {\n vec2f: vec2f,\n vec2h: vec2h,\n vec2i: vec2i,\n vec2u: vec2u,\n 'vec2<bool>': vec2b,\n vec3f: vec3f,\n vec3h: vec3h,\n vec3i: vec3i,\n vec3u: vec3u,\n 'vec3<bool>': vec3b,\n vec4f: vec4f,\n vec4h: vec4h,\n vec4i: vec4i,\n vec4u: vec4u,\n 'vec4<bool>': vec4b,\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 'vec2<bool>': bool,\n vec3f: f32,\n vec3h: f16,\n vec3i: i32,\n vec3u: u32,\n 'vec3<bool>': bool,\n vec4f: f32,\n vec4h: f16,\n vec4i: i32,\n vec4u: u32,\n 'vec4<bool>': bool,\n mat2x2f: vec2f,\n mat3x3f: vec3f,\n mat4x4f: vec4f,\n} as const;\n\nexport function getTypeForPropAccess(\n targetType: AnyData,\n propName: string,\n): AnyData | UnknownData {\n if (isWgslStruct(targetType) || isUnstruct(targetType)) {\n return targetType.propTypes[propName] as AnyData ?? UnknownData;\n }\n\n if (targetType === bool || isNumericSchema(targetType)) {\n // No props to be accessed here\n return UnknownData;\n }\n\n const propLength = propName.length;\n if (\n isVec(targetType) &&\n propLength >= 1 &&\n propLength <= 4\n ) {\n const swizzleTypeChar = targetType.type.includes('bool')\n ? 'b'\n : (targetType.type[4] as SwizzleableType);\n const swizzleType =\n swizzleLenToType[swizzleTypeChar][propLength as SwizzleLength];\n if (swizzleType) {\n return swizzleType;\n }\n }\n\n return UnknownData;\n}\n\nexport function getTypeForIndexAccess(\n dataType: AnyData,\n): AnyData | UnknownData {\n // array\n if (isWgslArray(dataType) || isDisarray(dataType)) {\n return dataType.elementType as AnyData;\n }\n\n // vector or matrix\n if (dataType.type in indexableTypeToResult) {\n return indexableTypeToResult[\n dataType.type as keyof typeof indexableTypeToResult\n ];\n }\n\n return UnknownData;\n}\n\nexport function numericLiteralToSnippet(value: string): Snippet | undefined {\n // Hex literals\n if (/^0x[0-9a-f]+$/i.test(value)) {\n return snip(value, abstractInt);\n }\n\n // Binary literals\n if (/^0b[01]+$/i.test(value)) {\n return snip(`${Number.parseInt(value.slice(2), 2)}`, abstractInt);\n }\n\n // Floating point literals\n if (/^-?(?:\\d+\\.\\d*|\\d*\\.\\d+)$/i.test(value)) {\n return snip(value, abstractFloat);\n }\n\n // Floating point literals with scientific notation\n if (/^-?\\d+(?:\\.\\d+)?e-?\\d+$/i.test(value)) {\n return snip(value, abstractFloat);\n }\n\n // Integer literals\n if (/^-?\\d+$/i.test(value)) {\n return snip(value, abstractInt);\n }\n\n return undefined;\n}\n\ntype ConversionAction = 'ref' | 'deref' | 'cast' | 'none';\n\ntype ConversionRankInfo =\n | { rank: number; action: 'cast'; targetType: AnyData }\n | { rank: number; action: Exclude<ConversionAction, 'cast'> };\n\nconst INFINITE_RANK: ConversionRankInfo = {\n rank: Number.POSITIVE_INFINITY,\n action: 'none',\n};\n\nfunction unwrapDecorated(data: AnyData): AnyData {\n if (data.type === 'decorated') {\n return data.inner as AnyData;\n }\n return data;\n}\n\nfunction getVectorComponent(type: AnyData): AnyData | undefined {\n return isVec(type) ? vecTypeToPrimitive[type.type] : undefined;\n}\n\nfunction getAutoConversionRank(\n src: AnyData,\n dest: AnyData,\n): ConversionRankInfo {\n const trueSrc = unwrapDecorated(src);\n const trueDst = unwrapDecorated(dest);\n\n if (trueSrc.type === trueDst.type) {\n return { rank: 0, action: 'none' };\n }\n\n if (trueSrc.type === 'abstractFloat') {\n if (trueDst.type === 'f32') return { rank: 1, action: 'none' };\n if (trueDst.type === 'f16') return { rank: 2, action: 'none' };\n }\n\n if (trueSrc.type === 'abstractInt') {\n if (trueDst.type === 'i32') return { rank: 3, action: 'none' };\n if (trueDst.type === 'u32') return { rank: 4, action: 'none' };\n if (trueDst.type === 'abstractFloat') return { rank: 5, action: 'none' };\n if (trueDst.type === 'f32') return { rank: 6, action: 'none' };\n if (trueDst.type === 'f16') return { rank: 7, action: 'none' };\n }\n\n if (isVec(trueSrc) && isVec(trueDst)) {\n const compSrc = getVectorComponent(trueSrc);\n const compDest = getVectorComponent(trueDst);\n if (compSrc && compDest) {\n return getAutoConversionRank(compSrc, compDest);\n }\n }\n\n if (isMat(trueSrc) && isMat(trueDst)) {\n // Matrix conversion rank depends only on component type (always f32 for now)\n return { rank: 0, action: 'none' };\n }\n\n return INFINITE_RANK;\n}\n\nfunction getImplicitConversionRank(\n src: AnyData,\n dest: AnyData,\n): ConversionRankInfo {\n const trueSrc = unwrapDecorated(src);\n const trueDst = unwrapDecorated(dest);\n\n if (\n trueSrc.type === 'ptr' &&\n getAutoConversionRank(trueSrc.inner as AnyData, trueDst).rank <\n Number.POSITIVE_INFINITY\n ) {\n return { rank: 0, action: 'deref' };\n }\n\n if (\n trueDst.type === 'ptr' &&\n getAutoConversionRank(trueSrc, trueDst.inner as AnyData).rank <\n Number.POSITIVE_INFINITY\n ) {\n return { rank: 1, action: 'ref' };\n }\n\n const primitivePreference = {\n f32: 0,\n f16: 1,\n i32: 2,\n u32: 3,\n bool: 4,\n } as const;\n type PrimitiveType = keyof typeof primitivePreference;\n\n if (\n trueSrc.type in primitivePreference &&\n trueDst.type in primitivePreference\n ) {\n const srcType = trueSrc.type as PrimitiveType;\n const destType = trueDst.type as PrimitiveType;\n\n if (srcType !== destType) {\n const srcPref = primitivePreference[srcType];\n const destPref = primitivePreference[destType];\n\n const rank = destPref < srcPref ? 10 : 20;\n\n return { rank: rank, action: 'cast', targetType: trueDst };\n }\n }\n\n return INFINITE_RANK;\n}\n\nfunction getConversionRank(\n src: AnyData,\n dest: AnyData,\n allowImplicit: boolean,\n): ConversionRankInfo {\n const autoRank = getAutoConversionRank(src, dest);\n if (autoRank.rank < Number.POSITIVE_INFINITY) {\n return autoRank;\n }\n if (allowImplicit) {\n return getImplicitConversionRank(src, dest);\n }\n return INFINITE_RANK;\n}\n\nexport type ConversionResultAction = {\n sourceIndex: number;\n action: ConversionAction;\n targetType?: U32 | F32 | I32 | F16;\n};\n\nexport type ConversionResult = {\n targetType: AnyData;\n actions: ConversionResultAction[];\n hasImplicitConversions?: boolean;\n};\n\nfunction findBestType(\n types: AnyData[],\n uniqueTypes: AnyData[],\n allowImplicit: boolean,\n): ConversionResult | undefined {\n let bestType: AnyData | undefined;\n let minSum = Number.POSITIVE_INFINITY;\n const conversionDetails = new Map<AnyData, ConversionRankInfo[]>();\n\n for (const targetType of uniqueTypes) {\n let currentSum = 0;\n const currentDetails: ConversionRankInfo[] = [];\n let possible = true;\n\n for (const sourceType of types) {\n const conversion = getConversionRank(\n sourceType,\n targetType,\n allowImplicit,\n );\n if (conversion.rank === Number.POSITIVE_INFINITY) {\n possible = false;\n break;\n }\n currentSum += conversion.rank;\n currentDetails.push(conversion);\n }\n\n if (possible && currentSum < minSum) {\n minSum = currentSum;\n bestType = targetType;\n conversionDetails.set(bestType, currentDetails);\n }\n }\n\n if (!bestType) {\n return undefined;\n }\n\n const bestDetails = conversionDetails.get(bestType) as ConversionRankInfo[];\n const actions: ConversionResultAction[] = bestDetails.map(\n (detail, index) => ({\n sourceIndex: index,\n action: detail.action,\n ...(detail.action === 'cast' && {\n targetType: detail.targetType as U32 | F32 | I32 | F16,\n }),\n }),\n );\n\n const hasCasts = actions.some((action) => action.action === 'cast');\n\n return { targetType: bestType, actions, hasImplicitConversions: hasCasts };\n}\n\nexport function concretize(type: AnyWgslData): AnyWgslData {\n if (type.type === 'abstractFloat') {\n return f32;\n }\n\n if (type.type === 'abstractInt') {\n return i32;\n }\n\n return type;\n}\n\nexport function getBestConversion(\n types: AnyData[],\n targetTypes?: AnyData[],\n): ConversionResult | undefined {\n if (types.length === 0) return undefined;\n\n const uniqueTypes = [...new Set(types.map(unwrapDecorated))];\n const uniqueTargetTypes = targetTypes\n ? [...new Set(targetTypes.map(unwrapDecorated))]\n : uniqueTypes;\n\n const explicitResult = findBestType(types, uniqueTargetTypes, false);\n if (explicitResult) {\n return explicitResult;\n }\n\n const implicitResult = findBestType(types, uniqueTargetTypes, true);\n if (implicitResult) {\n implicitResult.hasImplicitConversions = implicitResult.actions.some(\n (action) => action.action === 'cast',\n );\n return implicitResult;\n }\n\n return undefined;\n}\n\nexport function convertType(\n sourceType: AnyData,\n targetType: AnyData,\n allowImplicit = true,\n): ConversionResult | undefined {\n const conversion = getConversionRank(sourceType, targetType, allowImplicit);\n\n if (conversion.rank < Number.POSITIVE_INFINITY) {\n const actionDetail: ConversionResultAction = {\n sourceIndex: 0,\n action: conversion.action,\n };\n if (conversion.action === 'cast') {\n actionDetail.targetType = conversion.targetType as U32 | F32 | I32 | F16;\n }\n return {\n targetType: unwrapDecorated(targetType),\n actions: [actionDetail],\n hasImplicitConversions: conversion.action === 'cast',\n };\n }\n\n return undefined;\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): Snippet | null;\n defineVariable(id: string, dataType: AnyWgslData | UnknownData): Snippet;\n};\n\nfunction applyActionToSnippet(\n ctx: GenerationCtx,\n value: Snippet,\n action: ConversionResultAction,\n targetType: AnyData,\n): Snippet {\n if (action.action === 'none') {\n return snip(value.value, targetType);\n }\n\n const resolvedValue = ctx.resolve(value.value);\n\n switch (action.action) {\n case 'ref':\n return snip(`&${resolvedValue}`, targetType);\n case 'deref':\n return snip(`*${resolvedValue}`, targetType);\n case 'cast': {\n return snip(`${ctx.resolve(targetType)}(${resolvedValue})`, targetType);\n }\n default: {\n assertExhaustive(action.action, 'applyActionToSnippet');\n }\n }\n}\n\nexport function convertToCommonType(\n ctx: GenerationCtx,\n values: Snippet[],\n restrictTo?: AnyData[],\n): Snippet[] | undefined {\n const types = values.map((value) => value.dataType);\n\n if (types.some((type) => type === UnknownData)) {\n return undefined;\n }\n\n const conversion = getBestConversion(types as AnyData[], restrictTo);\n if (!conversion) {\n return undefined;\n }\n\n if (conversion.hasImplicitConversions) {\n console.warn(\n `Implicit conversions from [\\n${\n values\n .map((v) => ` ${v.value}: ${v.dataType.type}`)\n .join(\n ',\\n',\n )\n }\\n] to ${conversion.targetType.type} are supported, but not recommended.\nConsider using explicit conversions instead.`,\n );\n }\n\n return values.map((value, index) => {\n const action = conversion.actions[index];\n invariant(action, 'Action should not be undefined');\n return applyActionToSnippet(ctx, value, action, conversion.targetType);\n });\n}\n\nexport function convertStructValues(\n ctx: GenerationCtx,\n structType: AnyWgslStruct,\n values: Record<string, Snippet>,\n): Snippet[] {\n const propKeys = Object.keys(structType.propTypes);\n\n return propKeys.map((key) => {\n const val = values[key];\n if (!val) {\n throw new Error(`Missing property ${key}`);\n }\n\n const targetType = structType.propTypes[key];\n const converted = convertToCommonType(ctx, [val], [targetType as AnyData]);\n return converted?.[0] ?? val;\n });\n}\n\nexport function coerceToSnippet(value: unknown): Snippet {\n if (isSnippet(value)) {\n // Already a snippet\n return value;\n }\n\n if (hasInternalDataType(value)) {\n // The value knows better about what type it is\n return snip(value, value[$wgslDataType] as AnyData);\n }\n\n if (isVecInstance(value) || isMatInstance(value)) {\n return snip(value, kindToSchema[value.kind]);\n }\n\n if (Array.isArray(value)) {\n const coerced = value.map(coerceToSnippet).filter(Boolean);\n const context = getResolutionCtx() as GenerationCtx | undefined;\n if (!context) {\n throw new Error('Tried to coerce array without a context');\n }\n\n const converted = convertToCommonType(context, coerced as Snippet[]);\n const commonType = getBestConversion(\n coerced.map((v) => v.dataType as AnyData),\n )?.targetType as AnyWgslData | undefined;\n\n if (!converted || !commonType) {\n return snip(value, UnknownData);\n }\n\n return snip(\n converted.map((v) => v.value).join(', '),\n arrayOf(concretize(commonType), value.length),\n );\n }\n\n if (\n typeof value === 'string' || typeof value === 'function' ||\n typeof value === 'object' || typeof value === 'symbol' ||\n typeof value === 'undefined' || value === null\n ) {\n // Nothing representable in WGSL as-is, so unknown\n return snip(value, UnknownData);\n }\n\n if (typeof value === 'number' || typeof value === 'bigint') {\n return snip(\n value,\n numericLiteralToSnippet(String(value))?.dataType ?? UnknownData,\n );\n }\n\n if (typeof value === 'boolean') {\n return snip(value, bool);\n }\n\n return snip(value, UnknownData);\n}\n","import type { AnyData } from '../data/dataTypes.ts';\nimport type { BaseData } from '../data/wgslTypes.ts';\nimport {\n extractGpuValueGetter,\n type GpuValueGetter,\n} from '../extractGpuValueGetter.ts';\nimport { getName } from '../shared/meta.ts';\nimport { $wgslDataType } from '../shared/symbols.ts';\nimport { getTypeForPropAccess } from '../tgsl/generationHelpers.ts';\nimport type { ResolutionCtx, SelfResolvable } from '../types.ts';\n\nexport const valueProxyHandler: ProxyHandler<\n & SelfResolvable\n & { readonly [$wgslDataType]: BaseData }\n> = {\n get(target, prop) {\n if (prop in target) {\n return Reflect.get(target, prop);\n }\n\n if (prop === '~providing') {\n return undefined;\n }\n\n if (\n prop === 'toString' ||\n prop === Symbol.toStringTag ||\n prop === Symbol.toPrimitive\n ) {\n return () => target.toString();\n }\n\n return new Proxy(\n {\n '~resolve': (ctx: ResolutionCtx) =>\n `${ctx.resolve(target)}.${String(prop)}`,\n\n toString: () =>\n `.value(...).${String(prop)}:${getName(target) ?? '<unnamed>'}`,\n\n [$wgslDataType]: getTypeForPropAccess(\n target[$wgslDataType] as AnyData,\n String(prop),\n ) as BaseData,\n },\n valueProxyHandler,\n );\n },\n};\n\nexport function getGpuValueRecursively<T>(\n ctx: ResolutionCtx,\n value: unknown,\n): T {\n let unwrapped = value;\n let valueGetter: GpuValueGetter | undefined;\n\n // biome-ignore lint/suspicious/noAssignInExpressions: it's exactly what we want biome\n while (valueGetter = extractGpuValueGetter(unwrapped)) {\n unwrapped = valueGetter(ctx);\n }\n\n return unwrapped as T;\n}\n","import { FuncParameterType } from 'tinyest';\nimport { getAttributesString } from '../../data/attributes.ts';\nimport { snip } from '../../data/dataTypes.ts';\nimport {\n type AnyWgslData,\n type AnyWgslStruct,\n isWgslData,\n isWgslStruct,\n Void,\n} from '../../data/wgslTypes.ts';\nimport { MissingLinksError } from '../../errors.ts';\nimport { getMetaData, getName, setName } from '../../shared/meta.ts';\nimport type { ResolutionCtx } from '../../types.ts';\nimport {\n addArgTypesToExternals,\n addReturnTypeToExternals,\n applyExternals,\n type ExternalMap,\n replaceExternalsInWgsl,\n} from '../resolve/externals.ts';\nimport { extractArgs } from './extractArgs.ts';\nimport type { Implementation } from './fnTypes.ts';\n\nexport interface TgpuFnShellBase<Args extends unknown[], Return> {\n readonly argTypes: Args;\n readonly returnType: Return;\n readonly isEntry: boolean;\n}\n\nexport interface FnCore {\n applyExternals(newExternals: ExternalMap): void;\n resolve(ctx: ResolutionCtx, fnAttribute?: string): string;\n}\n\nexport function createFnCore(\n shell: TgpuFnShellBase<unknown[], unknown>,\n implementation: Implementation,\n): FnCore {\n /**\n * External application has to be deferred until resolution because\n * some externals can reference the owner function which has not been\n * initialized yet (like when accessing the Output struct of a vertex\n * entry fn).\n */\n const externalsToApply: ExternalMap[] = [];\n\n if (typeof implementation === 'string') {\n if (!shell.isEntry) {\n addArgTypesToExternals(\n implementation,\n shell.argTypes,\n (externals) => externalsToApply.push(externals),\n );\n addReturnTypeToExternals(\n implementation,\n shell.returnType,\n (externals) => externalsToApply.push(externals),\n );\n } else {\n if (isWgslStruct(shell.argTypes[0])) {\n externalsToApply.push({ In: shell.argTypes[0] });\n }\n\n if (isWgslStruct(shell.returnType)) {\n externalsToApply.push({ Out: shell.returnType });\n }\n }\n }\n\n const core = {\n applyExternals(newExternals: ExternalMap): void {\n externalsToApply.push(newExternals);\n },\n\n resolve(ctx: ResolutionCtx, fnAttribute = ''): string {\n const externalMap: ExternalMap = {};\n\n for (const externals of externalsToApply) {\n applyExternals(externalMap, externals);\n }\n\n const id = ctx.names.makeUnique(getName(this));\n\n if (typeof implementation === 'string') {\n const replacedImpl = replaceExternalsInWgsl(\n ctx,\n externalMap,\n implementation,\n );\n\n let header = '';\n let body = '';\n\n if (shell.isEntry) {\n const input = isWgslStruct(shell.argTypes[0])\n ? `(in: ${ctx.resolve(shell.argTypes[0])})`\n : '()';\n\n const attributes = isWgslData(shell.returnType)\n ? getAttributesString(shell.returnType)\n : '';\n const output = shell.returnType !== Void\n ? isWgslStruct(shell.returnType)\n ? `-> ${ctx.resolve(shell.returnType)}`\n : `-> ${attributes !== '' ? attributes : '@location(0)'} ${\n ctx.resolve(shell.returnType)\n }`\n : '';\n\n header = `${input} ${output} `;\n body = replacedImpl;\n } else {\n const providedArgs = extractArgs(replacedImpl);\n\n if (providedArgs.args.length !== shell.argTypes.length) {\n throw new Error(\n `WGSL implementation has ${providedArgs.args.length} arguments, while the shell has ${shell.argTypes.length} arguments.`,\n );\n }\n\n const input = providedArgs.args.map((argInfo, i) =>\n `${argInfo.identifier}: ${\n checkAndReturnType(\n ctx,\n `parameter ${argInfo.identifier}`,\n argInfo.type,\n shell.argTypes[i],\n )\n }`\n ).join(', ');\n\n const output = shell.returnType === Void\n ? ''\n : `-> ${\n checkAndReturnType(\n ctx,\n 'return type',\n providedArgs.ret?.type,\n shell.returnType,\n )\n }`;\n\n header = `(${input}) ${output}`;\n\n body = replacedImpl.slice(providedArgs.range.end);\n }\n\n ctx.addDeclaration(`${fnAttribute}fn ${id}${header}${body}`);\n } else {\n // get data generated by the plugin\n const pluginData = getMetaData(implementation);\n\n if (pluginData?.externals) {\n const missing = Object.fromEntries(\n Object.entries(pluginData.externals).filter(\n ([name]) => !(name in externalMap),\n ),\n );\n\n applyExternals(externalMap, missing);\n }\n const ast = pluginData?.ast ?? ctx.transpileFn(String(implementation));\n\n // verify all required externals are present\n const missingExternals = ast.externalNames.filter(\n (name) => !(name in externalMap),\n );\n if (missingExternals.length > 0) {\n throw new MissingLinksError(getName(this), missingExternals);\n }\n\n // generate wgsl string\n const { head, body } = ctx.fnToWgsl({\n args: shell.argTypes.map((arg, i) =>\n snip(\n ast.params[i]?.type === FuncParameterType.identifier\n ? ast.params[i].name\n : `_arg_${i}`,\n arg as AnyWgslData,\n )\n ),\n argAliases: Object.fromEntries(\n ast.params.flatMap((param, i) =>\n param.type === FuncParameterType.destructuredObject\n ? param.props.map(({ name, alias }) => [\n alias,\n snip(\n `_arg_${i}.${name}`,\n (shell.argTypes[i] as AnyWgslStruct)\n .propTypes[name] as AnyWgslData,\n ),\n ])\n : []\n ),\n ),\n returnType: shell.returnType as AnyWgslData,\n body: ast.body,\n externalMap,\n });\n\n ctx.addDeclaration(\n `${fnAttribute}fn ${id}${ctx.resolve(head)}${ctx.resolve(body)}`,\n );\n }\n\n return id;\n },\n };\n\n // The implementation could have been given a name by a bundler plugin,\n // so we try to transfer it to the core.\n const maybeName = getName(implementation);\n if (maybeName !== undefined) {\n setName(core, maybeName);\n }\n\n return core;\n}\n\nfunction checkAndReturnType(\n ctx: ResolutionCtx,\n name: string,\n wgslType: string | undefined,\n jsType: unknown,\n) {\n const resolvedJsType = ctx.resolve(jsType).replace(/\\s/g, '');\n\n if (!wgslType) {\n return resolvedJsType;\n }\n\n const resolvedWgslType = wgslType.replace(/\\s/g, '');\n\n if (resolvedWgslType !== resolvedJsType) {\n throw new Error(\n `Type mismatch between TGPU shell and WGSL code string: ${name}, JS type \"${resolvedJsType}\", WGSL type \"${resolvedWgslType}\".`,\n );\n }\n\n return wgslType;\n}\n","import { getAttributesString } from '../../data/attributes.ts';\nimport {\n type AnyData,\n type Disarray,\n isLooseData,\n type Unstruct,\n} from '../../data/dataTypes.ts';\nimport { formatToWGSLType } from '../../data/vertexFormatData.ts';\nimport type {\n AnyWgslData,\n BaseData,\n Bool,\n F16,\n F32,\n I32,\n Mat2x2f,\n Mat3x3f,\n Mat4x4f,\n U32,\n Vec2b,\n Vec2f,\n Vec2h,\n Vec2i,\n Vec2u,\n Vec3b,\n Vec3f,\n Vec3h,\n Vec3i,\n Vec3u,\n Vec4b,\n Vec4f,\n Vec4h,\n Vec4i,\n Vec4u,\n WgslArray,\n WgslStruct,\n} from '../../data/wgslTypes.ts';\nimport { getName } from '../../shared/meta.ts';\nimport { assertExhaustive } from '../../shared/utilityTypes.ts';\nimport type { ResolutionCtx } from '../../types.ts';\nimport { isAttribute } from '../vertexLayout/connectAttributesToShader.ts';\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 'vec2<bool>',\n 'vec3<bool>',\n 'vec4<bool>',\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 | Vec2b\n | Vec3b\n | Vec4b\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}: ${\n ctx.resolve(property as AnyWgslData)\n },\\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(getName(struct));\n\n ctx.addDeclaration(`\nstruct ${id} {\n${\n Object.entries(struct.propTypes)\n .map((prop) => resolveStructProperty(ctx, prop))\n .join('')\n }\\\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(getName(unstruct));\n\n ctx.addDeclaration(`\nstruct ${id} {\n${\n Object.entries(unstruct.propTypes)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [\n prop[0],\n formatToWGSLType[prop[1].format],\n ])\n : resolveStructProperty(ctx, prop)\n )\n .join('')\n }\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)}, ${\n data.access === 'read-write' ? 'read_write' : data.access\n }>`;\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 if (data.type === 'void') {\n throw new Error('Void has no representation in WGSL');\n }\n\n assertExhaustive(data, 'resolveData');\n}\n","import { roundUp } from '../mathUtils.ts';\nimport type { Infer } from '../shared/repr.ts';\nimport { alignmentOf } from './alignmentOf.ts';\nimport { isDisarray, isUnstruct } from './dataTypes.ts';\nimport { offsetsForProps } from './offsets.ts';\nimport { sizeOf } from './sizeOf.ts';\nimport * as wgsl from './wgslTypes.ts';\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.isAtomic(node) || wgsl.isDecorated(node)) {\n return buildWriter(node.inner, offsetExpr, valueExpr);\n }\n\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}.${\n components[i]\n }, 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 +=\n `output.${writeFunc}((${offsetExpr} + ${byteOffset}), ${valueExpr}.columns[${colIndex}].${\n ['x', 'y', 'z', 'w'][rowIndex]\n }, littleEndian);\\n`;\n }\n\n return code;\n }\n\n const primitive = typeToPrimitive[node.type as keyof typeof typeToPrimitive];\n return `output.${\n primitiveToWriteFunction[primitive]\n }(${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.ts';\nimport alignIO from './alignIO.ts';\nimport { alignmentOf, customAlignmentOf } from './alignmentOf.ts';\nimport type {\n AnyConcreteData,\n AnyData,\n Disarray,\n LooseDecorated,\n Unstruct,\n} from './dataTypes.ts';\nimport { mat2x2f, mat3x3f, mat4x4f } from './matrix.ts';\nimport { sizeOf } from './sizeOf.ts';\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.ts';\nimport type * as wgsl from './wgslTypes.ts';\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() {\n throw new Error('Booleans are not host-shareable');\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 'vec2<bool>'() {\n throw new Error('Booleans are not host-shareable');\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 'vec3<bool>'() {\n throw new Error('Booleans are not host-shareable');\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 'vec4<bool>'() {\n throw new Error('Booleans are not host-shareable');\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: wgsl.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(): boolean {\n throw new Error('Booleans are not host-shareable');\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 'vec2<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n 'vec3<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n 'vec4<bool>'() {\n throw new Error('Booleans are not host-shareable');\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: wgsl.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 * as tinyest from 'tinyest';\nimport { arrayOf } from '../data/array.ts';\nimport {\n type AnyData,\n isData,\n isLooseData,\n snip,\n type Snippet,\n UnknownData,\n} from '../data/dataTypes.ts';\nimport * as d from '../data/index.ts';\nimport { abstractInt } from '../data/numeric.ts';\nimport * as wgsl from '../data/wgslTypes.ts';\nimport { getName } from '../shared/meta.ts';\nimport { $internal } from '../shared/symbols.ts';\nimport { type FnArgsConversionHint, isMarkedInternal } from '../types.ts';\nimport {\n coerceToSnippet,\n concretize,\n convertStructValues,\n convertToCommonType,\n type GenerationCtx,\n getTypeForIndexAccess,\n getTypeForPropAccess,\n numericLiteralToSnippet,\n} from './generationHelpers.ts';\nimport { ResolutionError } from '../errors.ts';\n\nconst { NodeTypeCatalog: NODE } = tinyest;\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 | tinyest.BinaryOperator\n | tinyest.AssignmentOperator\n | tinyest.LogicalOperator\n | tinyest.UnaryOperator;\n\nfunction operatorToType<\n TL extends AnyData | UnknownData,\n TR extends AnyData | 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\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 generateBlock(\n ctx: GenerationCtx,\n [_, statements]: tinyest.Block,\n): string {\n ctx.pushBlockScope();\n try {\n return `${ctx.indent()}{\n${statements.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): Snippet {\n return ctx.defineVariable(id, dataType);\n}\n\nexport function generateIdentifier(ctx: GenerationCtx, id: string): Snippet {\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: tinyest.Expression,\n): Snippet {\n if (typeof expression === 'string') {\n return generateIdentifier(ctx, expression);\n }\n\n if (typeof expression === 'boolean') {\n return snip(expression ? 'true' : 'false', d.bool);\n }\n\n if (\n expression[0] === NODE.logicalExpr ||\n expression[0] === NODE.binaryExpr ||\n expression[0] === NODE.assignmentExpr\n ) {\n // Logical/Binary/Assignment Expression\n const [_, lhs, op, rhs] = expression;\n const lhsExpr = generateExpression(ctx, lhs);\n const rhsExpr = generateExpression(ctx, rhs);\n\n const converted = convertToCommonType(ctx, [lhsExpr, rhsExpr]) as\n | [Snippet, Snippet]\n | undefined;\n const [convLhs, convRhs] = converted || [lhsExpr, rhsExpr];\n\n const lhsStr = ctx.resolve(convLhs.value);\n const rhsStr = ctx.resolve(convRhs.value);\n const type = operatorToType(convLhs.dataType, op, convRhs.dataType);\n\n return snip(\n parenthesizedOps.includes(op)\n ? `(${lhsStr} ${op} ${rhsStr})`\n : `${lhsStr} ${op} ${rhsStr}`,\n type,\n );\n }\n\n if (expression[0] === NODE.postUpdate) {\n // Post-Update Expression\n const [_, op, arg] = expression;\n const argExpr = generateExpression(ctx, arg);\n const argStr = ctx.resolve(argExpr.value);\n\n return snip(`${argStr}${op}`, argExpr.dataType);\n }\n\n if (expression[0] === NODE.unaryExpr) {\n // Unary Expression\n const [_, op, arg] = expression;\n const argExpr = generateExpression(ctx, arg);\n const argStr = ctx.resolve(argExpr.value);\n\n const type = operatorToType(argExpr.dataType, op);\n return snip(`${op}${argStr}`, type);\n }\n\n if (expression[0] === NODE.memberAccess) {\n // Member Access\n const [_, targetNode, property] = expression;\n const target = generateExpression(ctx, targetNode);\n\n if (target.dataType.type === 'unknown') {\n // No idea what the type is, so we act on the snippet's value and try to guess\n\n // biome-ignore lint/suspicious/noExplicitAny: we're inspecting the value, and it could be any value\n const propValue = (target.value as any)[property];\n\n // We try to extract any type information based on the prop's value\n return coerceToSnippet(propValue);\n }\n\n if (wgsl.isPtr(target.dataType)) {\n return snip(\n `(*${ctx.resolve(target.value)}).${property}`,\n getTypeForPropAccess(target.dataType.inner as AnyData, property),\n );\n }\n\n if (wgsl.isWgslArray(target.dataType) && property === 'length') {\n if (target.dataType.elementCount === 0) {\n // Dynamically-sized array\n return snip(`arrayLength(&${ctx.resolve(target.value)})`, d.u32);\n }\n\n return snip(String(target.dataType.elementCount), abstractInt);\n }\n\n if (wgsl.isMat(target.dataType) && property === 'columns') {\n return snip(target.value, target.dataType);\n }\n\n if (\n wgsl.isVec(target.dataType) && wgsl.isVecInstance(target.value)\n ) {\n // We're operating on a vector that's known at resolution time\n // biome-ignore lint/suspicious/noExplicitAny: it's probably a swizzle\n return coerceToSnippet((target.value as any)[property]);\n }\n\n return snip(\n `${ctx.resolve(target.value)}.${property}`,\n getTypeForPropAccess(target.dataType, property),\n );\n }\n\n if (expression[0] === NODE.indexAccess) {\n // Index Access\n const [_, targetNode, propertyNode] = expression;\n const target = generateExpression(ctx, targetNode);\n const property = generateExpression(ctx, propertyNode);\n const targetStr = ctx.resolve(target.value);\n const propertyStr = ctx.resolve(property.value);\n\n if (target.dataType.type === 'unknown') {\n // No idea what the type is, so we act on the snippet's value and try to guess\n\n if (\n Array.isArray(propertyNode) && propertyNode[0] === NODE.numericLiteral\n ) {\n return coerceToSnippet(\n // biome-ignore lint/suspicious/noExplicitAny: we're inspecting the value, and it could be any value\n (target.value as any)[propertyNode[1] as number],\n );\n }\n\n throw new Error(\n `Cannot index value ${targetStr} of unknown type with index ${propertyStr}`,\n );\n }\n\n if (wgsl.isPtr(target.dataType)) {\n return snip(\n `(*${targetStr})[${propertyStr}]`,\n getTypeForIndexAccess(target.dataType.inner as AnyData),\n );\n }\n\n return snip(\n `${targetStr}[${propertyStr}]`,\n isData(target.dataType)\n ? getTypeForIndexAccess(target.dataType)\n : UnknownData,\n );\n }\n\n if (expression[0] === NODE.numericLiteral) {\n // Numeric Literal\n const type = numericLiteralToSnippet(expression[1]);\n if (!type) {\n throw new Error(`Invalid numeric literal ${expression[1]}`);\n }\n return type;\n }\n\n if (expression[0] === NODE.call) {\n // Function Call\n const [_, callee, args] = expression;\n const id = generateExpression(ctx, callee);\n\n ctx.callStack.push(id.value);\n\n const argSnippets = args.map((arg) => generateExpression(ctx, arg));\n const resolvedSnippets = argSnippets.map((res) =>\n snip(ctx.resolve(res.value), res.dataType)\n );\n const argValues = resolvedSnippets.map((res) => res.value);\n\n ctx.callStack.pop();\n\n resolvedSnippets.forEach((sn, idx) => {\n if (sn.dataType === UnknownData) {\n throw new Error(\n `Tried to pass '${sn.value}' of unknown type as argument #${idx} to '${\n typeof id.value === 'string'\n ? id.value\n : getName(id.value) ?? '<unnamed>'\n }()'`,\n );\n }\n });\n\n if (typeof id.value === 'string') {\n return snip(`${id.value}(${argValues.join(', ')})`, id.dataType);\n }\n\n if (wgsl.isWgslStruct(id.value)) {\n const resolvedId = ctx.resolve(id.value);\n\n return snip(\n `${resolvedId}(${argValues.join(', ')})`,\n // Unintuitive, but the type of the return value is the struct itself\n id.value,\n );\n }\n\n if (!isMarkedInternal(id.value)) {\n throw new Error(\n `Function ${String(id.value)} ${\n getName(id.value)\n } has not been created using TypeGPU APIs. Did you mean to wrap the function with tgpu.fn(args, return)(...) ?`,\n );\n }\n\n const argTypes = id.value[$internal]?.argTypes as\n | FnArgsConversionHint\n | undefined;\n let convertedResources: Snippet[];\n try {\n if (!argTypes || argTypes === 'keep') {\n convertedResources = resolvedSnippets;\n } else if (argTypes === 'coerce') {\n convertedResources = convertToCommonType(ctx, resolvedSnippets) ??\n resolvedSnippets;\n } else {\n const pairs =\n (Array.isArray(argTypes) ? argTypes : (argTypes(...resolvedSnippets)))\n .map((type, i) => [type, resolvedSnippets[i] as Snippet] as const);\n\n convertedResources = pairs.map(([type, sn]) => {\n if (sn.dataType.type === 'unknown') {\n console.warn(\n `Internal error: unknown type when generating expression: ${expression}`,\n );\n return sn;\n }\n\n const conv = convertToCommonType(ctx, [sn], [type])?.[0];\n if (!conv) {\n throw new ResolutionError(\n `Cannot convert argument of type '${sn.dataType.type}' to '${type.type}' for function ${\n getName(id.value)\n }`,\n [{\n function: id.value,\n callStack: ctx.callStack,\n error:\n `Cannot convert argument of type '${sn.dataType.type}' to '${type.type}'`,\n toString: () => getName(id.value),\n }],\n );\n }\n return conv;\n });\n }\n\n // Assuming that `id` is callable\n const fnRes = (id.value as unknown as (...args: unknown[]) => unknown)(\n ...convertedResources,\n ) as Snippet;\n return snip(ctx.resolve(fnRes.value), fnRes.dataType);\n } catch (error) {\n throw new ResolutionError(error, [{\n toString: () => getName(id.value),\n }]);\n }\n }\n\n if (expression[0] === NODE.objectExpr) {\n // Object Literal\n const obj = expression[1];\n const callee = ctx.callStack[ctx.callStack.length - 1];\n\n if (wgsl.isWgslStruct(callee)) {\n const propKeys = Object.keys(callee.propTypes);\n const entries = Object.fromEntries(\n 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 [key, generateExpression(ctx, val)];\n }),\n );\n\n const convertedValues = convertStructValues(ctx, callee, entries);\n\n return snip(\n convertedValues.map((v) => ctx.resolve(v.value)).join(', '),\n callee,\n );\n }\n\n if (isMarkedInternal(callee)) {\n const argTypes = callee[$internal]?.argTypes;\n\n if (typeof argTypes === 'object' && argTypes !== null) {\n const propKeys = Object.keys(argTypes);\n const snippets: Record<string, Snippet> = {};\n\n for (const key of propKeys) {\n const val = obj[key];\n if (val === undefined) {\n throw new Error(\n `Missing property ${key} in object literal for function ${callee}`,\n );\n }\n const expr = generateExpression(ctx, val);\n const targetType = argTypes[key as keyof typeof argTypes];\n const converted = convertToCommonType(ctx, [expr], [targetType]);\n snippets[key] = converted?.[0] ?? expr;\n }\n\n return snip(snippets, UnknownData);\n }\n }\n\n throw new Error(\n 'Object expressions are only allowed as return values of functions or as arguments to structs.',\n );\n }\n\n if (expression[0] === NODE.arrayExpr) {\n const [_, valuesRaw] = expression;\n // Array Expression\n const values = valuesRaw.map((value) =>\n generateExpression(ctx, value as tinyest.Expression)\n );\n if (values.length === 0) {\n throw new Error('Cannot create empty array literal.');\n }\n\n const convertedValues = convertToCommonType(ctx, values);\n if (!convertedValues) {\n throw new Error(\n 'The given values cannot be automatically converted to a common type. Consider explicitly casting them.',\n );\n }\n\n const targetType = convertedValues[0]?.dataType as AnyData;\n const type = targetType.type === 'abstractFloat'\n ? d.f32\n : targetType.type === 'abstractInt'\n ? d.i32\n : targetType;\n\n const typeId = ctx.resolve(type);\n\n const arrayType = `array<${typeId}, ${values.length}>`;\n const arrayValues = convertedValues.map((sn) => ctx.resolve(sn.value));\n\n return snip(\n `${arrayType}( ${arrayValues.join(', ')} )`,\n arrayOf(\n type as wgsl.AnyWgslData,\n values.length,\n ) as wgsl.AnyWgslData,\n );\n }\n\n if (expression[0] === NODE.stringLiteral) {\n throw new Error('Cannot use string literals in TGSL.');\n }\n\n if (expression[0] === NODE.preUpdate) {\n throw new Error('Cannot use pre-updates in TGSL.');\n }\n\n assertExhaustive(expression);\n}\n\nfunction blockifySingleStatement(statement: tinyest.Statement): tinyest.Block {\n return typeof statement !== 'object' ||\n statement[0] !== NODE.block\n ? [NODE.block, [statement]]\n : statement;\n}\n\nexport function generateStatement(\n ctx: GenerationCtx,\n statement: tinyest.Statement,\n): string {\n if (typeof statement === 'string') {\n return `${ctx.pre}${\n ctx.resolve(generateIdentifier(ctx, statement).value)\n };`;\n }\n\n if (typeof statement === 'boolean') {\n return `${ctx.pre}${statement ? 'true' : 'false'};`;\n }\n\n if (statement[0] === NODE.return) {\n const returnNode = statement[1];\n const returnValue = returnNode !== undefined\n ? ctx.resolve(generateExpression(ctx, returnNode).value)\n : undefined;\n\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 typeof returnNode === 'object' &&\n returnNode[0] === NODE.objectExpr\n ) {\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}return ${resolvedStruct}(${returnValue});`;\n }\n\n return returnValue\n ? `${ctx.pre}return ${returnValue};`\n : `${ctx.pre}return;`;\n }\n\n if (statement[0] === NODE.if) {\n const [_, cond, cons, alt] = statement;\n const condExpr = generateExpression(ctx, cond);\n let condSnippet = condExpr;\n const converted = convertToCommonType(ctx, [condExpr], [d.bool]);\n if (converted?.[0]) {\n [condSnippet] = converted;\n }\n const condition = ctx.resolve(condSnippet.value);\n\n ctx.indent(); // {\n const consequent = generateStatement(ctx, blockifySingleStatement(cons));\n ctx.dedent(); // }\n\n ctx.indent(); // {\n const alternate = alt\n ? generateStatement(ctx, blockifySingleStatement(alt))\n : 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 (statement[0] === NODE.let || statement[0] === NODE.const) {\n const [_, rawId, rawValue] = statement;\n const eq = rawValue !== undefined\n ? generateExpression(ctx, rawValue)\n : undefined;\n\n if (!eq) {\n throw new Error(\n `Cannot create variable '${rawId}' without an initial value.`,\n );\n }\n\n if (isLooseData(eq.dataType)) {\n throw new Error(\n `Cannot create variable '${rawId}' with loose data type.`,\n );\n }\n\n registerBlockVariable(\n ctx,\n rawId,\n concretize(eq.dataType as wgsl.AnyWgslData),\n );\n const id = ctx.resolve(generateIdentifier(ctx, rawId).value);\n\n // If the value is a plain JS object it has to be an output struct\n if (\n typeof rawValue === 'object' &&\n rawValue[0] === NODE.objectExpr &&\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1])\n ) {\n const structType = ctx.callStack[\n ctx.callStack.length - 1\n ] as wgsl.WgslStruct;\n const obj = rawValue[1];\n\n const entries: Record<string, Snippet> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (!value) {\n throw new Error(`Missing property ${key} in object literal`);\n }\n entries[key] = generateExpression(ctx, value);\n }\n\n const convertedValues = convertStructValues(ctx, structType, entries);\n const resolvedStruct = ctx.resolve(structType);\n return `${ctx.pre}var ${id} = ${resolvedStruct}(${\n convertedValues.map((sn) => ctx.resolve(sn.value)).join(', ')\n });`;\n }\n\n return `${ctx.pre}var ${id} = ${ctx.resolve(eq.value)};`;\n }\n\n if (statement[0] === NODE.block) {\n return generateBlock(ctx, statement);\n }\n\n if (statement[0] === NODE.for) {\n const [_, init, condition, update, body] = statement;\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 let condSnippet = conditionExpr;\n if (conditionExpr) {\n const converted = convertToCommonType(ctx, [conditionExpr], [d.bool]);\n if (converted?.[0]) {\n [condSnippet] = converted;\n }\n }\n const conditionStr = condSnippet ? ctx.resolve(condSnippet.value) : '';\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, blockifySingleStatement(body));\n ctx.dedent();\n\n return `\\\n${ctx.pre}for (${initStr}; ${conditionStr}; ${updateStr})\n${bodyStr}`;\n }\n\n if (statement[0] === NODE.while) {\n const [_, condition, body] = statement;\n const condExpr = generateExpression(ctx, condition);\n let condSnippet = condExpr;\n if (condExpr) {\n const converted = convertToCommonType(ctx, [condExpr], [d.bool]);\n if (converted?.[0]) {\n [condSnippet] = converted;\n }\n }\n const conditionStr = ctx.resolve(condSnippet.value);\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, blockifySingleStatement(body));\n ctx.dedent();\n\n return `\\\n${ctx.pre}while (${conditionStr})\n${bodyStr}`;\n }\n\n if (statement[0] === NODE.continue) {\n return `${ctx.pre}continue;`;\n }\n\n if (statement[0] === NODE.break) {\n return `${ctx.pre}break;`;\n }\n\n return `${ctx.pre}${ctx.resolve(generateExpression(ctx, statement).value)};`;\n}\n\nexport function generateFunction(\n ctx: GenerationCtx,\n body: tinyest.Block,\n): string {\n return generateBlock(ctx, body);\n}\n","import type { Block, FuncParameter } from 'tinyest';\nimport { resolveData } from './core/resolve/resolveData.ts';\nimport {\n type Eventual,\n isDerived,\n isProviding,\n isSlot,\n type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n} from './core/slot/slotTypes.ts';\nimport { getAttributesString } from './data/attributes.ts';\nimport {\n type AnyData,\n isData,\n snip,\n type Snippet,\n type UnknownData,\n} from './data/dataTypes.ts';\nimport { type BaseData, isWgslArray, isWgslStruct } from './data/wgslTypes.ts';\nimport { MissingSlotValueError, ResolutionError } from './errors.ts';\nimport { popMode, provideCtx, pushMode, RuntimeMode } from './gpuMode.ts';\nimport type { JitTranspiler } from './jitTranspiler.ts';\nimport type { NameRegistry } from './nameRegistry.ts';\nimport { naturalsExcept } from './shared/generators.ts';\nimport type { Infer } from './shared/repr.ts';\nimport { $internal } from './shared/symbols.ts';\nimport {\n bindGroupLayout,\n type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n} from './tgpuBindGroupLayout.ts';\nimport { coerceToSnippet } from './tgsl/generationHelpers.ts';\nimport { generateFunction } from './tgsl/wgslGenerator.ts';\nimport type {\n FnToWgslOptions,\n ItemLayer,\n ItemStateStack,\n ResolutionCtx,\n Wgsl,\n} from './types.ts';\nimport { isSelfResolvable, isWgsl } from './types.ts';\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: Snippet[];\n argAliases: Record<string, Snippet>;\n externalMap: Record<string, unknown>;\n returnType: AnyData;\n};\n\ntype BlockScopeLayer = {\n type: 'blockScope';\n declarations: Map<string, AnyData | 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: Snippet[],\n argAliases: Record<string, Snippet>,\n returnType: AnyData,\n externalMap: Record<string, unknown>,\n ) {\n this._stack.push({\n type: 'functionScope',\n args,\n argAliases,\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, AnyData | 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 getSnippetById(id: string): Snippet | 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 if (layer.argAliases[id]) {\n return layer.argAliases[id];\n }\n\n const external = layer.externalMap[id];\n\n if (external !== undefined && external !== null) {\n return coerceToSnippet(external);\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 snip(id, declarationType);\n }\n } else {\n // Skip\n }\n }\n\n return undefined;\n }\n\n defineBlockVariable(id: string, type: AnyData | UnknownData): Snippet {\n if (type.type === 'unknown') {\n throw Error(`Tried to define variable '${id}' of unknown type`);\n }\n\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 snip(id, 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): Snippet | null {\n const item = this._itemStateStack.getSnippetById(id);\n\n if (item === undefined) {\n return null;\n }\n\n return item;\n }\n\n defineVariable(id: string, dataType: AnyData | UnknownData): Snippet {\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 params: FuncParameter[];\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.argAliases,\n options.returnType,\n options.externalMap,\n );\n\n try {\n return {\n head: resolveFunctionHeader(this, options.args, options.returnType),\n body: generateFunction(this, options.body),\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\n ._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(\n 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(${\n (value as unknown[]).map((element) =>\n this.resolveValue(element, schema.elementType)\n )\n })`;\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)}(${\n Object.entries(schema.propTypes).map(([key, type_]) =>\n this.resolveValue((value as Infer<typeof schema>)[key], type_)\n )\n })`;\n }\n\n throw new Error(\n `Value ${value} (as json: ${\n JSON.stringify(value)\n }) 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\nexport function resolveFunctionHeader(\n ctx: ResolutionCtx,\n args: Snippet[],\n returnType: AnyData,\n) {\n const argList = args\n .map((arg) => `${arg.value}: ${ctx.resolve(arg.dataType as AnyData)}`)\n .join(', ');\n\n return returnType.type !== 'void'\n ? `(${argList}) -> ${getAttributesString(returnType)} ${\n ctx.resolve(returnType)\n }`\n : `(${argList})`;\n}\n"]}
|
package/index.d.cts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { W as Wgsl, J as JitTranspiler, T as TgpuRoot, A as AnyWgslData, a as TgpuFn, b as TgpuBufferUsage, I as Infer, c as TgpuAccessor, d as TgpuDerived, e as TgpuSlot, f as TgpuBindGroupLayout, g as TgpuVertexLayout, h as WgslArray, D as Disarray, i as TgpuBuffer, j as AnyData, k as bindGroupLayout, v as vertexLayout, l as fn, m as fragmentFn, n as vertexFn, o as computeFn, p as privateVar, w as workgroupVar, q as constant, r as declare, s as sampler, t as comparisonSampler } from './tgpuComputeFn-
|
2
|
-
export { am as BindLayoutEntry, a8 as Eventual, an as ExtractBindGroupInputFromLayout, ao as LayoutEntryToInput, R as RandomNameRegistry, ag as Render, ah as Sampled, Z as Storage, _ as StorageFlag, S as StrictNameRegistry, af as TextureProps, a9 as TgpuAnyTextureView, ap as TgpuBindGroup, a5 as TgpuBufferMutable, a6 as TgpuBufferReadonly, a7 as TgpuBufferUniform, aD as TgpuComputeFn, aE as TgpuComputeFnShell, a0 as TgpuComputePipeline, ai as TgpuConst, aF as TgpuDeclare, ay as TgpuFnShell, aB as TgpuFragmentFn, aC as TgpuFragmentFnShell, aq as TgpuLayoutComparisonSampler, ar as TgpuLayoutEntry, as as TgpuLayoutExternalTexture, at as TgpuLayoutSampler, au as TgpuLayoutStorage, av as TgpuLayoutStorageTexture, aw as TgpuLayoutTexture, ax as TgpuLayoutUniform, aa as TgpuMutableTexture, ab as TgpuReadonlyTexture, $ as TgpuRenderPipeline, ac as TgpuSampledTexture, al as TgpuSampler, ad as TgpuTexture, aj as TgpuVar, az as TgpuVertexFn, aA as TgpuVertexFnShell, ae as TgpuWriteonlyTexture, a1 as Uniform, a2 as UniformFlag, ak as VariableScope, a3 as Vertex, a4 as VertexFlag, U as WithBinding, V as WithCompute, X as WithFragment, Y as WithVertex, u as isBuffer, B as isComparisonSampler, y as isDerived, E as isSampledTextureView, C as isSampler, z as isSlot, F as isStorageTextureView, G as isTexture, Q as isTgpuFn, H as isUsableAsRender, K as isUsableAsSampled, L as isUsableAsStorage, P as isUsableAsUniform, x as isUsableAsVertex, M as unstable_asMutable, N as unstable_asReadonly, O as unstable_asUniform } from './tgpuComputeFn-
|
1
|
+
import { W as Wgsl, J as JitTranspiler, T as TgpuRoot, A as AnyWgslData, a as TgpuFn, b as TgpuBufferUsage, I as Infer, c as TgpuAccessor, d as TgpuDerived, e as TgpuSlot, f as TgpuBindGroupLayout, g as TgpuVertexLayout, h as WgslArray, D as Disarray, i as TgpuBuffer, j as AnyData, k as bindGroupLayout, v as vertexLayout, l as fn, m as fragmentFn, n as vertexFn, o as computeFn, p as privateVar, w as workgroupVar, q as constant, r as declare, s as sampler, t as comparisonSampler } from './tgpuComputeFn-BxPDI5hQ.cjs';
|
2
|
+
export { am as BindLayoutEntry, a8 as Eventual, an as ExtractBindGroupInputFromLayout, ao as LayoutEntryToInput, R as RandomNameRegistry, ag as Render, ah as Sampled, Z as Storage, _ as StorageFlag, S as StrictNameRegistry, af as TextureProps, a9 as TgpuAnyTextureView, ap as TgpuBindGroup, a5 as TgpuBufferMutable, a6 as TgpuBufferReadonly, a7 as TgpuBufferUniform, aD as TgpuComputeFn, aE as TgpuComputeFnShell, a0 as TgpuComputePipeline, ai as TgpuConst, aF as TgpuDeclare, ay as TgpuFnShell, aB as TgpuFragmentFn, aC as TgpuFragmentFnShell, aq as TgpuLayoutComparisonSampler, ar as TgpuLayoutEntry, as as TgpuLayoutExternalTexture, at as TgpuLayoutSampler, au as TgpuLayoutStorage, av as TgpuLayoutStorageTexture, aw as TgpuLayoutTexture, ax as TgpuLayoutUniform, aa as TgpuMutableTexture, ab as TgpuReadonlyTexture, $ as TgpuRenderPipeline, ac as TgpuSampledTexture, al as TgpuSampler, ad as TgpuTexture, aj as TgpuVar, az as TgpuVertexFn, aA as TgpuVertexFnShell, ae as TgpuWriteonlyTexture, a1 as Uniform, a2 as UniformFlag, ak as VariableScope, a3 as Vertex, a4 as VertexFlag, U as WithBinding, V as WithCompute, X as WithFragment, Y as WithVertex, u as isBuffer, B as isComparisonSampler, y as isDerived, E as isSampledTextureView, C as isSampler, z as isSlot, F as isStorageTextureView, G as isTexture, Q as isTgpuFn, H as isUsableAsRender, K as isUsableAsSampled, L as isUsableAsStorage, P as isUsableAsUniform, x as isUsableAsVertex, M as unstable_asMutable, N as unstable_asReadonly, O as unstable_asUniform } from './tgpuComputeFn-BxPDI5hQ.cjs';
|
3
3
|
import 'tinyest';
|
4
4
|
|
5
5
|
interface TgpuResolveOptions {
|
@@ -65,7 +65,9 @@ declare function resolve(options: TgpuResolveOptions): string;
|
|
65
65
|
*/
|
66
66
|
type InitOptions = {
|
67
67
|
adapter?: GPURequestAdapterOptions | undefined;
|
68
|
-
device?: GPUDeviceDescriptor
|
68
|
+
device?: GPUDeviceDescriptor & {
|
69
|
+
optionalFeatures?: Iterable<GPUFeatureName>;
|
70
|
+
} | undefined;
|
69
71
|
/** @default 'random' */
|
70
72
|
unstable_names?: 'random' | 'strict' | undefined;
|
71
73
|
unstable_jitTranspiler?: JitTranspiler | undefined;
|
package/index.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { W as Wgsl, J as JitTranspiler, T as TgpuRoot, A as AnyWgslData, a as TgpuFn, b as TgpuBufferUsage, I as Infer, c as TgpuAccessor, d as TgpuDerived, e as TgpuSlot, f as TgpuBindGroupLayout, g as TgpuVertexLayout, h as WgslArray, D as Disarray, i as TgpuBuffer, j as AnyData, k as bindGroupLayout, v as vertexLayout, l as fn, m as fragmentFn, n as vertexFn, o as computeFn, p as privateVar, w as workgroupVar, q as constant, r as declare, s as sampler, t as comparisonSampler } from './tgpuComputeFn-
|
2
|
-
export { am as BindLayoutEntry, a8 as Eventual, an as ExtractBindGroupInputFromLayout, ao as LayoutEntryToInput, R as RandomNameRegistry, ag as Render, ah as Sampled, Z as Storage, _ as StorageFlag, S as StrictNameRegistry, af as TextureProps, a9 as TgpuAnyTextureView, ap as TgpuBindGroup, a5 as TgpuBufferMutable, a6 as TgpuBufferReadonly, a7 as TgpuBufferUniform, aD as TgpuComputeFn, aE as TgpuComputeFnShell, a0 as TgpuComputePipeline, ai as TgpuConst, aF as TgpuDeclare, ay as TgpuFnShell, aB as TgpuFragmentFn, aC as TgpuFragmentFnShell, aq as TgpuLayoutComparisonSampler, ar as TgpuLayoutEntry, as as TgpuLayoutExternalTexture, at as TgpuLayoutSampler, au as TgpuLayoutStorage, av as TgpuLayoutStorageTexture, aw as TgpuLayoutTexture, ax as TgpuLayoutUniform, aa as TgpuMutableTexture, ab as TgpuReadonlyTexture, $ as TgpuRenderPipeline, ac as TgpuSampledTexture, al as TgpuSampler, ad as TgpuTexture, aj as TgpuVar, az as TgpuVertexFn, aA as TgpuVertexFnShell, ae as TgpuWriteonlyTexture, a1 as Uniform, a2 as UniformFlag, ak as VariableScope, a3 as Vertex, a4 as VertexFlag, U as WithBinding, V as WithCompute, X as WithFragment, Y as WithVertex, u as isBuffer, B as isComparisonSampler, y as isDerived, E as isSampledTextureView, C as isSampler, z as isSlot, F as isStorageTextureView, G as isTexture, Q as isTgpuFn, H as isUsableAsRender, K as isUsableAsSampled, L as isUsableAsStorage, P as isUsableAsUniform, x as isUsableAsVertex, M as unstable_asMutable, N as unstable_asReadonly, O as unstable_asUniform } from './tgpuComputeFn-
|
1
|
+
import { W as Wgsl, J as JitTranspiler, T as TgpuRoot, A as AnyWgslData, a as TgpuFn, b as TgpuBufferUsage, I as Infer, c as TgpuAccessor, d as TgpuDerived, e as TgpuSlot, f as TgpuBindGroupLayout, g as TgpuVertexLayout, h as WgslArray, D as Disarray, i as TgpuBuffer, j as AnyData, k as bindGroupLayout, v as vertexLayout, l as fn, m as fragmentFn, n as vertexFn, o as computeFn, p as privateVar, w as workgroupVar, q as constant, r as declare, s as sampler, t as comparisonSampler } from './tgpuComputeFn-BxPDI5hQ.js';
|
2
|
+
export { am as BindLayoutEntry, a8 as Eventual, an as ExtractBindGroupInputFromLayout, ao as LayoutEntryToInput, R as RandomNameRegistry, ag as Render, ah as Sampled, Z as Storage, _ as StorageFlag, S as StrictNameRegistry, af as TextureProps, a9 as TgpuAnyTextureView, ap as TgpuBindGroup, a5 as TgpuBufferMutable, a6 as TgpuBufferReadonly, a7 as TgpuBufferUniform, aD as TgpuComputeFn, aE as TgpuComputeFnShell, a0 as TgpuComputePipeline, ai as TgpuConst, aF as TgpuDeclare, ay as TgpuFnShell, aB as TgpuFragmentFn, aC as TgpuFragmentFnShell, aq as TgpuLayoutComparisonSampler, ar as TgpuLayoutEntry, as as TgpuLayoutExternalTexture, at as TgpuLayoutSampler, au as TgpuLayoutStorage, av as TgpuLayoutStorageTexture, aw as TgpuLayoutTexture, ax as TgpuLayoutUniform, aa as TgpuMutableTexture, ab as TgpuReadonlyTexture, $ as TgpuRenderPipeline, ac as TgpuSampledTexture, al as TgpuSampler, ad as TgpuTexture, aj as TgpuVar, az as TgpuVertexFn, aA as TgpuVertexFnShell, ae as TgpuWriteonlyTexture, a1 as Uniform, a2 as UniformFlag, ak as VariableScope, a3 as Vertex, a4 as VertexFlag, U as WithBinding, V as WithCompute, X as WithFragment, Y as WithVertex, u as isBuffer, B as isComparisonSampler, y as isDerived, E as isSampledTextureView, C as isSampler, z as isSlot, F as isStorageTextureView, G as isTexture, Q as isTgpuFn, H as isUsableAsRender, K as isUsableAsSampled, L as isUsableAsStorage, P as isUsableAsUniform, x as isUsableAsVertex, M as unstable_asMutable, N as unstable_asReadonly, O as unstable_asUniform } from './tgpuComputeFn-BxPDI5hQ.js';
|
3
3
|
import 'tinyest';
|
4
4
|
|
5
5
|
interface TgpuResolveOptions {
|
@@ -65,7 +65,9 @@ declare function resolve(options: TgpuResolveOptions): string;
|
|
65
65
|
*/
|
66
66
|
type InitOptions = {
|
67
67
|
adapter?: GPURequestAdapterOptions | undefined;
|
68
|
-
device?: GPUDeviceDescriptor
|
68
|
+
device?: GPUDeviceDescriptor & {
|
69
|
+
optionalFeatures?: Iterable<GPUFeatureName>;
|
70
|
+
} | undefined;
|
69
71
|
/** @default 'random' */
|
70
72
|
unstable_names?: 'random' | 'strict' | undefined;
|
71
73
|
unstable_jitTranspiler?: JitTranspiler | undefined;
|