typegpu 0.5.5 → 0.5.6
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/{attributes-DsIdcdq4.d.ts → attributes-B90UjSYb.d.ts} +1 -1
- package/{attributes-BcDZsQCE.d.cts → attributes-zljQ-iA7.d.cts} +1 -1
- package/chunk-KJHEEZQT.js +4 -0
- package/chunk-KJHEEZQT.js.map +1 -0
- package/chunk-LB4TSIPN.cjs +2 -0
- package/chunk-LB4TSIPN.cjs.map +1 -0
- package/chunk-VTUUXD6H.cjs +4 -0
- package/{chunk-VUYQ2ZIK.cjs.map → chunk-VTUUXD6H.cjs.map} +1 -1
- package/chunk-YVK55BVR.js +2 -0
- package/chunk-YVK55BVR.js.map +1 -0
- package/data/index.cjs +1 -1
- package/data/index.cjs.map +1 -1
- package/data/index.d.cts +3 -3
- package/data/index.d.ts +3 -3
- package/data/index.js +1 -1
- package/{dataTypes-ts2Ccted.d.ts → dataTypes-6k4EJeol.d.cts} +65 -42
- package/{dataTypes-ts2Ccted.d.cts → dataTypes-6k4EJeol.d.ts} +65 -42
- package/index.cjs +17 -17
- package/index.cjs.map +1 -1
- package/index.d.cts +14 -14
- package/index.d.ts +14 -14
- package/index.js +17 -17
- package/index.js.map +1 -1
- package/package.json +3 -2
- package/{sampler-CPNwYXSH.d.ts → sampler-ByLNpSxj.d.ts} +1 -1
- package/{sampler-DeX4AnIZ.d.cts → sampler-NRTAYTIW.d.cts} +1 -1
- package/std/index.cjs +1 -1
- package/std/index.d.cts +2 -2
- package/std/index.d.ts +2 -2
- package/std/index.js +1 -1
- package/chunk-A6AAWPBU.cjs +0 -2
- package/chunk-A6AAWPBU.cjs.map +0 -1
- package/chunk-HZAXWB4J.js +0 -2
- package/chunk-HZAXWB4J.js.map +0 -1
- package/chunk-T5Y2EQPZ.js +0 -4
- package/chunk-T5Y2EQPZ.js.map +0 -1
- package/chunk-VUYQ2ZIK.cjs +0 -4
package/index.cjs.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["/Users/marcinhawryluk/Documents/wigsill/packages/typegpu/dist/index.cjs","../src/core/slot/slotTypes.ts","../src/types.ts","../src/smol/generationHelpers.ts","../src/core/resolve/resolveData.ts","../src/smol/wgslGenerator.ts","../src/data/compiledIO.ts","../src/data/dataIO.ts","../src/resolutionCtx.ts","../src/core/function/fnCore.ts"],"names":["isSlot","value","isDerived","isProviding","isAccessor","UnknownData","isSelfResolvable","isWgsl","isWgslData","isGPUBuffer","isBufferUsage","hasInternalDataType","$internal","isMarkedInternal","swizzleableTypes","swizzleLenToType","f32","vec2f","vec3f","vec4f","f16","vec2h","vec3h","vec4h","i32","vec2i","vec3i","vec4i","u32","vec2u","vec3u","vec4u","bool","vec2b","vec3b","vec4b","kindToSchema","mat2x2f","mat3x3f","mat4x4f","indexableTypeToResult","getTypeForPropAccess","targetType","propName","ctx","getResolutionCtx","unwrapped","getTypeFromWgsl","target","isDecorated","targetTypeStr","propLength","swizzleTypeChar","swizzleType","getTypeForIndexAccess","resource","isWgslArray","numericLiteralToSnippet","kind","abstractInt"],"mappings":"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","file":"/Users/marcinhawryluk/Documents/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import type { AnyWgslData } from '../../data/wgslTypes.ts';\nimport type { TgpuNamable } from '../../namable.ts';\nimport type { $repr, Infer } from '../../shared/repr.ts';\nimport type { Labelled } from '../../types.ts';\nimport type { TgpuFn } from '../function/tgpuFn.ts';\nimport type { TgpuBufferUsage } from './../buffer/bufferUsage.ts';\n\nexport interface TgpuSlot<T> extends TgpuNamable, Labelled {\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 Labelled {\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","import type { ArgNames, Block } from 'tinyest';\nimport type {\n TgpuBufferMutable,\n TgpuBufferReadonly,\n TgpuBufferUniform,\n TgpuBufferUsage,\n} from './core/buffer/bufferUsage.ts';\nimport type { TgpuConst } from './core/constant/tgpuConstant.ts';\nimport type { TgpuDeclare } from './core/declare/tgpuDeclare.ts';\nimport type { TgpuComputeFn } from './core/function/tgpuComputeFn.ts';\nimport type { TgpuFn } from './core/function/tgpuFn.ts';\nimport type { TgpuFragmentFn } from './core/function/tgpuFragmentFn.ts';\nimport type { TgpuVertexFn } from './core/function/tgpuVertexFn.ts';\nimport type { TgpuComputePipeline } from './core/pipeline/computePipeline.ts';\nimport type { TgpuRenderPipeline } from './core/pipeline/renderPipeline.ts';\nimport type { TgpuSampler } from './core/sampler/sampler.ts';\nimport {\n type Eventual,\n type SlotValuePair,\n type TgpuAccessor,\n type TgpuSlot,\n isDerived,\n isProviding,\n isSlot,\n} from './core/slot/slotTypes.ts';\nimport type { TgpuExternalTexture } from './core/texture/externalTexture.ts';\nimport type {\n TgpuAnyTextureView,\n TgpuTexture,\n} from './core/texture/texture.ts';\nimport type { TgpuVar } from './core/variable/tgpuVariable.ts';\nimport type { AnyData } from './data/dataTypes.ts';\nimport {\n type AnyMatInstance,\n type AnyVecInstance,\n type AnyWgslData,\n type BaseData,\n isWgslData,\n} from './data/wgslTypes.ts';\nimport type { NameRegistry } from './nameRegistry.ts';\nimport type { Infer } from './shared/repr.ts';\nimport { $internal } from './shared/symbols.ts';\nimport type {\n TgpuBindGroupLayout,\n TgpuLayoutEntry,\n} from './tgpuBindGroupLayout.ts';\n\nexport type ResolvableObject =\n | SelfResolvable\n | TgpuBufferUsage\n | TgpuConst\n | TgpuDeclare\n | TgpuFn\n | TgpuComputeFn\n | TgpuFragmentFn\n | TgpuComputePipeline\n | TgpuRenderPipeline\n | TgpuVertexFn\n | TgpuSampler\n | TgpuAccessor\n | TgpuExternalTexture\n | TgpuTexture\n | TgpuAnyTextureView\n | TgpuVar\n | AnyVecInstance\n | AnyMatInstance\n | AnyData\n // biome-ignore lint/suspicious/noExplicitAny: <has to be more permissive than unknown>\n | TgpuFn<any, any>;\n\nexport type Wgsl = Eventual<string | number | boolean | ResolvableObject>;\n\nexport const UnknownData = {\n type: 'unknown' as const,\n};\nexport type UnknownData = typeof UnknownData;\n\nexport type Snippet = {\n value: unknown;\n dataType: AnyData | UnknownData;\n};\n\nexport type TgpuShaderStage = 'compute' | 'vertex' | 'fragment';\n\nexport interface FnToWgslOptions {\n args: Snippet[];\n returnType: AnyWgslData;\n body: Block;\n externalMap: Record<string, unknown>;\n}\n\nexport type ItemLayer = {\n type: 'item';\n usedSlots: Set<TgpuSlot<unknown>>;\n};\n\nexport interface ItemStateStack {\n readonly itemDepth: number;\n readonly topItem: ItemLayer;\n\n pushItem(): void;\n popItem(): void;\n pushSlotBindings(pairs: SlotValuePair<unknown>[]): void;\n popSlotBindings(): void;\n pushFunctionScope(\n args: Snippet[],\n returnType: AnyWgslData | undefined,\n externalMap: Record<string, unknown>,\n ): void;\n popFunctionScope(): void;\n pushBlockScope(): void;\n popBlockScope(): void;\n pop(type?: 'functionScope' | 'blockScope' | 'slotBinding' | 'item'): void;\n readSlot<T>(slot: TgpuSlot<T>): T | undefined;\n getSnippetById(id: string): Snippet | undefined;\n defineBlockVariable(id: string, type: AnyWgslData | UnknownData): Snippet;\n}\n\n/**\n * Passed into each resolvable item. All items in a tree share a resolution ctx,\n * but there can be layers added and removed from the item stack when going down\n * and up the tree.\n */\nexport interface ResolutionCtx {\n readonly names: NameRegistry;\n\n addDeclaration(declaration: string): void;\n\n /**\n * Reserves a bind group number, and returns a placeholder that will be replaced\n * with a concrete number at the end of the resolution process.\n */\n allocateLayoutEntry(layout: TgpuBindGroupLayout): string;\n\n /**\n * Reserves a spot in the catch-all bind group, without the indirection of a bind-group.\n * This means the resource is 'fixed', and cannot be swapped between code execution.\n */\n allocateFixedEntry(\n layoutEntry: TgpuLayoutEntry,\n resource: object,\n ): {\n group: string;\n binding: number;\n };\n\n withSlots<T>(pairs: SlotValuePair<unknown>[], callback: () => T): T;\n\n /**\n * Unwraps all layers of slot/derived indirection and returns the concrete value if available.\n * @throws {MissingSlotValueError}\n */\n unwrap<T>(eventual: Eventual<T>): T;\n\n resolve(item: unknown): string;\n resolveValue<T extends BaseData>(value: Infer<T>, schema: T): string;\n\n transpileFn(fn: string): {\n argNames: ArgNames;\n body: Block;\n externalNames: string[];\n };\n fnToWgsl(options: FnToWgslOptions): {\n head: Wgsl;\n body: Wgsl;\n };\n\n [$internal]: {\n itemStateStack: ItemStateStack;\n };\n}\n\n/**\n * Houses a method '~resolve` that returns a code string\n * representing it, as opposed to offloading the resolution\n * to another mechanism.\n */\nexport interface SelfResolvable {\n '~resolve'(ctx: ResolutionCtx): string;\n toString(): string;\n}\n\nexport interface Labelled {\n readonly label?: string | undefined;\n}\n\nexport function isSelfResolvable(value: unknown): value is SelfResolvable {\n return typeof (value as SelfResolvable)?.['~resolve'] === 'function';\n}\n\nexport function isWgsl(value: unknown): value is Wgsl {\n return (\n typeof value === 'number' ||\n typeof value === 'boolean' ||\n typeof value === 'string' ||\n isSelfResolvable(value) ||\n isWgslData(value) ||\n isSlot(value) ||\n isDerived(value) ||\n isProviding(value)\n );\n}\n\nexport type BindableBufferUsage = 'uniform' | 'readonly' | 'mutable';\nexport type BufferUsage = 'uniform' | 'readonly' | 'mutable' | 'vertex';\n\nexport function isGPUBuffer(value: unknown): value is GPUBuffer {\n return (\n !!value &&\n typeof value === 'object' &&\n 'getMappedRange' in value &&\n 'mapAsync' in value\n );\n}\n\nexport function isBufferUsage<\n T extends\n | TgpuBufferUniform<BaseData>\n | TgpuBufferReadonly<BaseData>\n | TgpuBufferMutable<BaseData>,\n>(value: T | unknown): value is T {\n return (value as T)?.resourceType === 'buffer-usage';\n}\n\nexport function hasInternalDataType(\n value: unknown,\n): value is { [$internal]: { dataType: BaseData } } {\n return (\n !!value &&\n typeof value === 'object' &&\n !!(value as { [$internal]: { dataType: BaseData } })?.[$internal]?.dataType\n );\n}\n\nexport function isMarkedInternal(\n value: unknown,\n): value is { [$internal]: Record<string, unknown> } {\n return !!(value as { [$internal]: Record<string, unknown> })?.[$internal];\n}\n","import { isDerived, isSlot } from '../core/slot/slotTypes.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} from '../data/vector.ts';\nimport {\n type WgslStruct,\n isDecorated,\n isWgslArray,\n isWgslData,\n} from '../data/wgslTypes.ts';\nimport { getResolutionCtx } from '../gpuMode.ts';\nimport { $internal } from '../shared/symbols.ts';\nimport {\n type Snippet,\n UnknownData,\n type Wgsl,\n hasInternalDataType,\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 =\n 'kind' in target ? (target.kind as string) : 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 (isDerived(resource) || isSlot(resource)) {\n return getTypeFromWgsl(resource.value as Wgsl);\n }\n\n if (typeof resource === 'string') {\n return UnknownData;\n }\n if (typeof resource === 'number') {\n return 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 return isWgslData(resource) ? resource : 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","import { getAttributesString } from '../../data/attributes.ts';\nimport {\n type AnyData,\n type Disarray,\n type Unstruct,\n isLooseData,\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 { 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}: ${ctx.resolve(property as AnyWgslData)},\\n`;\n}\n\n/**\n * Resolves a struct and adds its declaration to the resolution context.\n * @param ctx - The resolution context.\n * @param struct - The struct to resolve.\n *\n * @returns The resolved struct name.\n */\nfunction resolveStruct(ctx: ResolutionCtx, struct: WgslStruct) {\n const id = ctx.names.makeUnique(struct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(struct.propTypes)\n .map((prop) => resolveStructProperty(ctx, prop))\n .join('')}\\\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an unstruct (struct that does not align data by default) to its struct data counterpart.\n * @param ctx - The resolution context.\n * @param unstruct - The unstruct to resolve.\n *\n * @returns The resolved unstruct name.\n *\n * @example\n * ```ts\n * resolveUnstruct(ctx, {\n * uv: d.float16x2, // -> d.vec2f after resolution\n * color: d.snorm8x4, -> d.vec4f after resolution\n * });\n * ```\n */\nfunction resolveUnstruct(ctx: ResolutionCtx, unstruct: Unstruct) {\n const id = ctx.names.makeUnique(unstruct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(unstruct.propTypes)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [prop[0], formatToWGSLType[prop[1].format]])\n : resolveStructProperty(ctx, prop),\n )\n .join('')}\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an array.\n * @param ctx - The resolution context.\n * @param array - The array to resolve.\n *\n * @returns The resolved array name along with its element type and count (if not runtime-sized).\n *\n * @example\n * ```ts\n * resolveArray(ctx, d.arrayOf(d.u32, 0)); // 'array<u32>' (not a real pattern, a function is preferred)\n * resolveArray(ctx, d.arrayOf(d.u32, 5)); // 'array<u32, 5>'\n * ```\n */\nfunction resolveArray(ctx: ResolutionCtx, array: WgslArray) {\n const element = ctx.resolve(array.elementType as AnyWgslData);\n\n return array.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${array.elementCount}>`;\n}\n\nfunction resolveDisarray(ctx: ResolutionCtx, disarray: Disarray) {\n const element = ctx.resolve(\n isAttribute(disarray.elementType)\n ? formatToWGSLType[disarray.elementType.format]\n : (disarray.elementType as AnyWgslData),\n );\n\n return disarray.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${disarray.elementCount}>`;\n}\n\n/**\n * Resolves a WGSL data-type schema to a string.\n * @param ctx - The resolution context.\n * @param data - The data-type to resolve.\n *\n * @returns The resolved data-type string.\n */\nexport function resolveData(ctx: ResolutionCtx, data: AnyData): string {\n if (isLooseData(data)) {\n if (data.type === 'unstruct') {\n return resolveUnstruct(ctx, data);\n }\n\n if (data.type === 'disarray') {\n return resolveDisarray(ctx, data);\n }\n\n if (data.type === 'loose-decorated') {\n return ctx.resolve(\n isAttribute(data.inner)\n ? formatToWGSLType[data.inner.format]\n : data.inner,\n );\n }\n\n return ctx.resolve(formatToWGSLType[data.type]);\n }\n\n if (isIdentityType(data)) {\n return data.type;\n }\n\n if (data.type === 'struct') {\n return resolveStruct(ctx, data);\n }\n\n if (data.type === 'array') {\n return resolveArray(ctx, data);\n }\n\n if (data.type === 'atomic') {\n return `atomic<${resolveData(ctx, data.inner)}>`;\n }\n\n if (data.type === 'decorated') {\n return ctx.resolve(data.inner as AnyWgslData);\n }\n\n if (data.type === 'ptr') {\n if (data.addressSpace === 'storage') {\n return `ptr<storage, ${ctx.resolve(data.inner)}, ${data.access === 'read-write' ? 'read_write' : data.access}>`;\n }\n return `ptr<${data.addressSpace}, ${ctx.resolve(data.inner)}>`;\n }\n\n if (data.type === 'abstractInt' || data.type === 'abstractFloat') {\n throw new Error('Abstract types have no concrete representation in WGSL');\n }\n\n if (data.type === 'void') {\n throw new Error('Void has no representation in WGSL');\n }\n\n assertExhaustive(data, 'resolveData');\n}\n","import type * as smol from 'tinyest';\nimport type { AnyData } 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 {\n type ResolutionCtx,\n type Snippet,\n UnknownData,\n isMarkedInternal,\n isWgsl,\n} from '../types.ts';\nimport {\n getTypeForIndexAccess,\n getTypeForPropAccess,\n getTypeFromWgsl,\n numericLiteralToSnippet,\n} from './generationHelpers.ts';\n\nconst parenthesizedOps = [\n '==',\n '!=',\n '<',\n '<=',\n '>',\n '>=',\n '<<',\n '>>',\n '+',\n '-',\n '*',\n '/',\n '%',\n '|',\n '^',\n '&',\n '&&',\n '||',\n];\n\nconst binaryLogicalOps = ['&&', '||', '==', '!=', '<', '<=', '>', '>='];\n\ntype Operator =\n | smol.BinaryOperator\n | smol.AssignmentOperator\n | smol.LogicalOperator\n | smol.UnaryOperator;\n\nfunction operatorToType<\n TL extends 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\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: wgsl.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 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(ctx: GenerationCtx, value: smol.Block): string {\n ctx.pushBlockScope();\n try {\n return `${ctx.indent()}{\n${value.b.map((statement) => generateStatement(ctx, statement)).join('\\n')}\n${ctx.dedent()}}`;\n } finally {\n ctx.popBlockScope();\n }\n}\n\nexport function registerBlockVariable(\n ctx: GenerationCtx,\n id: string,\n dataType: wgsl.AnyWgslData | UnknownData,\n): 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: smol.Expression,\n): Snippet {\n if (typeof expression === 'string') {\n return generateIdentifier(ctx, expression);\n }\n if (typeof expression === 'boolean') {\n return generateBoolean(ctx, expression);\n }\n\n if ('x' in expression) {\n // Logical/Binary/Assignment Expression\n const [lhs, op, rhs] = expression.x;\n const lhsExpr = generateExpression(ctx, lhs);\n const rhsExpr = generateExpression(ctx, rhs);\n\n const lhsStr = resolveRes(ctx, lhsExpr);\n const rhsStr = resolveRes(ctx, rhsExpr);\n const type = operatorToType(lhsExpr.dataType, op, rhsExpr.dataType);\n\n return {\n value: parenthesizedOps.includes(op)\n ? `(${lhsStr} ${op} ${rhsStr})`\n : `${lhsStr} ${op} ${rhsStr}`,\n dataType: type,\n };\n }\n\n if ('p' in expression) {\n // Update Expression\n const [op, arg] = expression.p;\n const argExpr = generateExpression(ctx, arg);\n const argStr = resolveRes(ctx, argExpr);\n\n return {\n value: `${argStr}${op}`,\n dataType: argExpr.dataType,\n };\n }\n\n if ('u' in expression) {\n // Unary Expression\n const [op, arg] = expression.u;\n const argExpr = generateExpression(ctx, arg);\n const argStr = resolveRes(ctx, argExpr);\n\n const type = operatorToType(argExpr.dataType, op);\n return {\n value: `${op}${argStr}`,\n dataType: type,\n };\n }\n\n if ('a' in expression) {\n // Member Access\n const [targetId, property] = expression.a;\n const target = generateExpression(ctx, targetId);\n\n if (typeof target.value === 'string') {\n return {\n value: `${target.value}.${property}`,\n dataType: d.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 ('i' in expression) {\n // Index Access\n const [target, property] = expression.i;\n const targetExpr = generateExpression(ctx, target);\n const propertyExpr = generateExpression(ctx, property);\n const targetStr = resolveRes(ctx, targetExpr);\n const propertyStr = resolveRes(ctx, propertyExpr);\n\n return {\n value: `${targetStr}[${propertyStr}]`,\n dataType: d.isData(targetExpr.dataType)\n ? getTypeForIndexAccess(targetExpr.dataType)\n : UnknownData,\n };\n }\n\n if ('n' in expression) {\n // Numeric Literal\n const type = numericLiteralToSnippet(expression.n);\n if (!type) {\n throw new Error(`Invalid numeric literal ${expression.n}`);\n }\n return type;\n }\n\n if ('f' in expression) {\n // Function Call\n const [callee, args] = expression.f;\n const id = generateExpression(ctx, callee);\n const idValue = id.value;\n\n ctx.callStack.push(idValue);\n\n const 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 ${String(idValue)} has not been created using TypeGPU APIs. Did you mean to wrap the function with tgpu.fn(args, return)(...) ?`,\n );\n }\n\n // Assuming that `id` is callable\n const fnRes = (idValue as unknown as (...args: unknown[]) => unknown)(\n ...resolvedSnippets,\n ) as Snippet;\n\n return {\n value: resolveRes(ctx, fnRes),\n dataType: fnRes.dataType,\n };\n }\n\n if ('o' in expression) {\n // Object Literal\n const obj = expression.o;\n const callee = ctx.callStack[ctx.callStack.length - 1];\n\n const generateEntries = (values: smol.Expression[]) =>\n values\n .map((value) => {\n const valueRes = generateExpression(ctx, value);\n return resolveRes(ctx, valueRes);\n })\n .join(', ');\n\n if (wgsl.isWgslStruct(callee)) {\n const propKeys = Object.keys(callee.propTypes);\n const values = propKeys.map((key) => {\n const val = obj[key];\n if (val === undefined) {\n throw new Error(\n `Missing property ${key} in object literal for struct ${callee}`,\n );\n }\n return val;\n });\n\n return {\n value: generateEntries(values),\n dataType: callee,\n };\n }\n\n return {\n value: generateEntries(Object.values(obj)),\n dataType: UnknownData,\n };\n }\n\n if ('y' in expression) {\n // Array Expression\n const values = expression.y.map((value) => {\n return generateExpression(ctx, value);\n });\n if (values.length === 0) {\n throw new Error('Cannot create empty array literal.');\n }\n\n let type = values[0]?.dataType;\n const mismatchedType = values.find((value) => value.dataType !== type);\n if (mismatchedType) {\n throw new Error(\n `Cannot mix types in array literal. Type ${mismatchedType.dataType.type} does not match expected type ${type?.type}.`,\n );\n }\n\n if (!wgsl.isWgslData(type)) {\n throw new Error('Cannot use non-WGSL data types in array literals.');\n }\n\n type =\n type.type === 'abstractInt'\n ? d.u32\n : type.type === 'abstractFloat'\n ? d.f32\n : type;\n\n const typeId = ctx.resolve(type);\n\n const arrayType = `array<${typeId}, ${values.length}>`;\n const arrayValues = values.map((value) => resolveRes(ctx, value));\n\n return {\n value: `${arrayType}( ${arrayValues.join(', ')} )`,\n dataType: d.arrayOf(type, values.length) as d.AnyWgslData,\n };\n }\n\n if ('s' in expression) {\n throw new Error('Cannot use string literals in TGSL.');\n }\n\n assertExhaustive(expression);\n}\n\nexport function generateStatement(\n ctx: GenerationCtx,\n statement: smol.Statement,\n): string {\n if (typeof statement === 'string') {\n return `${ctx.pre}${resolveRes(ctx, generateIdentifier(ctx, statement))};`;\n }\n\n if (typeof statement === 'boolean') {\n return `${ctx.pre}${resolveRes(ctx, generateBoolean(ctx, statement))};`;\n }\n\n if ('r' in statement) {\n // check if the thing at the top of the call stack is a struct and the statement is a plain JS object\n // if so wrap the value returned in a constructor of the struct (its resolved name)\n if (\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1]) &&\n statement.r !== null &&\n typeof statement.r === 'object' &&\n 'o' in statement.r\n ) {\n const resource = resolveRes(ctx, generateExpression(ctx, statement.r));\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}return ${resolvedStruct}(${resource});`;\n }\n\n return statement.r === null\n ? `${ctx.pre}return;`\n : `${ctx.pre}return ${resolveRes(\n ctx,\n generateExpression(ctx, statement.r),\n )};`;\n }\n\n if ('q' in statement) {\n const [cond, cons, alt] = statement.q;\n const condition = resolveRes(ctx, generateExpression(ctx, cond));\n\n ctx.indent(); // {\n const consequent = generateStatement(ctx, cons);\n ctx.dedent(); // }\n\n ctx.indent(); // {\n const alternate = alt ? generateStatement(ctx, alt) : undefined;\n ctx.dedent(); // }\n\n if (!alternate) {\n return `\\\n${ctx.pre}if (${condition})\n${consequent}`;\n }\n\n return `\\\n${ctx.pre}if (${condition})\n${consequent}\n${ctx.pre}else\n${alternate}`;\n }\n\n if ('l' in statement || 'c' in statement) {\n const [rawId, rawValue] = 'l' in statement ? statement.l : statement.c;\n const eq = rawValue ? generateExpression(ctx, rawValue) : undefined;\n\n if (!eq || !rawValue) {\n throw new Error('Cannot create variable without an initial value.');\n }\n\n if (d.isLooseData(eq.dataType)) {\n throw new Error('Cannot create variable with loose data type.');\n }\n\n registerBlockVariable(ctx, rawId, eq.dataType);\n const id = resolveRes(ctx, generateIdentifier(ctx, rawId));\n\n // If the value is a plain JS object it has to be an output struct\n if (\n typeof rawValue === 'object' &&\n 'o' in rawValue &&\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1])\n ) {\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}var ${id} = ${resolvedStruct}(${resolveRes(ctx, eq)});`;\n }\n\n return `${ctx.pre}var ${id} = ${resolveRes(ctx, eq)};`;\n }\n\n if ('b' in statement) {\n ctx.pushBlockScope();\n try {\n return generateBlock(ctx, statement);\n } finally {\n ctx.popBlockScope();\n }\n }\n\n // 'j' stands for for (trust me)\n if ('j' in statement) {\n const [init, condition, update, body] = statement.j;\n\n const initStatement = init ? generateStatement(ctx, init) : undefined;\n const initStr = initStatement ? initStatement.slice(0, -1) : '';\n\n const conditionExpr = condition\n ? generateExpression(ctx, condition)\n : undefined;\n const conditionStr = conditionExpr ? resolveRes(ctx, conditionExpr) : '';\n\n const updateStatement = update ? generateStatement(ctx, update) : undefined;\n const updateStr = updateStatement ? updateStatement.slice(0, -1) : '';\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}for (${initStr}; ${conditionStr}; ${updateStr})\n${bodyStr}`;\n }\n\n if ('w' in statement) {\n const [condition, body] = statement.w;\n const conditionStr = resolveRes(ctx, generateExpression(ctx, condition));\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}while (${conditionStr})\n${bodyStr}`;\n }\n\n if ('k' in statement) {\n return `${ctx.pre}continue;`;\n }\n\n if ('d' in statement) {\n return `${ctx.pre}break;`;\n }\n\n return `${ctx.pre}${resolveRes(ctx, generateExpression(ctx, statement))};`;\n}\n\nexport function generateFunction(ctx: GenerationCtx, body: smol.Block): string {\n return generateBlock(ctx, body);\n}\n","import { roundUp } from '../mathUtils.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}.${components[i]}, littleEndian);\\n`;\n }\n return code;\n }\n\n if (wgsl.isMat(node)) {\n const primitive = typeToPrimitive[node.type];\n const writeFunc = primitiveToWriteFunction[primitive];\n\n const matSize = wgsl.isMat2x2f(node) ? 2 : wgsl.isMat3x3f(node) ? 3 : 4;\n const elementCount = matSize * matSize;\n const rowStride = roundUp(matSize * 4, 8);\n\n let code = '';\n for (let i = 0; i < elementCount; i++) {\n const colIndex = Math.floor(i / matSize);\n const rowIndex = i % matSize;\n const byteOffset = colIndex * rowStride + rowIndex * 4;\n\n code += `output.${writeFunc}((${offsetExpr} + ${byteOffset}), ${valueExpr}.columns[${colIndex}].${['x', 'y', 'z', 'w'][rowIndex]}, littleEndian);\\n`;\n }\n\n return code;\n }\n\n const primitive = typeToPrimitive[node.type as keyof typeof typeToPrimitive];\n return `output.${primitiveToWriteFunction[primitive]}(${offsetExpr}, ${valueExpr}, littleEndian);\\n`;\n}\n\nexport function getCompiledWriterForSchema<T extends wgsl.BaseData>(\n schema: T,\n): (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n) => void {\n if (compiledWriters.has(schema)) {\n return compiledWriters.get(schema) as (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n ) => void;\n }\n\n const body = buildWriter(schema, 'offset', 'value');\n\n const fn = new Function(\n 'output',\n 'offset',\n 'value',\n 'littleEndian=true',\n body,\n ) as (\n output: DataView,\n offset: number,\n value: Infer<T> | unknown,\n littleEndian?: boolean,\n ) => void;\n\n compiledWriters.set(schema, fn);\n\n return fn;\n}\n","import type { ISerialInput, ISerialOutput } from 'typed-binary';\nimport type { Infer, InferRecord } from '../shared/repr.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 type { ArgNames, Block } from 'tinyest';\nimport { resolveData } from './core/resolve/resolveData.ts';\nimport {\n type Eventual,\n type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n isDerived,\n isProviding,\n isSlot,\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 { RuntimeMode, popMode, provideCtx, pushMode } 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 { getTypeFromWgsl } from './smol/generationHelpers.ts';\nimport { generateFunction } from './smol/wgslGenerator.ts';\nimport {\n type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n bindGroupLayout,\n} from './tgpuBindGroupLayout.ts';\nimport type {\n FnToWgslOptions,\n ItemLayer,\n ItemStateStack,\n ResolutionCtx,\n Snippet,\n Wgsl,\n} from './types.ts';\nimport { UnknownData, 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 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) {\n return {\n value: external,\n dataType: isWgsl(external)\n ? getTypeFromWgsl(external)\n : UnknownData,\n };\n }\n\n // Since functions cannot access resources from the calling scope, we\n // return early here.\n return undefined;\n }\n\n if (layer?.type === 'blockScope') {\n const declarationType = layer.declarations.get(id);\n if (declarationType !== undefined) {\n return { value: id, dataType: declarationType };\n }\n } else {\n // Skip\n }\n }\n\n return undefined;\n }\n\n defineBlockVariable(id: string, type: AnyWgslData | UnknownData): 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._nextFreeLayoutPlaceholderIdx++}#`;\n memoMap.set(layout, placeholderKey);\n }\n\n return placeholderKey;\n }\n\n allocateFixedEntry(\n layoutEntry: TgpuLayoutEntry,\n resource: object,\n ): { group: string; binding: number } {\n const binding = this.fixedBindings.length;\n this.fixedBindings.push({ layoutEntry, resource });\n\n return {\n group: CATCHALL_BIND_GROUP_IDX_MARKER,\n binding,\n };\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T {\n const value = this._itemStateStack.readSlot(slot);\n\n if (value === undefined) {\n throw new MissingSlotValueError(slot);\n }\n\n return value;\n }\n\n withSlots<T>(pairs: SlotValuePair<unknown>[], callback: () => T): T {\n this._itemStateStack.pushSlotBindings(pairs);\n\n try {\n return callback();\n } finally {\n this._itemStateStack.popSlotBindings();\n }\n }\n\n unwrap<T>(eventual: Eventual<T>): T {\n if (isProviding(eventual)) {\n return this.withSlots(\n eventual['~providing'].pairs,\n () => this.unwrap(eventual['~providing'].inner) as T,\n );\n }\n\n let maybeEventual = eventual;\n\n // Unwrapping all layers of slots.\n while (true) {\n if (isSlot(maybeEventual)) {\n maybeEventual = this.readSlot(maybeEventual);\n } else if (isDerived(maybeEventual)) {\n maybeEventual = this._getOrCompute(maybeEventual);\n } else {\n break;\n }\n }\n\n return maybeEventual;\n }\n\n _getOrCompute<T>(derived: TgpuDerived<T>): T {\n // All memoized versions of `derived`\n const instances = this._memoizedDerived.get(derived) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue),\n )\n ) {\n return instance.result as T;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n // Derived computations are always done on the CPU\n pushMode(RuntimeMode.CPU);\n\n let result: T;\n try {\n result = derived['~compute']();\n } finally {\n popMode(RuntimeMode.CPU);\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedDerived.set(derived, instances);\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(derived);\n }\n\n throw new ResolutionError(err, [derived]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n /**\n * @param item The item whose resolution should be either retrieved from the cache (if there is a cache hit), or resolved.\n */\n _getOrInstantiate(item: object): string {\n // All memoized versions of `item`\n const instances = this._memoizedResolves.get(item) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue),\n )\n ) {\n return instance.result;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n let result: string;\n if (isData(item)) {\n result = resolveData(this, item);\n } else if (isDerived(item) || isSlot(item)) {\n result = this.resolve(this.unwrap(item));\n } else if (isSelfResolvable(item)) {\n result = item['~resolve'](this);\n } else {\n result = this.resolveValue(item);\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedResolves.set(item, instances);\n\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(item);\n }\n\n throw new ResolutionError(err, [item]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n resolve(item: unknown): string {\n if (isProviding(item)) {\n return this.withSlots(item['~providing'].pairs, () =>\n this.resolve(item['~providing'].inner),\n );\n }\n\n if ((item && typeof item === 'object') || typeof item === 'function') {\n if (this._itemStateStack.itemDepth === 0) {\n try {\n pushMode(RuntimeMode.GPU);\n const result = provideCtx(this, () => this._getOrInstantiate(item));\n return `${[...this._declarations].join('\\n\\n')}${result}`;\n } finally {\n popMode(RuntimeMode.GPU);\n }\n }\n\n return this._getOrInstantiate(item);\n }\n\n return String(item);\n }\n\n resolveValue<T extends BaseData>(\n value: Infer<T>,\n schema?: T | undefined,\n ): string {\n if (isWgsl(value)) {\n return this.resolve(value);\n }\n\n if (schema && isWgslArray(schema)) {\n return `array(${(value as unknown[]).map((element) => this.resolveValue(element, schema.elementType))})`;\n }\n\n if (Array.isArray(value)) {\n return `array(${value.map((element) => this.resolveValue(element))})`;\n }\n\n if (schema && isWgslStruct(schema)) {\n return `${this.resolve(schema)}(${Object.entries(schema.propTypes).map(([key, type_]) => this.resolveValue((value as Infer<typeof schema>)[key], type_))})`;\n }\n\n throw new Error(\n `Value ${value} (as json: ${JSON.stringify(value)}) of schema ${schema} is not resolvable to WGSL`,\n );\n }\n}\n\nexport interface ResolutionResult {\n code: string;\n bindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | null;\n}\n\nexport function resolve(\n item: Wgsl,\n options: ResolutionCtxImplOptions,\n): ResolutionResult {\n const ctx = new ResolutionCtxImpl(options);\n let code = ctx.resolve(item);\n\n const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;\n const bindGroupLayouts: TgpuBindGroupLayout[] = [];\n const takenIndices = new Set<number>(\n [...memoMap.keys()]\n .map((layout) => layout.index)\n .filter((v): v is number => v !== undefined),\n );\n\n const automaticIds = naturalsExcept(takenIndices);\n\n const layoutEntries = ctx.fixedBindings.map(\n (binding, idx) =>\n [String(idx), binding.layoutEntry] as [string, TgpuLayoutEntry],\n );\n\n const createCatchallGroup = () => {\n const catchallIdx = automaticIds.next().value;\n const catchallLayout = bindGroupLayout(Object.fromEntries(layoutEntries));\n bindGroupLayouts[catchallIdx] = catchallLayout;\n code = code.replaceAll(CATCHALL_BIND_GROUP_IDX_MARKER, String(catchallIdx));\n\n return [\n catchallIdx,\n new TgpuBindGroupImpl(\n catchallLayout,\n Object.fromEntries(\n ctx.fixedBindings.map(\n (binding, idx) =>\n // biome-ignore lint/suspicious/noExplicitAny: <it's fine>\n [String(idx), binding.resource] as [string, any],\n ),\n ),\n ),\n ] as [number, TgpuBindGroup];\n };\n\n // Retrieving the catch-all binding index first, because it's inherently\n // the least swapped bind group (fixed and cannot be swapped).\n const catchall = layoutEntries.length > 0 ? createCatchallGroup() : null;\n\n for (const [layout, placeholder] of memoMap.entries()) {\n const idx = layout.index ?? automaticIds.next().value;\n bindGroupLayouts[idx] = layout;\n code = code.replaceAll(placeholder, String(idx));\n }\n\n return {\n code,\n bindGroupLayouts,\n catchall,\n };\n}\n\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)} ${ctx.resolve(returnType)}`\n : `(${argList})`;\n}\n","import { getAttributesString } from '../../data/attributes.ts';\nimport {\n type AnyWgslData,\n isWgslData,\n isWgslStruct,\n} from '../../data/wgslTypes.ts';\nimport { MissingLinksError } from '../../errors.ts';\nimport { resolveFunctionHeader } from '../../resolutionCtx.ts';\nimport type { ResolutionCtx, Snippet } from '../../types.ts';\nimport {\n type ExternalMap,\n addArgTypesToExternals,\n addReturnTypeToExternals,\n applyExternals,\n replaceExternalsInWgsl,\n} from '../resolve/externals.ts';\nimport { getPrebuiltAstFor } from './astUtils.ts';\nimport type { Implementation } from './fnTypes.ts';\n\nexport interface TgpuFnShellBase<\n Args extends unknown[] | Record<string, unknown>,\n Return,\n> {\n readonly argTypes: Args;\n readonly returnType: Return | undefined;\n readonly isEntry: boolean;\n}\n\nexport interface FnCore {\n label: string | undefined;\n applyExternals(newExternals: ExternalMap): void;\n resolve(ctx: ResolutionCtx, fnAttribute?: string): string;\n}\n\nexport function createFnCore(\n shell: TgpuFnShellBase<unknown[] | Record<string, unknown>, unknown>,\n implementation: Implementation<unknown[], unknown>,\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 if (Array.isArray(shell.argTypes)) {\n // TODO: Remove this branch along with deprecated array arg types\n\n addArgTypesToExternals(\n implementation,\n Array.isArray(shell.argTypes)\n ? shell.argTypes\n : Object.values(shell.argTypes),\n (externals) => externalsToApply.push(externals),\n );\n addReturnTypeToExternals(\n implementation,\n shell.returnType,\n (externals) => externalsToApply.push(externals),\n );\n }\n } else {\n if (Array.isArray(shell.argTypes) && 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 return {\n label: undefined as string | undefined,\n\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(this.label);\n\n if (typeof implementation === 'string') {\n let header = '';\n\n if (!shell.isEntry) {\n header = Array.isArray(shell.argTypes)\n ? ''\n : resolveFunctionHeader(\n ctx,\n Object.entries(shell.argTypes).map(([value, dataType]) => ({\n value,\n dataType: dataType as AnyWgslData,\n })),\n shell.returnType as AnyWgslData,\n );\n } else {\n const input =\n Array.isArray(shell.argTypes) && isWgslStruct(shell.argTypes[0])\n ? '(in: In)'\n : '()';\n\n const attributes = isWgslData(shell.returnType)\n ? getAttributesString(shell.returnType)\n : '';\n const output =\n shell.returnType !== undefined\n ? isWgslStruct(shell.returnType)\n ? '-> Out'\n : `-> ${attributes !== '' ? attributes : '@location(0)'} ${ctx.resolve(shell.returnType)}`\n : '';\n header = `${input} ${output} `;\n }\n\n const replacedImpl = replaceExternalsInWgsl(\n ctx,\n externalMap,\n `${header}${implementation.trim()}`,\n );\n\n ctx.addDeclaration(`${fnAttribute}fn ${id}${replacedImpl}`);\n } else {\n // get data generated by the plugin\n const pluginData = getPrebuiltAstFor(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 if (ast.argNames.type === 'destructured-object') {\n applyExternals(\n externalMap,\n Object.fromEntries(\n ast.argNames.props.map(({ prop, alias }) => [alias, prop]),\n ),\n );\n }\n\n if (\n !Array.isArray(shell.argTypes) &&\n ast.argNames.type === 'identifiers' &&\n ast.argNames.names[0] !== undefined\n ) {\n applyExternals(externalMap, {\n [ast.argNames.names[0]]: Object.fromEntries(\n Object.keys(shell.argTypes).map((arg) => [arg, arg]),\n ),\n });\n }\n\n // Verifying all required externals are present.\n const missingExternals = ast.externalNames.filter(\n (name) => !(name in externalMap),\n );\n\n if (missingExternals.length > 0) {\n throw new MissingLinksError(this.label, missingExternals);\n }\n\n const args: Snippet[] = Array.isArray(shell.argTypes)\n ? ast.argNames.type === 'identifiers'\n ? shell.argTypes.map((arg, i) => ({\n value:\n (ast.argNames.type === 'identifiers'\n ? ast.argNames.names[i]\n : undefined) ?? `arg_${i}`,\n dataType: arg as AnyWgslData,\n }))\n : []\n : Object.entries(shell.argTypes).map(([name, dataType]) => ({\n value: name,\n dataType: dataType as AnyWgslData,\n }));\n\n const { head, body } = ctx.fnToWgsl({\n args,\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"]}
|
1
|
+
{"version":3,"sources":["/Users/marcinhawryluk/Documents/wigsill/packages/typegpu/dist/index.cjs","../src/core/slot/slotTypes.ts","../src/types.ts","../src/tgsl/generationHelpers.ts","../src/core/resolve/resolveData.ts","../src/data/compiledIO.ts","../src/data/dataIO.ts","../src/tgsl/wgslGenerator.ts","../src/resolutionCtx.ts","../src/core/function/fnCore.ts"],"names":["isSlot","value","isDerived","isProviding","isAccessor","UnknownData","isSelfResolvable","isWgsl","isWgslData","isGPUBuffer","isBufferUsage","hasInternalDataType","$internal","isMarkedInternal","swizzleableTypes","swizzleLenToType","f32","vec2f","vec3f","vec4f","f16","vec2h","vec3h","vec4h","i32","vec2i","vec3i","vec4i","u32","vec2u","vec3u","vec4u","bool","vec2b","vec3b","vec4b","kindToSchema","mat2x2f","mat3x3f","mat4x4f","indexableTypeToResult","getTypeForPropAccess","targetType","propName","ctx","getResolutionCtx","unwrapped","getTypeFromWgsl","target","isDecorated","targetTypeStr","propLength","swizzleTypeChar","swizzleType","getTypeForIndexAccess","resource","isWgslArray","numericLiteralToSnippet","kind","abstractInt"],"mappings":"AAAA,y3CAAiL,wDAA6b,SCiE9lBA,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,CCbO,IAAMI,CAAAA,CAAc,CACzB,IAAA,CAAM,SACR,CAAA,CAgHO,SAASC,EAAAA,CAAiBL,CAAAA,CAAyC,CACxE,OAAO,uBAAQA,CAAAA,4BAAAA,CAA2B,UAAU,GAAA,EAAM,UAC5D,CAEO,SAASM,CAAAA,CAAON,CAAAA,CAA+B,CACpD,OACE,OAAOA,CAAAA,EAAU,QAAA,EACjB,OAAOA,CAAAA,EAAU,SAAA,EACjB,OAAOA,CAAAA,EAAU,QAAA,EACjBK,EAAAA,CAAiBL,CAAK,CAAA,EACtBO,iCAAAA,CAAgB,CAAA,EAChBR,CAAAA,CAAOC,CAAK,CAAA,EACZC,CAAAA,CAAUD,CAAK,CAAA,EACfE,EAAAA,CAAYF,CAAK,CAErB,CAKO,SAASQ,EAAAA,CAAYR,CAAAA,CAAoC,CAC9D,MACE,CAAC,CAACA,CAAAA,EACF,OAAOA,CAAAA,EAAU,QAAA,EACjB,gBAAA,GAAoBA,CAAAA,EACpB,UAAA,GAAcA,CAElB,CAEO,SAASS,EAAAA,CAKdT,CAAAA,CAAgC,CAChC,uBAAQA,CAAAA,6BAAa,cAAA,GAAiB,cACxC,CAEO,SAASU,EAAAA,CACdV,CAAAA,CACkD,CAClD,MACE,CAAC,CAACA,CAAAA,EACF,OAAOA,CAAAA,EAAU,QAAA,EACjB,CAAC,iBAAEA,CAAAA,4BAAAA,CAAoDW,mBAAS,CAAA,6BAAG,UAEvE,CAEO,SAASC,EAAAA,CACdZ,CAAAA,CACmD,CACnD,MAAO,CAAC,iBAAEA,CAAAA,8BAAAA,CAAqDW,mBAAS,GAC1E,CClMA,IAAME,EAAAA,CAAmB,CACvB,OAAA,CACA,OAAA,CACA,OAAA,CACA,OAAA,CACA,YAAA,CACA,OAAA,CACA,OAAA,CACA,OAAA,CACA,OAAA,CACA,YAAA,CACA,OAAA,CACA,OAAA,CACA,OAAA,CACA,OAAA,CACA,YAAA,CACA,QACF,CAAA,CAKMC,EAAAA,CAGF,CACF,CAAA,CAAG,CACD,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBACL,CAAA,CACA,CAAA,CAAG,CACD,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBACL,CAAA,CACA,CAAA,CAAG,CACD,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBACL,CAAA,CACA,CAAA,CAAG,CACD,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBACL,CAAA,CACA,CAAA,CAAG,CACD,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBAAAA,CACH,CAAA,CAAGC,mBACL,CACF,CAAA,CAEMC,EAAAA,CAAe,CACnB,KAAA,CAAOnB,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,YAAA,CAAcI,mBAAAA,CACd,KAAA,CAAOf,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,YAAA,CAAcI,mBAAAA,CACd,KAAA,CAAOf,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,YAAA,CAAcI,mBAAAA,CACd,OAAA,CAASE,mBAAAA,CACT,OAAA,CAASC,mBAAAA,CACT,OAAA,CAASC,mBACX,CAAA,CAEMC,EAAAA,CAAwB,CAC5B,KAAA,CAAOxB,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,YAAA,CAAcI,mBAAAA,CACd,KAAA,CAAOhB,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,YAAA,CAAcI,mBAAAA,CACd,KAAA,CAAOhB,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,KAAA,CAAOI,mBAAAA,CACP,YAAA,CAAcI,mBAAAA,CACd,OAAA,CAASf,mBAAAA,CACT,OAAA,CAASC,mBAAAA,CACT,OAAA,CAASC,mBACX,CAAA,CAEO,SAASsB,EAAAA,CACdC,CAAAA,CACAC,CAAAA,CACuB,CACvB,EAAA,CACE,OAAOD,CAAAA,EAAe,QAAA,EACtB,OAAOA,CAAAA,EAAe,QAAA,EACtB,OAAOA,CAAAA,EAAe,SAAA,CAEtB,OAAOrC,CAAAA,CAGT,EAAA,CAAIH,CAAAA,CAAUwC,CAAU,CAAA,EAAK1C,CAAAA,CAAO0C,CAAU,CAAA,CAAG,CAC/C,IAAME,CAAAA,CAAMC,iCAAAA,CAAiB,CAC7B,EAAA,CAAI,CAACD,CAAAA,CACH,MAAM,IAAI,KAAA,CACR,8DACF,CAAA,CAEF,IAAME,CAAAA,CAAYF,CAAAA,CAAI,MAAA,CAAOF,CAAU,CAAA,CAEvC,OAAOK,EAAAA,CAAgBD,CAAS,CAClC,CAEA,IAAIE,CAAAA,CAASN,CAAAA,CAKb,GAAA,CAHI/B,EAAAA,CAAoBqC,CAAM,CAAA,EAAA,CAC5BA,CAAAA,CAASA,CAAAA,CAAOpC,mBAAS,CAAA,CAAE,QAAA,CAAA,CAEtBqC,kCAAAA,CAAkB,CAAA,CAAA,CACvBD,CAAAA,CAASA,CAAAA,CAAO,KAAA,CAGlB,IAAME,CAAAA,CACJ,MAAA,GAAUF,CAAAA,CAAUA,CAAAA,CAAO,IAAA,CAAkBA,CAAAA,CAAO,IAAA,CAEtD,EAAA,CAAIE,CAAAA,GAAkB,QAAA,CACpB,wBACIF,CAAAA,CAAsB,SAAA,CAAUL,CAAQ,CAAA,SAAiBtC,GAAAA,CAI/D,IAAM8C,CAAAA,CAAaR,CAAAA,CAAS,MAAA,CAC5B,EAAA,CACE7B,EAAAA,CAAiB,QAAA,CACfoC,CACF,CAAA,EACAC,CAAAA,EAAc,CAAA,EACdA,CAAAA,EAAc,CAAA,CACd,CACA,IAAMC,CAAAA,CAAkBF,CAAAA,CAAc,QAAA,CAAS,MAAM,CAAA,CACjD,GAAA,CACCA,CAAAA,CAAc,CAAC,CAAA,CACdG,CAAAA,CACJtC,EAAAA,CAAiBqC,CAAe,CAAA,CAAED,CAA2B,CAAA,CAC/D,EAAA,CAAIE,CAAAA,CACF,OAAOA,CAEX,CAEA,OAAO7C,iCAAAA,CAAiB,CAAA,CAAIwC,CAAAA,CAAS3C,CACvC,CAEO,SAASiD,EAAAA,CAAsBC,CAAAA,CAAuC,CAC3E,EAAA,CAAI/C,iCAAAA,CAAmB,CAAA,CAAG,CAExB,EAAA,CAAIgD,iCAAAA,CAAoB,CAAA,CACtB,OAAOD,CAAAA,CAAS,WAAA,CAIlB,EAAA,CAAIA,CAAAA,CAAS,KAAA,GAAQf,EAAAA,CACnB,OAAOA,EAAAA,CACLe,CAAAA,CAAS,IACX,CAEJ,CAEA,OAAOlD,CACT,CAEO,SAAS0C,EAAAA,CAAgBQ,CAAAA,CAAuC,CACrE,EAAA,CAAIrD,CAAAA,CAAUqD,CAAQ,CAAA,EAAKvD,CAAAA,CAAOuD,CAAQ,CAAA,CACxC,OAAOR,EAAAA,CAAgBQ,CAAAA,CAAS,KAAa,CAAA,CAG/C,EAAA,CAAI,OAAOA,CAAAA,EAAa,QAAA,CACtB,OAAOlD,CAAAA,CAET,EAAA,CAAI,OAAOkD,CAAAA,EAAa,QAAA,CACtB,wCAAOE,EAAAA,qBAAwB,MAAA,CAAOF,CAAQ,CAAC,CAAA,+BAAG,UAAA,SAAYlD,GAAAA,CAEhE,EAAA,CAAI,OAAOkD,CAAAA,EAAa,SAAA,CACtB,OAAOvB,mBAAAA,CAGT,EAAA,CAAI,MAAA,GAAUuB,CAAAA,CAAU,CACtB,IAAMG,CAAAA,CAAOH,CAAAA,CAAS,IAAA,CACtB,EAAA,CAAIG,EAAAA,GAAQtB,EAAAA,CACV,OAAOA,EAAAA,CAAasB,CAAI,CAE5B,CAEA,OAAOlD,iCAAAA,CAAmB,CAAA,CAAI+C,CAAAA,CAAWlD,CAC3C,CAEO,SAASoD,EAAAA,CAAwBxD,CAAAA,CAAoC,CAE1E,EAAA,CAAI,gBAAA,CAAiB,IAAA,CAAKA,CAAK,CAAA,CAC7B,MAAO,CAAE,KAAA,CAAAA,CAAAA,CAAO,QAAA,CAAU0D,mBAAY,CAAA,CAIxC,EAAA,CAAI,YAAA,CAAa,IAAA,CAAK1D,CAAK,CAAA,CACzB,MAAO,CACL,KAAA,CAAO,CAAA,EAAA;ACtIQ;AACV,OAAA;AAGA;AAwBU;AACV,OAAA;AAOA;AAAA;ACpE8C;AAM7C;AAa+E;AAmB2C;AAOpD;ACoIhF;ACjKsE;AAAK;AAsRE;AA8FnE;AACH;AAPgB;AAgE8B;AAaxB;ACrNG;AAsPe;ACpZ3B","file":"/Users/marcinhawryluk/Documents/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import type { AnyWgslData } from '../../data/wgslTypes.ts';\nimport type { TgpuNamable } from '../../namable.ts';\nimport type { $repr, Infer } from '../../shared/repr.ts';\nimport type { Labelled } from '../../types.ts';\nimport type { TgpuFn } from '../function/tgpuFn.ts';\nimport type { TgpuBufferUsage } from './../buffer/bufferUsage.ts';\n\nexport interface TgpuSlot<T> extends TgpuNamable, Labelled {\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 Labelled {\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","import type { ArgNames, Block } from 'tinyest';\nimport type {\n TgpuBufferMutable,\n TgpuBufferReadonly,\n TgpuBufferUniform,\n TgpuBufferUsage,\n} from './core/buffer/bufferUsage.ts';\nimport type { TgpuConst } from './core/constant/tgpuConstant.ts';\nimport type { TgpuDeclare } from './core/declare/tgpuDeclare.ts';\nimport type { TgpuComputeFn } from './core/function/tgpuComputeFn.ts';\nimport type { TgpuFn } from './core/function/tgpuFn.ts';\nimport type { TgpuFragmentFn } from './core/function/tgpuFragmentFn.ts';\nimport type { TgpuVertexFn } from './core/function/tgpuVertexFn.ts';\nimport type { TgpuComputePipeline } from './core/pipeline/computePipeline.ts';\nimport type { TgpuRenderPipeline } from './core/pipeline/renderPipeline.ts';\nimport type { TgpuSampler } from './core/sampler/sampler.ts';\nimport {\n type Eventual,\n type SlotValuePair,\n type TgpuAccessor,\n type TgpuSlot,\n isDerived,\n isProviding,\n isSlot,\n} from './core/slot/slotTypes.ts';\nimport type { TgpuExternalTexture } from './core/texture/externalTexture.ts';\nimport type {\n TgpuAnyTextureView,\n TgpuTexture,\n} from './core/texture/texture.ts';\nimport type { TgpuVar } from './core/variable/tgpuVariable.ts';\nimport type { AnyData } from './data/dataTypes.ts';\nimport {\n type AnyMatInstance,\n type AnyVecInstance,\n type AnyWgslData,\n type BaseData,\n isWgslData,\n} from './data/wgslTypes.ts';\nimport type { NameRegistry } from './nameRegistry.ts';\nimport type { Infer } from './shared/repr.ts';\nimport { $internal } from './shared/symbols.ts';\nimport type {\n TgpuBindGroupLayout,\n TgpuLayoutEntry,\n} from './tgpuBindGroupLayout.ts';\n\nexport type ResolvableObject =\n | SelfResolvable\n | TgpuBufferUsage\n | TgpuConst\n | TgpuDeclare\n | TgpuFn\n | TgpuComputeFn\n | TgpuFragmentFn\n | TgpuComputePipeline\n | TgpuRenderPipeline\n | TgpuVertexFn\n | TgpuSampler\n | TgpuAccessor\n | TgpuExternalTexture\n | TgpuTexture\n | TgpuAnyTextureView\n | TgpuVar\n | AnyVecInstance\n | AnyMatInstance\n | AnyData\n // biome-ignore lint/suspicious/noExplicitAny: <has to be more permissive than unknown>\n | TgpuFn<any, any>;\n\nexport type Wgsl = Eventual<string | number | boolean | ResolvableObject>;\n\nexport const UnknownData = {\n type: 'unknown' as const,\n};\nexport type UnknownData = typeof UnknownData;\n\nexport type Snippet = {\n value: unknown;\n dataType: AnyData | UnknownData;\n};\n\nexport type TgpuShaderStage = 'compute' | 'vertex' | 'fragment';\n\nexport interface FnToWgslOptions {\n args: Snippet[];\n returnType: AnyWgslData;\n body: Block;\n externalMap: Record<string, unknown>;\n}\n\nexport type ItemLayer = {\n type: 'item';\n usedSlots: Set<TgpuSlot<unknown>>;\n};\n\nexport interface ItemStateStack {\n readonly itemDepth: number;\n readonly topItem: ItemLayer;\n\n pushItem(): void;\n popItem(): void;\n pushSlotBindings(pairs: SlotValuePair<unknown>[]): void;\n popSlotBindings(): void;\n pushFunctionScope(\n args: Snippet[],\n returnType: AnyWgslData | undefined,\n externalMap: Record<string, unknown>,\n ): void;\n popFunctionScope(): void;\n pushBlockScope(): void;\n popBlockScope(): void;\n pop(type?: 'functionScope' | 'blockScope' | 'slotBinding' | 'item'): void;\n readSlot<T>(slot: TgpuSlot<T>): T | undefined;\n getSnippetById(id: string): Snippet | undefined;\n defineBlockVariable(id: string, type: AnyWgslData | UnknownData): Snippet;\n}\n\n/**\n * Passed into each resolvable item. All items in a tree share a resolution ctx,\n * but there can be layers added and removed from the item stack when going down\n * and up the tree.\n */\nexport interface ResolutionCtx {\n readonly names: NameRegistry;\n\n addDeclaration(declaration: string): void;\n\n /**\n * Reserves a bind group number, and returns a placeholder that will be replaced\n * with a concrete number at the end of the resolution process.\n */\n allocateLayoutEntry(layout: TgpuBindGroupLayout): string;\n\n /**\n * Reserves a spot in the catch-all bind group, without the indirection of a bind-group.\n * This means the resource is 'fixed', and cannot be swapped between code execution.\n */\n allocateFixedEntry(\n layoutEntry: TgpuLayoutEntry,\n resource: object,\n ): {\n group: string;\n binding: number;\n };\n\n withSlots<T>(pairs: SlotValuePair<unknown>[], callback: () => T): T;\n\n /**\n * Unwraps all layers of slot/derived indirection and returns the concrete value if available.\n * @throws {MissingSlotValueError}\n */\n unwrap<T>(eventual: Eventual<T>): T;\n\n resolve(item: unknown): string;\n resolveValue<T extends BaseData>(value: Infer<T>, schema: T): string;\n\n transpileFn(fn: string): {\n argNames: ArgNames;\n body: Block;\n externalNames: string[];\n };\n fnToWgsl(options: FnToWgslOptions): {\n head: Wgsl;\n body: Wgsl;\n };\n\n [$internal]: {\n itemStateStack: ItemStateStack;\n };\n}\n\n/**\n * Houses a method '~resolve` that returns a code string\n * representing it, as opposed to offloading the resolution\n * to another mechanism.\n */\nexport interface SelfResolvable {\n '~resolve'(ctx: ResolutionCtx): string;\n toString(): string;\n}\n\nexport interface Labelled {\n readonly label?: string | undefined;\n}\n\nexport function isSelfResolvable(value: unknown): value is SelfResolvable {\n return typeof (value as SelfResolvable)?.['~resolve'] === 'function';\n}\n\nexport function isWgsl(value: unknown): value is Wgsl {\n return (\n typeof value === 'number' ||\n typeof value === 'boolean' ||\n typeof value === 'string' ||\n isSelfResolvable(value) ||\n isWgslData(value) ||\n isSlot(value) ||\n isDerived(value) ||\n isProviding(value)\n );\n}\n\nexport type BindableBufferUsage = 'uniform' | 'readonly' | 'mutable';\nexport type BufferUsage = 'uniform' | 'readonly' | 'mutable' | 'vertex';\n\nexport function isGPUBuffer(value: unknown): value is GPUBuffer {\n return (\n !!value &&\n typeof value === 'object' &&\n 'getMappedRange' in value &&\n 'mapAsync' in value\n );\n}\n\nexport function isBufferUsage<\n T extends\n | TgpuBufferUniform<BaseData>\n | TgpuBufferReadonly<BaseData>\n | TgpuBufferMutable<BaseData>,\n>(value: T | unknown): value is T {\n return (value as T)?.resourceType === 'buffer-usage';\n}\n\nexport function hasInternalDataType(\n value: unknown,\n): value is { [$internal]: { dataType: BaseData } } {\n return (\n !!value &&\n typeof value === 'object' &&\n !!(value as { [$internal]: { dataType: BaseData } })?.[$internal]?.dataType\n );\n}\n\nexport function isMarkedInternal(\n value: unknown,\n): value is { [$internal]: Record<string, unknown> } {\n return !!(value as { [$internal]: Record<string, unknown> })?.[$internal];\n}\n","import { isDerived, isSlot } from '../core/slot/slotTypes.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} from '../data/vector.ts';\nimport {\n type WgslStruct,\n isDecorated,\n isWgslArray,\n isWgslData,\n} from '../data/wgslTypes.ts';\nimport { getResolutionCtx } from '../gpuMode.ts';\nimport { $internal } from '../shared/symbols.ts';\nimport {\n type Snippet,\n UnknownData,\n type Wgsl,\n hasInternalDataType,\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 =\n 'kind' in target ? (target.kind as string) : 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 (isDerived(resource) || isSlot(resource)) {\n return getTypeFromWgsl(resource.value as Wgsl);\n }\n\n if (typeof resource === 'string') {\n return UnknownData;\n }\n if (typeof resource === 'number') {\n return 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 return isWgslData(resource) ? resource : 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","import { getAttributesString } from '../../data/attributes.ts';\nimport {\n type AnyData,\n type Disarray,\n type Unstruct,\n isLooseData,\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 { 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}: ${ctx.resolve(property as AnyWgslData)},\\n`;\n}\n\n/**\n * Resolves a struct and adds its declaration to the resolution context.\n * @param ctx - The resolution context.\n * @param struct - The struct to resolve.\n *\n * @returns The resolved struct name.\n */\nfunction resolveStruct(ctx: ResolutionCtx, struct: WgslStruct) {\n const id = ctx.names.makeUnique(struct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(struct.propTypes)\n .map((prop) => resolveStructProperty(ctx, prop))\n .join('')}\\\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an unstruct (struct that does not align data by default) to its struct data counterpart.\n * @param ctx - The resolution context.\n * @param unstruct - The unstruct to resolve.\n *\n * @returns The resolved unstruct name.\n *\n * @example\n * ```ts\n * resolveUnstruct(ctx, {\n * uv: d.float16x2, // -> d.vec2f after resolution\n * color: d.snorm8x4, -> d.vec4f after resolution\n * });\n * ```\n */\nfunction resolveUnstruct(ctx: ResolutionCtx, unstruct: Unstruct) {\n const id = ctx.names.makeUnique(unstruct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(unstruct.propTypes)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [prop[0], formatToWGSLType[prop[1].format]])\n : resolveStructProperty(ctx, prop),\n )\n .join('')}\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an array.\n * @param ctx - The resolution context.\n * @param array - The array to resolve.\n *\n * @returns The resolved array name along with its element type and count (if not runtime-sized).\n *\n * @example\n * ```ts\n * resolveArray(ctx, d.arrayOf(d.u32, 0)); // 'array<u32>' (not a real pattern, a function is preferred)\n * resolveArray(ctx, d.arrayOf(d.u32, 5)); // 'array<u32, 5>'\n * ```\n */\nfunction resolveArray(ctx: ResolutionCtx, array: WgslArray) {\n const element = ctx.resolve(array.elementType as AnyWgslData);\n\n return array.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${array.elementCount}>`;\n}\n\nfunction resolveDisarray(ctx: ResolutionCtx, disarray: Disarray) {\n const element = ctx.resolve(\n isAttribute(disarray.elementType)\n ? formatToWGSLType[disarray.elementType.format]\n : (disarray.elementType as AnyWgslData),\n );\n\n return disarray.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${disarray.elementCount}>`;\n}\n\n/**\n * Resolves a WGSL data-type schema to a string.\n * @param ctx - The resolution context.\n * @param data - The data-type to resolve.\n *\n * @returns The resolved data-type string.\n */\nexport function resolveData(ctx: ResolutionCtx, data: AnyData): string {\n if (isLooseData(data)) {\n if (data.type === 'unstruct') {\n return resolveUnstruct(ctx, data);\n }\n\n if (data.type === 'disarray') {\n return resolveDisarray(ctx, data);\n }\n\n if (data.type === 'loose-decorated') {\n return ctx.resolve(\n isAttribute(data.inner)\n ? formatToWGSLType[data.inner.format]\n : data.inner,\n );\n }\n\n return ctx.resolve(formatToWGSLType[data.type]);\n }\n\n if (isIdentityType(data)) {\n return data.type;\n }\n\n if (data.type === 'struct') {\n return resolveStruct(ctx, data);\n }\n\n if (data.type === 'array') {\n return resolveArray(ctx, data);\n }\n\n if (data.type === 'atomic') {\n return `atomic<${resolveData(ctx, data.inner)}>`;\n }\n\n if (data.type === 'decorated') {\n return ctx.resolve(data.inner as AnyWgslData);\n }\n\n if (data.type === 'ptr') {\n if (data.addressSpace === 'storage') {\n return `ptr<storage, ${ctx.resolve(data.inner)}, ${data.access === 'read-write' ? 'read_write' : data.access}>`;\n }\n return `ptr<${data.addressSpace}, ${ctx.resolve(data.inner)}>`;\n }\n\n if (data.type === 'abstractInt' || data.type === 'abstractFloat') {\n throw new Error('Abstract types have no concrete representation in WGSL');\n }\n\n 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}.${components[i]}, littleEndian);\\n`;\n }\n return code;\n }\n\n if (wgsl.isMat(node)) {\n const primitive = typeToPrimitive[node.type];\n const writeFunc = primitiveToWriteFunction[primitive];\n\n const matSize = wgsl.isMat2x2f(node) ? 2 : wgsl.isMat3x3f(node) ? 3 : 4;\n const elementCount = matSize * matSize;\n const rowStride = roundUp(matSize * 4, 8);\n\n let code = '';\n for (let i = 0; i < elementCount; i++) {\n const colIndex = Math.floor(i / matSize);\n const rowIndex = i % matSize;\n const byteOffset = colIndex * rowStride + rowIndex * 4;\n\n code += `output.${writeFunc}((${offsetExpr} + ${byteOffset}), ${valueExpr}.columns[${colIndex}].${['x', 'y', 'z', 'w'][rowIndex]}, littleEndian);\\n`;\n }\n\n return code;\n }\n\n const primitive = typeToPrimitive[node.type as keyof typeof typeToPrimitive];\n return `output.${primitiveToWriteFunction[primitive]}(${offsetExpr}, ${valueExpr}, littleEndian);\\n`;\n}\n\nexport function getCompiledWriterForSchema<T extends wgsl.BaseData>(\n schema: T,\n): (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n) => void {\n if (compiledWriters.has(schema)) {\n return compiledWriters.get(schema) as (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n ) => void;\n }\n\n const body = buildWriter(schema, 'offset', 'value');\n\n const fn = new Function(\n 'output',\n 'offset',\n 'value',\n 'littleEndian=true',\n body,\n ) as (\n output: DataView,\n offset: number,\n value: Infer<T> | unknown,\n littleEndian?: boolean,\n ) => void;\n\n compiledWriters.set(schema, fn);\n\n return fn;\n}\n","import type { ISerialInput, ISerialOutput } from 'typed-binary';\nimport type { Infer, InferRecord } from '../shared/repr.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 type { AnyData } 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 {\n type ResolutionCtx,\n type Snippet,\n UnknownData,\n isMarkedInternal,\n isWgsl,\n} from '../types.ts';\nimport {\n getTypeForIndexAccess,\n getTypeForPropAccess,\n getTypeFromWgsl,\n numericLiteralToSnippet,\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\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: wgsl.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 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 lhsStr = resolveRes(ctx, lhsExpr);\n const rhsStr = resolveRes(ctx, rhsExpr);\n const type = operatorToType(lhsExpr.dataType, op, rhsExpr.dataType);\n\n return {\n value: parenthesizedOps.includes(op)\n ? `(${lhsStr} ${op} ${rhsStr})`\n : `${lhsStr} ${op} ${rhsStr}`,\n dataType: type,\n };\n }\n\n if (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 (typeof target.value === 'string') {\n return {\n value: `${target.value}.${property}`,\n dataType: d.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 return {\n value: `${targetStr}[${propertyStr}]`,\n dataType: d.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 ${String(idValue)} has not been created using TypeGPU APIs. Did you mean to wrap the function with tgpu.fn(args, return)(...) ?`,\n );\n }\n\n // Assuming that `id` is callable\n const fnRes = (idValue as unknown as (...args: unknown[]) => unknown)(\n ...resolvedSnippets,\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 const generateEntries = (values: tinyest.Expression[]) =>\n values\n .map((value) => {\n const valueRes = generateExpression(ctx, value);\n return resolveRes(ctx, valueRes);\n })\n .join(', ');\n\n if (wgsl.isWgslStruct(callee)) {\n const propKeys = Object.keys(callee.propTypes);\n const values = propKeys.map((key) => {\n const val = obj[key];\n if (val === undefined) {\n throw new Error(\n `Missing property ${key} in object literal for struct ${callee}`,\n );\n }\n return val;\n });\n\n return {\n value: generateEntries(values),\n dataType: callee,\n };\n }\n\n return {\n value: generateEntries(Object.values(obj)),\n dataType: UnknownData,\n };\n }\n\n if (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 let type = values[0]?.dataType;\n const mismatchedType = values.find((value) => value.dataType !== type);\n if (mismatchedType) {\n throw new Error(\n `Cannot mix types in array literal. Type ${mismatchedType.dataType.type} does not match expected type ${type?.type}.`,\n );\n }\n\n if (!wgsl.isWgslData(type)) {\n throw new Error('Cannot use non-WGSL data types in array literals.');\n }\n\n type =\n type.type === 'abstractInt'\n ? d.u32\n : type.type === 'abstractFloat'\n ? d.f32\n : type;\n\n const typeId = ctx.resolve(type);\n\n const arrayType = `array<${typeId}, ${values.length}>`;\n const arrayValues = values.map((value) => resolveRes(ctx, value));\n\n return {\n value: `${arrayType}( ${arrayValues.join(', ')} )`,\n dataType: d.arrayOf(type, values.length) as d.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\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 =\n 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 condition = resolveRes(ctx, generateExpression(ctx, cond));\n\n ctx.indent(); // {\n const consequent = generateStatement(ctx, cons);\n ctx.dedent(); // }\n\n ctx.indent(); // {\n const alternate = alt ? generateStatement(ctx, alt) : undefined;\n ctx.dedent(); // }\n\n if (!alternate) {\n return `\\\n${ctx.pre}if (${condition})\n${consequent}`;\n }\n\n return `\\\n${ctx.pre}if (${condition})\n${consequent}\n${ctx.pre}else\n${alternate}`;\n }\n\n if (statement[0] === NODE.let || statement[0] === NODE.const) {\n const [_, rawId, rawValue] = statement;\n const eq = rawValue ? generateExpression(ctx, rawValue) : undefined;\n\n if (!eq || !rawValue) {\n throw new Error('Cannot create variable without an initial value.');\n }\n\n if (d.isLooseData(eq.dataType)) {\n throw new Error('Cannot create variable with loose data type.');\n }\n\n registerBlockVariable(ctx, rawId, eq.dataType);\n const id = resolveRes(ctx, generateIdentifier(ctx, rawId));\n\n // If the value is a plain JS object it has to be an output struct\n if (\n typeof rawValue === 'object' &&\n rawValue[0] === NODE.objectExpr &&\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1])\n ) {\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}var ${id} = ${resolvedStruct}(${resolveRes(ctx, eq)});`;\n }\n\n return `${ctx.pre}var ${id} = ${resolveRes(ctx, eq)};`;\n }\n\n if (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 const conditionStr = conditionExpr ? resolveRes(ctx, conditionExpr) : '';\n\n const updateStatement = update ? generateStatement(ctx, update) : undefined;\n const updateStr = updateStatement ? updateStatement.slice(0, -1) : '';\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}for (${initStr}; ${conditionStr}; ${updateStr})\n${bodyStr}`;\n }\n\n if (statement[0] === NODE.while) {\n const [_, condition, body] = statement;\n const conditionStr = resolveRes(ctx, generateExpression(ctx, condition));\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}while (${conditionStr})\n${bodyStr}`;\n }\n\n if (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 type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n isDerived,\n isProviding,\n isSlot,\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 { RuntimeMode, popMode, provideCtx, pushMode } 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 type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n bindGroupLayout,\n} from './tgpuBindGroupLayout.ts';\nimport { getTypeFromWgsl } 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 { UnknownData, 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 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) {\n return {\n value: external,\n dataType: isWgsl(external)\n ? getTypeFromWgsl(external)\n : UnknownData,\n };\n }\n\n // Since functions cannot access resources from the calling scope, we\n // return early here.\n return undefined;\n }\n\n if (layer?.type === 'blockScope') {\n const declarationType = layer.declarations.get(id);\n if (declarationType !== undefined) {\n return { value: id, dataType: declarationType };\n }\n } else {\n // Skip\n }\n }\n\n return undefined;\n }\n\n defineBlockVariable(id: string, type: AnyWgslData | UnknownData): 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._nextFreeLayoutPlaceholderIdx++}#`;\n memoMap.set(layout, placeholderKey);\n }\n\n return placeholderKey;\n }\n\n allocateFixedEntry(\n layoutEntry: TgpuLayoutEntry,\n resource: object,\n ): { group: string; binding: number } {\n const binding = this.fixedBindings.length;\n this.fixedBindings.push({ layoutEntry, resource });\n\n return {\n group: CATCHALL_BIND_GROUP_IDX_MARKER,\n binding,\n };\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T {\n const value = this._itemStateStack.readSlot(slot);\n\n if (value === undefined) {\n throw new MissingSlotValueError(slot);\n }\n\n return value;\n }\n\n withSlots<T>(pairs: SlotValuePair<unknown>[], callback: () => T): T {\n this._itemStateStack.pushSlotBindings(pairs);\n\n try {\n return callback();\n } finally {\n this._itemStateStack.popSlotBindings();\n }\n }\n\n unwrap<T>(eventual: Eventual<T>): T {\n if (isProviding(eventual)) {\n return this.withSlots(\n eventual['~providing'].pairs,\n () => this.unwrap(eventual['~providing'].inner) as T,\n );\n }\n\n let maybeEventual = eventual;\n\n // Unwrapping all layers of slots.\n while (true) {\n if (isSlot(maybeEventual)) {\n maybeEventual = this.readSlot(maybeEventual);\n } else if (isDerived(maybeEventual)) {\n maybeEventual = this._getOrCompute(maybeEventual);\n } else {\n break;\n }\n }\n\n return maybeEventual;\n }\n\n _getOrCompute<T>(derived: TgpuDerived<T>): T {\n // All memoized versions of `derived`\n const instances = this._memoizedDerived.get(derived) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue),\n )\n ) {\n return instance.result as T;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n // Derived computations are always done on the CPU\n pushMode(RuntimeMode.CPU);\n\n let result: T;\n try {\n result = derived['~compute']();\n } finally {\n popMode(RuntimeMode.CPU);\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedDerived.set(derived, instances);\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(derived);\n }\n\n throw new ResolutionError(err, [derived]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n /**\n * @param item The item whose resolution should be either retrieved from the cache (if there is a cache hit), or resolved.\n */\n _getOrInstantiate(item: object): string {\n // All memoized versions of `item`\n const instances = this._memoizedResolves.get(item) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue),\n )\n ) {\n return instance.result;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n let result: string;\n if (isData(item)) {\n result = resolveData(this, item);\n } else if (isDerived(item) || isSlot(item)) {\n result = this.resolve(this.unwrap(item));\n } else if (isSelfResolvable(item)) {\n result = item['~resolve'](this);\n } else {\n result = this.resolveValue(item);\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedResolves.set(item, instances);\n\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(item);\n }\n\n throw new ResolutionError(err, [item]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n resolve(item: unknown): string {\n if (isProviding(item)) {\n return this.withSlots(item['~providing'].pairs, () =>\n this.resolve(item['~providing'].inner),\n );\n }\n\n if ((item && typeof item === 'object') || typeof item === 'function') {\n if (this._itemStateStack.itemDepth === 0) {\n try {\n pushMode(RuntimeMode.GPU);\n const result = provideCtx(this, () => this._getOrInstantiate(item));\n return `${[...this._declarations].join('\\n\\n')}${result}`;\n } finally {\n popMode(RuntimeMode.GPU);\n }\n }\n\n return this._getOrInstantiate(item);\n }\n\n return String(item);\n }\n\n resolveValue<T extends BaseData>(\n value: Infer<T>,\n schema?: T | undefined,\n ): string {\n if (isWgsl(value)) {\n return this.resolve(value);\n }\n\n if (schema && isWgslArray(schema)) {\n return `array(${(value as unknown[]).map((element) => this.resolveValue(element, schema.elementType))})`;\n }\n\n if (Array.isArray(value)) {\n return `array(${value.map((element) => this.resolveValue(element))})`;\n }\n\n if (schema && isWgslStruct(schema)) {\n return `${this.resolve(schema)}(${Object.entries(schema.propTypes).map(([key, type_]) => this.resolveValue((value as Infer<typeof schema>)[key], type_))})`;\n }\n\n throw new Error(\n `Value ${value} (as json: ${JSON.stringify(value)}) of schema ${schema} is not resolvable to WGSL`,\n );\n }\n}\n\nexport interface ResolutionResult {\n code: string;\n bindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | null;\n}\n\nexport function resolve(\n item: Wgsl,\n options: ResolutionCtxImplOptions,\n): ResolutionResult {\n const ctx = new ResolutionCtxImpl(options);\n let code = ctx.resolve(item);\n\n const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;\n const bindGroupLayouts: TgpuBindGroupLayout[] = [];\n const takenIndices = new Set<number>(\n [...memoMap.keys()]\n .map((layout) => layout.index)\n .filter((v): v is number => v !== undefined),\n );\n\n const automaticIds = naturalsExcept(takenIndices);\n\n const layoutEntries = ctx.fixedBindings.map(\n (binding, idx) =>\n [String(idx), binding.layoutEntry] as [string, TgpuLayoutEntry],\n );\n\n const createCatchallGroup = () => {\n const catchallIdx = automaticIds.next().value;\n const catchallLayout = bindGroupLayout(Object.fromEntries(layoutEntries));\n bindGroupLayouts[catchallIdx] = catchallLayout;\n code = code.replaceAll(CATCHALL_BIND_GROUP_IDX_MARKER, String(catchallIdx));\n\n return [\n catchallIdx,\n new TgpuBindGroupImpl(\n catchallLayout,\n Object.fromEntries(\n ctx.fixedBindings.map(\n (binding, idx) =>\n // biome-ignore lint/suspicious/noExplicitAny: <it's fine>\n [String(idx), binding.resource] as [string, any],\n ),\n ),\n ),\n ] as [number, TgpuBindGroup];\n };\n\n // Retrieving the catch-all binding index first, because it's inherently\n // the least swapped bind group (fixed and cannot be swapped).\n const catchall = layoutEntries.length > 0 ? createCatchallGroup() : null;\n\n for (const [layout, placeholder] of memoMap.entries()) {\n const idx = layout.index ?? automaticIds.next().value;\n bindGroupLayouts[idx] = layout;\n code = code.replaceAll(placeholder, String(idx));\n }\n\n return {\n code,\n bindGroupLayouts,\n catchall,\n };\n}\n\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)} ${ctx.resolve(returnType)}`\n : `(${argList})`;\n}\n","import { getAttributesString } from '../../data/attributes.ts';\nimport {\n type AnyWgslData,\n isWgslData,\n isWgslStruct,\n} from '../../data/wgslTypes.ts';\nimport { MissingLinksError } from '../../errors.ts';\nimport { resolveFunctionHeader } from '../../resolutionCtx.ts';\nimport type { ResolutionCtx, Snippet } from '../../types.ts';\nimport {\n type ExternalMap,\n addArgTypesToExternals,\n addReturnTypeToExternals,\n applyExternals,\n replaceExternalsInWgsl,\n} from '../resolve/externals.ts';\nimport { getPrebuiltAstFor } from './astUtils.ts';\nimport type { Implementation } from './fnTypes.ts';\n\nexport interface TgpuFnShellBase<\n Args extends unknown[] | Record<string, unknown>,\n Return,\n> {\n readonly argTypes: Args;\n readonly returnType: Return | undefined;\n readonly isEntry: boolean;\n}\n\nexport interface FnCore {\n label: string | undefined;\n applyExternals(newExternals: ExternalMap): void;\n resolve(ctx: ResolutionCtx, fnAttribute?: string): string;\n}\n\nexport function createFnCore(\n shell: TgpuFnShellBase<unknown[] | Record<string, 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 if (Array.isArray(shell.argTypes)) {\n // TODO: Remove this branch along with deprecated array arg types\n\n addArgTypesToExternals(\n implementation,\n Array.isArray(shell.argTypes)\n ? shell.argTypes\n : Object.values(shell.argTypes),\n (externals) => externalsToApply.push(externals),\n );\n addReturnTypeToExternals(\n implementation,\n shell.returnType,\n (externals) => externalsToApply.push(externals),\n );\n }\n } else {\n if (Array.isArray(shell.argTypes) && 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 return {\n label: undefined as string | undefined,\n\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(this.label);\n\n if (typeof implementation === 'string') {\n let header = '';\n\n if (!shell.isEntry) {\n header = Array.isArray(shell.argTypes)\n ? ''\n : resolveFunctionHeader(\n ctx,\n Object.entries(shell.argTypes).map(([value, dataType]) => ({\n value,\n dataType: dataType as AnyWgslData,\n })),\n shell.returnType as AnyWgslData,\n );\n } else {\n const input =\n Array.isArray(shell.argTypes) && isWgslStruct(shell.argTypes[0])\n ? '(in: In)'\n : '()';\n\n const attributes = isWgslData(shell.returnType)\n ? getAttributesString(shell.returnType)\n : '';\n const output =\n shell.returnType !== undefined\n ? isWgslStruct(shell.returnType)\n ? '-> Out'\n : `-> ${attributes !== '' ? attributes : '@location(0)'} ${ctx.resolve(shell.returnType)}`\n : '';\n header = `${input} ${output} `;\n }\n\n const replacedImpl = replaceExternalsInWgsl(\n ctx,\n externalMap,\n `${header}${implementation.trim()}`,\n );\n\n ctx.addDeclaration(`${fnAttribute}fn ${id}${replacedImpl}`);\n } else {\n // get data generated by the plugin\n const pluginData = getPrebuiltAstFor(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 if (ast.argNames.type === 'destructured-object') {\n applyExternals(\n externalMap,\n Object.fromEntries(\n ast.argNames.props.map(({ prop, alias }) => [alias, prop]),\n ),\n );\n }\n\n if (\n !Array.isArray(shell.argTypes) &&\n ast.argNames.type === 'identifiers' &&\n ast.argNames.names[0] !== undefined\n ) {\n applyExternals(externalMap, {\n [ast.argNames.names[0]]: Object.fromEntries(\n Object.keys(shell.argTypes).map((arg) => [arg, arg]),\n ),\n });\n }\n\n // Verifying all required externals are present.\n const missingExternals = ast.externalNames.filter(\n (name) => !(name in externalMap),\n );\n\n if (missingExternals.length > 0) {\n throw new MissingLinksError(this.label, missingExternals);\n }\n\n const args: Snippet[] = Array.isArray(shell.argTypes)\n ? ast.argNames.type === 'identifiers'\n ? shell.argTypes.map((arg, i) => ({\n value:\n (ast.argNames.type === 'identifiers'\n ? ast.argNames.names[i]\n : undefined) ?? `arg_${i}`,\n dataType: arg as AnyWgslData,\n }))\n : []\n : Object.entries(shell.argTypes).map(([name, dataType]) => ({\n value: name,\n dataType: dataType as AnyWgslData,\n }));\n\n const { head, body } = ctx.fnToWgsl({\n args,\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"]}
|
package/index.d.cts
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
import { A as AnyWgslData, T as TgpuNamable, I as Infer, $ as $internal, F as F32, a as F16, b as I32, U as U32, V as Vec2f, c as Vec3f, d as Vec4f, e as Vec2h, f as Vec3h, g as Vec4h, h as Vec2i, i as Vec3i, j as Vec4i, k as Vec2u, l as Vec3u, m as Vec4u, D as Decorated, n as AnyWgslStruct, o as $repr, B as BaseData, L as Location, W as WgslStruct, p as WgslArray, q as Disarray, r as AnyUnstruct, s as VertexFormat, t as TgpuVertexAttrib, K as KindToDefaultFormatMap, u as KindToAcceptedAttribMap, v as AnyData, O as OmitProps, P as Prettify, M as Mutable, w as WgslTypeLiteral, x as UnionToIntersection, y as InferPartial, z as MemIdentity, C as Default, E as InferGPU, G as AnyVecInstance, H as AnyMatInstance } from './dataTypes-
|
2
|
-
import { A as AnyAttribute, a as AnyComputeBuiltin, D as Decorate, I as IsBuiltin, H as HasCustomLocation, b as AnyFragmentInputBuiltin, c as AnyFragmentOutputBuiltin, O as OmitBuiltins } from './attributes-
|
3
|
-
import * as
|
1
|
+
import { A as AnyWgslData, T as TgpuNamable, I as Infer, $ as $internal, F as F32, a as F16, b as I32, U as U32, V as Vec2f, c as Vec3f, d as Vec4f, e as Vec2h, f as Vec3h, g as Vec4h, h as Vec2i, i as Vec3i, j as Vec4i, k as Vec2u, l as Vec3u, m as Vec4u, D as Decorated, n as AnyWgslStruct, o as $repr, B as BaseData, L as Location, W as WgslStruct, p as WgslArray, q as Disarray, r as AnyUnstruct, s as VertexFormat, t as TgpuVertexAttrib, K as KindToDefaultFormatMap, u as KindToAcceptedAttribMap, v as AnyData, O as OmitProps, P as Prettify, M as Mutable, w as WgslTypeLiteral, x as UnionToIntersection, y as InferPartial, z as MemIdentity, C as Default, E as InferGPU, G as AnyVecInstance, H as AnyMatInstance } from './dataTypes-6k4EJeol.cjs';
|
2
|
+
import { A as AnyAttribute, a as AnyComputeBuiltin, D as Decorate, I as IsBuiltin, H as HasCustomLocation, b as AnyFragmentInputBuiltin, c as AnyFragmentOutputBuiltin, O as OmitBuiltins } from './attributes-zljQ-iA7.cjs';
|
3
|
+
import * as tinyest from 'tinyest';
|
4
4
|
import { ArgNames, Block } from 'tinyest';
|
5
|
-
import { T as TgpuTexture, R as Render, a as TgpuReadonlyTexture, b as TgpuWriteonlyTexture, c as TgpuMutableTexture, d as TgpuSampledTexture, e as TgpuSampler, f as TgpuComparisonSampler, S as StorageFlag, g as StorageTextureTexelFormat, h as StorageTextureDimension, C as ChannelFormatToSchema, V as ViewDimensionToDimension, i as TexelFormatToDataType, j as Sampled, k as TextureProps, l as ChannelTypeToLegalFormats, m as SampleTypeToStringChannelType, n as TgpuAnyTextureView, s as sampler, o as comparisonSampler } from './sampler-
|
6
|
-
export { y as Storage, q as isComparisonSampler, r as isSampledTextureView, p as isSampler, t as isStorageTextureView, u as isTexture, v as isUsableAsRender, w as isUsableAsSampled, x as isUsableAsStorage } from './sampler-
|
5
|
+
import { T as TgpuTexture, R as Render, a as TgpuReadonlyTexture, b as TgpuWriteonlyTexture, c as TgpuMutableTexture, d as TgpuSampledTexture, e as TgpuSampler, f as TgpuComparisonSampler, S as StorageFlag, g as StorageTextureTexelFormat, h as StorageTextureDimension, C as ChannelFormatToSchema, V as ViewDimensionToDimension, i as TexelFormatToDataType, j as Sampled, k as TextureProps, l as ChannelTypeToLegalFormats, m as SampleTypeToStringChannelType, n as TgpuAnyTextureView, s as sampler, o as comparisonSampler } from './sampler-NRTAYTIW.cjs';
|
6
|
+
export { y as Storage, q as isComparisonSampler, r as isSampledTextureView, p as isSampler, t as isStorageTextureView, u as isTexture, v as isUsableAsRender, w as isUsableAsSampled, x as isUsableAsStorage } from './sampler-NRTAYTIW.cjs';
|
7
7
|
|
8
8
|
interface TgpuConst<TDataType extends AnyWgslData = AnyWgslData> extends TgpuNamable {
|
9
9
|
readonly value: Infer<TDataType>;
|
@@ -36,8 +36,8 @@ declare function declare(declaration: string): TgpuDeclare;
|
|
36
36
|
* Information extracted from transpiling a JS function.
|
37
37
|
*/
|
38
38
|
type TranspilationResult = {
|
39
|
-
argNames:
|
40
|
-
body:
|
39
|
+
argNames: tinyest.ArgNames;
|
40
|
+
body: tinyest.Block;
|
41
41
|
/**
|
42
42
|
* All identifiers found in the function code that are not declared in the
|
43
43
|
* function itself, or in the block that is accessing that identifier.
|
@@ -48,7 +48,8 @@ type InferArgs<T extends unknown[]> = {
|
|
48
48
|
[Idx in keyof T]: Infer<T[Idx]>;
|
49
49
|
};
|
50
50
|
type InferReturn<T> = T extends undefined ? void : Infer<T>;
|
51
|
-
type
|
51
|
+
type JsImplementation<Args extends unknown[] | Record<string, unknown> = unknown[] | Record<string, unknown>, Return = unknown> = (...args: Args extends unknown[] ? InferArgs<Args> : Args extends Record<string, never> ? [] : [InferIO<Args>]) => InferReturn<Return>;
|
52
|
+
type Implementation<Args extends unknown[] | Record<string, unknown> = unknown[] | Record<string, unknown>, Return = unknown> = string | JsImplementation<Args, Return>;
|
52
53
|
type BaseIOData = F32 | F16 | I32 | U32 | Vec2f | Vec3f | Vec4f | Vec2h | Vec3h | Vec4h | Vec2i | Vec3i | Vec4i | Vec2u | Vec3u | Vec4u;
|
53
54
|
type IOData = BaseIOData | Decorated<BaseIOData, AnyAttribute[]>;
|
54
55
|
type IORecord<TElementType extends IOData = IOData> = Record<string, TElementType>;
|
@@ -111,7 +112,7 @@ declare function computeFn<ComputeIn extends Record<string, AnyComputeBuiltin>>(
|
|
111
112
|
}): TgpuComputeFnShell<ComputeIn>;
|
112
113
|
|
113
114
|
/**
|
114
|
-
* Used to transpile JS resources into
|
115
|
+
* Used to transpile JS resources into tinyest on demand.
|
115
116
|
*/
|
116
117
|
interface JitTranspiler {
|
117
118
|
transpileFn(rawJs: string): TranspilationResult;
|
@@ -1027,6 +1028,9 @@ type TgpuFnShell<Args extends AnyWgslData[] | Record<string, AnyWgslData>, Retur
|
|
1027
1028
|
does: ((implementation: (...args: Args extends AnyWgslData[] ? InferArgs<Args> : [InferIO<Args>]) => InferReturn<Return>) => TgpuFn<Args, Return>) & ((implementation: string) => TgpuFn<Args, Return>);
|
1028
1029
|
};
|
1029
1030
|
interface TgpuFnBase<Args extends AnyWgslData[] | Record<string, AnyWgslData>, Return extends AnyWgslData | undefined = undefined> extends TgpuNamable, Labelled {
|
1031
|
+
readonly [$internal]: {
|
1032
|
+
implementation: Implementation<Args, Return>;
|
1033
|
+
};
|
1030
1034
|
readonly resourceType: 'function';
|
1031
1035
|
readonly shell: TgpuFnShellHeader<Args, Return>;
|
1032
1036
|
readonly '~providing'?: Providing | undefined;
|
@@ -1034,11 +1038,7 @@ interface TgpuFnBase<Args extends AnyWgslData[] | Record<string, AnyWgslData>, R
|
|
1034
1038
|
with<T>(slot: TgpuSlot<T>, value: Eventual<T>): TgpuFn<Args, Return>;
|
1035
1039
|
with<T extends AnyWgslData>(accessor: TgpuAccessor<T>, value: TgpuFn<[], T> | TgpuBufferUsage<T> | Infer<T>): TgpuFn<Args, Return>;
|
1036
1040
|
}
|
1037
|
-
type TgpuFn<Args extends AnyWgslData[] | Record<string, AnyWgslData> = AnyWgslData[], Return extends AnyWgslData | undefined = AnyWgslData | undefined> = TgpuFnBase<Args, Return> & ((...args: Args extends AnyWgslData[] ? InferArgs<Args> : Args extends Record<string, never> ? [] : [InferIO<Args>]) => InferReturn<Return>)
|
1038
|
-
readonly [$internal]: {
|
1039
|
-
implementation: Implementation<Args extends AnyWgslData[] ? InferArgs<Args> : Args extends Record<string, never> ? [] : [InferIO<Args>], InferReturn<Return>>;
|
1040
|
-
};
|
1041
|
-
};
|
1041
|
+
type TgpuFn<Args extends AnyWgslData[] | Record<string, AnyWgslData> = AnyWgslData[], Return extends AnyWgslData | undefined = AnyWgslData | undefined> = TgpuFnBase<Args, Return> & ((...args: Args extends AnyWgslData[] ? InferArgs<Args> : Args extends Record<string, never> ? [] : [InferIO<Args>]) => InferReturn<Return>);
|
1042
1042
|
declare function fn<Args extends AnyWgslData[] | Record<string, AnyWgslData> | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, undefined>;
|
1043
1043
|
declare function fn<Args extends AnyWgslData[] | Record<string, AnyWgslData> | [], Return extends AnyWgslData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>;
|
1044
1044
|
declare function isTgpuFn<Args extends AnyWgslData[], Return extends AnyWgslData | undefined = undefined>(value: unknown | TgpuFn<Args, Return>): value is TgpuFn<Args, Return>;
|