typegpu 0.4.3 → 0.4.5
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-Dri8dk1v.d.ts → attributes-B4JpvOTz.d.ts} +1 -1
- package/{attributes--1y4Kirx.d.cts → attributes-DSOqT8yA.d.cts} +1 -1
- package/chunk-7S2KQX6O.js +4 -0
- package/chunk-7S2KQX6O.js.map +1 -0
- package/chunk-JSF3RDCP.js +2 -0
- package/chunk-JSF3RDCP.js.map +1 -0
- package/chunk-TLEBUZ6A.cjs +4 -0
- package/{chunk-7X4KDNDZ.cjs.map → chunk-TLEBUZ6A.cjs.map} +1 -1
- package/chunk-WVFPBCIE.cjs +2 -0
- package/chunk-WVFPBCIE.cjs.map +1 -0
- package/data/index.cjs +1 -1
- package/data/index.cjs.map +1 -1
- package/data/index.d.cts +10 -5
- package/data/index.d.ts +10 -5
- package/data/index.js +1 -1
- package/index.cjs +12 -10
- package/index.cjs.map +1 -1
- package/index.d.cts +107 -102
- package/index.d.ts +107 -102
- package/index.js +12 -10
- package/index.js.map +1 -1
- package/package.json +2 -2
- package/std/index.cjs +1 -1
- package/std/index.cjs.map +1 -1
- package/std/index.d.cts +20 -2
- package/std/index.d.ts +20 -2
- package/std/index.js +1 -1
- package/std/index.js.map +1 -1
- package/{wgslTypes-D0JqCGnw.d.ts → wgslTypes-VtSRoe90.d.cts} +112 -28
- package/{wgslTypes-D0JqCGnw.d.cts → wgslTypes-VtSRoe90.d.ts} +112 -28
- package/chunk-7X4KDNDZ.cjs +0 -4
- package/chunk-O7JKWPDH.cjs +0 -2
- package/chunk-O7JKWPDH.cjs.map +0 -1
- package/chunk-OERXDFXG.js +0 -2
- package/chunk-OERXDFXG.js.map +0 -1
- package/chunk-ZHXAQ2YE.js +0 -4
- package/chunk-ZHXAQ2YE.js.map +0 -1
package/index.cjs.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","../src/core/slot/slotTypes.ts","../src/types.ts","../src/core/valueProxyUtils.ts","../src/core/resolve/resolveData.ts","../src/smol/wgslGenerator.ts","../src/data/dataIO.ts","../src/resolutionCtx.ts","../src/core/resolve/tgpuResolve.ts"],"names":["isSlot","value","isDerived","isProviding","isAccessor","UnknownData","isSelfResolvable","isWgsl","isWgslData","isGPUBuffer","isBufferUsage","valueProxyHandler","target","prop","ctx"],"mappings":"AAAA,iIAA6O,wDAA0P,SCiEvdA,CAAAA,CAAUC,CAAAA,CAAoD,CAC5E,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAuB,YAAA,CAAA,GAAiB,MAClD,CAEO,SAASC,CAAAA,CACdD,CAAAA,CACY,CACZ,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAa,YAAA,CAAA,GAAiB,SACxC,CAEO,SAASE,EAAAA,CACdF,CAAAA,CACsC,CACtC,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAwC,YAAA,CAAA,CAAA,GAAkB,KAAA,CACpE,CAEO,SAASG,EAAAA,CACdH,CAAAA,CAC0B,CAC1B,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAA2B,YAAA,CAAA,GAAiB,UACtD,CClBO,IAAMI,CAAAA,CAAc,MAAA,CAAO,mBAAmB,CAAA,CAiF9C,SAASC,EAAAA,CAAiBL,CAAAA,CAAyC,CACxE,OAAO,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAA2B,UAAA,CAAA,CAAA,EAAgB,UAC5D,CAEO,SAASM,EAAAA,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,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAa,YAAA,CAAA,GAAiB,cACxC,CChLO,IAAMU,CAAAA,CAA6D,CACxE,GAAA,CAAIC,CAAAA,CAAQC,CAAAA,CAAM,CAChB,EAAA,CAAIA,EAAAA,GAAQD,CAAAA,CACV,OAAO,OAAA,CAAQ,GAAA,CAAIA,CAAAA,CAAQC,CAAI,CAAA,CAGjC,EAAA,CAAIA,CAAAA,GAAS,YAAA,CAIb,OAAO,IAAI,KAAA,CACT,CACE,UAAA,CAAaC,CAAAA,EACX,CAAA,EAAA;AC+FW;AACV,OAAA;AAGA;AAwBU;AACV,OAAA;AAOA;AAAA;AC3Fa;AAC6C;AAAK;AA+JlE;AA4EI;AACH;AAPgB;AC3LvB;AC+Z6C;ACxb3B","file":"/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import type { AnyWgslData } from '../../data';\nimport type { TgpuNamable } from '../../namable';\nimport type { Infer } from '../../shared/repr';\nimport type { Labelled } from '../../types';\nimport type { TgpuFn } from '../function/tgpuFn';\nimport type { TgpuBufferUsage } from './../buffer/bufferUsage';\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 { Block } from 'tinyest';\nimport type {\n TgpuBufferMutable,\n TgpuBufferReadonly,\n TgpuBufferUniform,\n TgpuBufferUsage,\n} from './core/buffer/bufferUsage';\nimport type { TgpuConst } from './core/constant/tgpuConstant';\nimport type { TgpuDeclare } from './core/declare/tgpuDeclare';\nimport type { TgpuComputeFn } from './core/function/tgpuComputeFn';\nimport type { TgpuFn } from './core/function/tgpuFn';\nimport type { TgpuFragmentFn } from './core/function/tgpuFragmentFn';\nimport type { TgpuVertexFn } from './core/function/tgpuVertexFn';\nimport type { TgpuComputePipeline } from './core/pipeline/computePipeline';\nimport type { TgpuRenderPipeline } from './core/pipeline/renderPipeline';\nimport type { TgpuSampler } from './core/sampler/sampler';\nimport {\n type Eventual,\n type SlotValuePair,\n type TgpuAccessor,\n isDerived,\n isProviding,\n isSlot,\n} from './core/slot/slotTypes';\nimport type { TgpuExternalTexture } from './core/texture/externalTexture';\nimport type { TgpuAnyTextureView, TgpuTexture } from './core/texture/texture';\nimport type { TgpuVar } from './core/variable/tgpuVariable';\nimport type { AnyData } from './data';\nimport {\n type AnyMatInstance,\n type AnyVecInstance,\n type AnyWgslData,\n type BaseData,\n isWgslData,\n} from './data/wgslTypes';\nimport type { NameRegistry } from './nameRegistry';\nimport type { Infer } from './shared/repr';\nimport type {\n TgpuBindGroupLayout,\n TgpuLayoutEntry,\n} from './tgpuBindGroupLayout';\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 = Symbol('Unknown data type');\nexport type UnknownData = typeof UnknownData;\n\nexport type Resource = {\n value: unknown;\n dataType: AnyWgslData | UnknownData;\n};\n\nexport type TgpuShaderStage = 'compute' | 'vertex' | 'fragment';\n\nexport interface FnToWgslOptions {\n args: Resource[];\n returnType: AnyWgslData;\n body: Block;\n externalMap: Record<string, unknown>;\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: string[];\n body: Block;\n externalNames: string[];\n };\n fnToWgsl(options: FnToWgslOptions): {\n head: Wgsl;\n body: Wgsl;\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","import {\n type Labelled,\n type ResolutionCtx,\n type SelfResolvable,\n isBufferUsage,\n} from '../types';\nimport { isAccessor, isDerived, isSlot } from './slot/slotTypes';\n\nexport const valueProxyHandler: ProxyHandler<SelfResolvable & Labelled> = {\n get(target, prop) {\n if (prop in target) {\n return Reflect.get(target, prop);\n }\n\n if (prop === '~providing') {\n return undefined;\n }\n\n return new Proxy(\n {\n '~resolve': (ctx: ResolutionCtx) =>\n `${ctx.resolve(target)}.${String(prop)}`,\n\n toString: () =>\n `.value(...).${String(prop)}:${target.label ?? '<unnamed>'}`,\n },\n valueProxyHandler,\n );\n },\n};\n\nexport function unwrapProxy<T>(value: unknown): T {\n let unwrapped = value;\n\n while (\n isSlot(unwrapped) ||\n isDerived(unwrapped) ||\n isAccessor(unwrapped) ||\n isBufferUsage(unwrapped)\n ) {\n unwrapped = unwrapped.value;\n }\n\n return unwrapped as T;\n}\n","import { getAttributesString } from '../../data/attributes';\nimport {\n type AnyData,\n type Disarray,\n type Unstruct,\n isLooseData,\n} from '../../data/dataTypes';\nimport type { WgslStruct } from '../../data/struct';\nimport { formatToWGSLType } from '../../data/vertexFormatData';\nimport type {\n AnyWgslData,\n BaseData,\n Bool,\n F16,\n F32,\n I32,\n Mat2x2f,\n Mat3x3f,\n Mat4x4f,\n U32,\n Vec2f,\n Vec2h,\n Vec2i,\n Vec2u,\n Vec3f,\n Vec3h,\n Vec3i,\n Vec3u,\n Vec4f,\n Vec4h,\n Vec4i,\n Vec4u,\n WgslArray,\n} from '../../data/wgslTypes';\nimport { assertExhaustive } from '../../shared/utilityTypes';\nimport type { ResolutionCtx } from '../../types';\nimport { isAttribute } from '../vertexLayout/connectAttributesToShader';\n\n/**\n * Schemas for which their `type` property directly\n * translates to the resulting WGSL code.\n */\nconst identityTypes = [\n 'bool',\n 'f32',\n 'f16',\n 'i32',\n 'u32',\n 'vec2f',\n 'vec3f',\n 'vec4f',\n 'vec2h',\n 'vec3h',\n 'vec4h',\n 'vec2i',\n 'vec3i',\n 'vec4i',\n 'vec2u',\n 'vec3u',\n 'vec4u',\n 'mat2x2f',\n 'mat3x3f',\n 'mat4x4f',\n];\n\ntype IdentityType =\n | Bool\n | F32\n | F16\n | I32\n | U32\n | Vec2f\n | Vec3f\n | Vec4f\n | Vec2h\n | Vec3h\n | Vec4h\n | Vec2i\n | Vec3i\n | Vec4i\n | Vec2u\n | Vec3u\n | Vec4u\n | Mat2x2f\n | Mat3x3f\n | Mat4x4f;\n\nfunction isIdentityType(data: AnyWgslData): data is IdentityType {\n return identityTypes.includes(data.type);\n}\n\n/**\n * Resolves a single property of a struct.\n * @param ctx - The resolution context.\n * @param key - The key of the property.\n * @param property - The property itself.\n *\n * @returns The resolved property string.\n */\nfunction resolveStructProperty(\n ctx: ResolutionCtx,\n [key, property]: [string, BaseData],\n) {\n return ` ${getAttributesString(property)}${key}: ${ctx.resolve(property as AnyWgslData)},\\n`;\n}\n\n/**\n * Resolves a struct and adds its declaration to the resolution context.\n * @param ctx - The resolution context.\n * @param struct - The struct to resolve.\n *\n * @returns The resolved struct name.\n */\nfunction resolveStruct(ctx: ResolutionCtx, struct: WgslStruct) {\n const id = ctx.names.makeUnique(struct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(struct.propTypes)\n .map((prop) => resolveStructProperty(ctx, prop))\n .join('')}\\\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an unstruct (struct that does not align data by default) to its struct data counterpart.\n * @param ctx - The resolution context.\n * @param unstruct - The unstruct to resolve.\n *\n * @returns The resolved unstruct name.\n *\n * @example\n * ```ts\n * resolveUnstruct(ctx, {\n * uv: d.float16x2, // -> d.vec2f after resolution\n * color: d.snorm8x4, -> d.vec4f after resolution\n * });\n * ```\n */\nfunction resolveUnstruct(ctx: ResolutionCtx, unstruct: Unstruct) {\n const id = ctx.names.makeUnique(unstruct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(unstruct.propTypes)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [prop[0], formatToWGSLType[prop[1].format]])\n : resolveStructProperty(ctx, prop),\n )\n .join('')}\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an array.\n * @param ctx - The resolution context.\n * @param array - The array to resolve.\n *\n * @returns The resolved array name along with its element type and count (if not runtime-sized).\n *\n * @example\n * ```ts\n * resolveArray(ctx, d.arrayOf(d.u32, 0)); // 'array<u32>' (not a real pattern, a function is preferred)\n * resolveArray(ctx, d.arrayOf(d.u32, 5)); // 'array<u32, 5>'\n * ```\n */\nfunction resolveArray(ctx: ResolutionCtx, array: WgslArray) {\n const element = ctx.resolve(array.elementType as AnyWgslData);\n\n return array.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${array.elementCount}>`;\n}\n\nfunction resolveDisarray(ctx: ResolutionCtx, disarray: Disarray) {\n const element = ctx.resolve(\n isAttribute(disarray.elementType)\n ? formatToWGSLType[disarray.elementType.format]\n : (disarray.elementType as AnyWgslData),\n );\n\n return disarray.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${disarray.elementCount}>`;\n}\n\n/**\n * Resolves a WGSL data-type schema to a string.\n * @param ctx - The resolution context.\n * @param data - The data-type to resolve.\n *\n * @returns The resolved data-type string.\n */\nexport function resolveData(ctx: ResolutionCtx, data: AnyData): string {\n if (isLooseData(data)) {\n if (data.type === 'unstruct') {\n return resolveUnstruct(ctx, data);\n }\n\n if (data.type === 'disarray') {\n return resolveDisarray(ctx, data);\n }\n\n if (data.type === 'loose-decorated') {\n return ctx.resolve(\n isAttribute(data.inner)\n ? formatToWGSLType[data.inner.format]\n : data.inner,\n );\n }\n\n return ctx.resolve(formatToWGSLType[data.type]);\n }\n\n if (isIdentityType(data)) {\n return data.type;\n }\n\n if (data.type === 'struct') {\n return resolveStruct(ctx, data);\n }\n\n if (data.type === 'array') {\n return resolveArray(ctx, data);\n }\n\n if (data.type === 'atomic') {\n return `atomic<${resolveData(ctx, data.inner)}>`;\n }\n\n if (data.type === 'decorated') {\n return ctx.resolve(data.inner as AnyWgslData);\n }\n\n if (data.type === 'ptrFn') {\n return `ptr<function, ${ctx.resolve(data.inner)}>`;\n }\n\n assertExhaustive(data, 'resolveData');\n}\n","import type * as smol from 'tinyest';\nimport { bool } from '../data';\nimport { isWgslData, isWgslStruct } from '../data/wgslTypes';\nimport {\n type ResolutionCtx,\n type Resource,\n UnknownData,\n type Wgsl,\n isWgsl,\n} from '../types';\n\nconst parenthesizedOps = [\n '==',\n '!=',\n '<',\n '<=',\n '>',\n '>=',\n '<<',\n '>>',\n '+',\n '-',\n '*',\n '/',\n '%',\n '|',\n '^',\n '&',\n '&&',\n '||',\n];\n\nexport type GenerationCtx = ResolutionCtx & {\n readonly pre: string;\n readonly callStack: unknown[];\n indent(): string;\n dedent(): string;\n getById(id: string): Resource;\n};\n\nfunction resolveRes(ctx: GenerationCtx, res: Resource): string {\n if (isWgsl(res.value) || isWgslData(res.value)) {\n return ctx.resolve(res.value);\n }\n\n return String(res.value);\n}\n\nfunction assertExhaustive(value: unknown): never {\n throw new Error(\n `'${JSON.stringify(value)}' was not handled by the WGSL generator.`,\n );\n}\n\nfunction generateBoolean(ctx: GenerationCtx, value: boolean): Resource {\n return value\n ? { value: 'true', dataType: bool }\n : { value: 'false', dataType: bool };\n}\n\nfunction generateBlock(ctx: GenerationCtx, value: smol.Block): string {\n return `${ctx.indent()}{\n${value.b.map((statement) => generateStatement(ctx, statement)).join('\\n')}\n${ctx.dedent()}}`;\n}\n\nfunction generateIdentifier(ctx: GenerationCtx, id: string): Resource {\n return ctx.getById(id);\n}\n\nfunction generateExpression(\n ctx: GenerationCtx,\n expression: smol.Expression,\n): Resource {\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 ('x' in expression) {\n // Logical/Binary/Assignment Expression\n\n const [lhs, op, rhs] = expression.x;\n const lhsExpr = resolveRes(ctx, generateExpression(ctx, lhs));\n const rhsExpr = resolveRes(ctx, generateExpression(ctx, rhs));\n return {\n value: parenthesizedOps.includes(op)\n ? `(${lhsExpr} ${op} ${rhsExpr})`\n : `${lhsExpr} ${op} ${rhsExpr}`,\n // TODO: Infer data type from expression type and arguments.\n dataType: UnknownData,\n };\n }\n\n if ('u' in expression) {\n // Unary Expression\n\n const [op, arg] = expression.u;\n const argExpr = resolveRes(ctx, generateExpression(ctx, arg));\n return {\n value: `${op}${argExpr}`,\n // TODO: Infer data type from expression type and arguments.\n dataType: UnknownData,\n };\n }\n\n if ('a' in expression) {\n // Member Access\n\n const [targetId, property] = expression.a;\n const target = generateExpression(ctx, targetId);\n const propertyStr = resolveRes(ctx, generateExpression(ctx, property));\n\n if (typeof target.value === 'string') {\n return {\n value: `${target.value}.${propertyStr}`,\n // TODO: Infer data type\n dataType: UnknownData,\n };\n }\n\n if (isWgsl(target.value)) {\n return {\n // biome-ignore lint/suspicious/noExplicitAny: <sorry TypeScript>\n value: (target.value as any)[propertyStr],\n // TODO: Infer data type\n dataType: UnknownData,\n };\n }\n\n if (typeof target.value === 'object') {\n return {\n // biome-ignore lint/suspicious/noExplicitAny: <sorry TypeScript>\n value: (target.value as any)[propertyStr],\n // TODO: Infer data type\n dataType: UnknownData,\n };\n }\n\n throw new Error(`Cannot access member ${propertyStr} of ${target.value}`);\n }\n\n if ('i' in expression) {\n // Index Access\n\n const [target, property] = expression.i;\n const targetStr = resolveRes(ctx, generateExpression(ctx, target));\n const propertyStr = resolveRes(ctx, generateExpression(ctx, property));\n\n return {\n value: `${targetStr}[${propertyStr}]`,\n // TODO: Infer data type\n dataType: UnknownData,\n };\n }\n\n if ('n' in expression) {\n // Numeric Literal\n\n // TODO: Infer numeric data type from literal\n return { value: expression.n, dataType: UnknownData };\n }\n\n if ('f' in expression) {\n // Function Call\n\n const [callee, args] = expression.f;\n const id = generateExpression(ctx, callee);\n const idValue = id.value;\n\n ctx.callStack.push(idValue);\n\n const argResources = args.map((arg) => generateExpression(ctx, arg));\n const argValues = argResources.map((res) => resolveRes(ctx, res));\n\n ctx.callStack.pop();\n\n if (typeof idValue === 'string') {\n return {\n value: `${idValue}(${argValues.join(', ')})`,\n dataType: UnknownData,\n };\n }\n\n if (isWgslStruct(idValue)) {\n const id = ctx.resolve(idValue);\n\n return {\n value: `${id}(${argValues.join(', ')})`,\n dataType: UnknownData,\n };\n }\n\n // Assuming that `id` is callable\n // TODO: Pass in resources, not just values.\n const result = (idValue as unknown as (...args: unknown[]) => unknown)(\n ...argValues,\n ) as Wgsl;\n // TODO: Make function calls return resources instead of just values.\n return { value: result, dataType: UnknownData };\n }\n\n if ('o' in expression) {\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 (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 assertExhaustive(expression);\n}\n\nfunction 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 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(ctx, generateExpression(ctx, statement.r))};`;\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 id = resolveRes(ctx, generateIdentifier(ctx, rawId));\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 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 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 // TODO: Push block scope layer onto the stack\n return generateBlock(ctx, statement);\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 type { ISerialInput, ISerialOutput } from 'typed-binary';\nimport type { Infer, InferRecord } from '../shared/repr';\nimport alignIO from './alignIO';\nimport { alignmentOf, customAlignmentOf } from './alignmentOf';\nimport type { AnyData, Disarray, LooseDecorated, Unstruct } from './dataTypes';\nimport { mat2x2f, mat3x3f, mat4x4f } from './matrix';\nimport { sizeOf } from './sizeOf';\nimport type { WgslStruct } from './struct';\nimport {\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n} from './vector';\nimport type * as wgsl from './wgslTypes';\n\ntype DataWriter<TSchema extends wgsl.BaseData> = (\n output: ISerialOutput,\n schema: TSchema,\n value: Infer<TSchema>,\n) => void;\n\ntype DataReader<TSchema extends wgsl.BaseData> = (\n input: ISerialInput,\n schema: TSchema,\n) => Infer<TSchema>;\n\ntype CompleteDataWriters = {\n [TType in AnyData['type']]: DataWriter<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\ntype CompleteDataReaders = {\n [TType in AnyData['type']]: DataReader<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\nconst dataWriters = {\n bool(output, _schema: wgsl.Bool, value: boolean) {\n output.writeBool(value);\n },\n\n f32(output, _schema: wgsl.F32, value: number) {\n output.writeFloat32(value);\n },\n\n f16(output, _schema: wgsl.F16, value: number) {\n output.writeFloat16(value);\n },\n\n i32(output, _schema: wgsl.I32, value: number) {\n output.writeInt32(value);\n },\n\n u32(output, _schema: wgsl.U32, value: number) {\n output.writeUint32(value);\n },\n\n vec2f(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n\n vec2h(output, _, value: wgsl.v2h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n\n vec2i(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n\n vec2u(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n\n vec3f(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n\n vec3h(output, _, value: wgsl.v3h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n },\n\n vec3i(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n\n vec3u(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n\n vec4f(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n\n vec4h(output, _, value: wgsl.v4h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n\n vec4i(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n\n vec4u(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n\n mat2x2f(output, _, value: wgsl.m2x2f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat3x3f(output, _, value: wgsl.m3x3f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat4x4f(output, _, value: wgsl.m4x4f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n struct(\n output,\n schema: WgslStruct,\n value: InferRecord<Record<string, wgsl.BaseData>>,\n ) {\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(output, alignmentOf(property));\n writeData(output, property, value[key] as wgsl.BaseData);\n }\n\n alignIO(output, alignment);\n },\n\n array(output, schema: wgsl.WgslArray, value: Infer<wgsl.BaseData>[]) {\n if (schema.elementCount === 0) {\n throw new Error('Cannot write using a runtime-sized schema.');\n }\n\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n const beginning = output.currentByteOffset;\n for (let i = 0; i < Math.min(schema.elementCount, value.length); i++) {\n alignIO(output, alignment);\n writeData(output, schema.elementType, value[i]);\n }\n output.seekTo(beginning + sizeOf(schema));\n },\n\n ptrFn() {\n throw new Error('Pointers are not host-shareable');\n },\n\n atomic(output, schema: wgsl.Atomic, value: number) {\n dataWriters[schema.inner.type]?.(output, schema, value);\n },\n\n decorated(output, schema: wgsl.Decorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n dataWriters[(schema.inner as AnyData)?.type]?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n },\n\n // Loose Types\n\n uint8(output, _, value: number) {\n output.writeUint8(value);\n },\n uint8x2(output, _, value: wgsl.v2u) {\n output.writeUint8(value.x);\n output.writeUint8(value.y);\n },\n uint8x4(output, _, value: wgsl.v4u) {\n output.writeUint8(value.x);\n output.writeUint8(value.y);\n output.writeUint8(value.z);\n output.writeUint8(value.w);\n },\n sint8(output, _, value: number) {\n output.writeInt8(value);\n },\n sint8x2(output, _, value: wgsl.v2i) {\n output.writeInt8(value.x);\n output.writeInt8(value.y);\n },\n sint8x4(output, _, value: wgsl.v4i) {\n output.writeInt8(value.x);\n output.writeInt8(value.y);\n output.writeInt8(value.z);\n output.writeInt8(value.w);\n },\n unorm8(output, _, value: number) {\n output.writeUint8(value * 255);\n },\n unorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(value.x * 255);\n output.writeUint8(value.y * 255);\n },\n unorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(value.x * 255);\n output.writeUint8(value.y * 255);\n output.writeUint8(value.z * 255);\n output.writeUint8(value.w * 255);\n },\n snorm8(output, _, value: number) {\n output.writeUint8(value * 127 + 128);\n },\n snorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(value.x * 127 + 128);\n output.writeUint8(value.y * 127 + 128);\n },\n snorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(value.x * 127 + 128);\n output.writeUint8(value.y * 127 + 128);\n output.writeUint8(value.z * 127 + 128);\n output.writeUint8(value.w * 127 + 128);\n },\n uint16(output, _, value: number) {\n output.writeUint16(value);\n },\n uint16x2(output, _, value: wgsl.v2u) {\n output.writeUint16(value.x);\n output.writeUint16(value.y);\n },\n uint16x4(output, _, value: wgsl.v4u) {\n output.writeUint16(value.x);\n output.writeUint16(value.y);\n output.writeUint16(value.z);\n output.writeUint16(value.w);\n },\n sint16(output, _, value: number) {\n output.writeInt16(value);\n },\n sint16x2(output, _, value: wgsl.v2i) {\n output.writeInt16(value.x);\n output.writeInt16(value.y);\n },\n sint16x4(output, _, value: wgsl.v4i) {\n output.writeInt16(value.x);\n output.writeInt16(value.y);\n output.writeInt16(value.z);\n output.writeInt16(value.w);\n },\n unorm16(output, _, value: number) {\n output.writeUint16(value * 65535);\n },\n unorm16x2(output, _, value: wgsl.v2f) {\n output.writeUint16(value.x * 65535);\n output.writeUint16(value.y * 65535);\n },\n unorm16x4(output, _, value: wgsl.v4f) {\n output.writeUint16(value.x * 65535);\n output.writeUint16(value.y * 65535);\n output.writeUint16(value.z * 65535);\n output.writeUint16(value.w * 65535);\n },\n snorm16(output, _, value: number) {\n output.writeUint16(value * 32767 + 32768);\n },\n snorm16x2(output, _, value: wgsl.v2f) {\n output.writeUint16(value.x * 32767 + 32768);\n output.writeUint16(value.y * 32767 + 32768);\n },\n snorm16x4(output, _, value: wgsl.v4f) {\n output.writeUint16(value.x * 32767 + 32768);\n output.writeUint16(value.y * 32767 + 32768);\n output.writeUint16(value.z * 32767 + 32768);\n output.writeUint16(value.w * 32767 + 32768);\n },\n float16(output, _, value: number) {\n output.writeFloat16(value);\n },\n float16x2(output, _, value: wgsl.v2f) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n float16x4(output, _, value: wgsl.v4f) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n float32(output, _, value: number) {\n output.writeFloat32(value);\n },\n float32x2(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n float32x3(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n float32x4(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n uint32(output, _, value: number) {\n output.writeUint32(value);\n },\n uint32x2(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n uint32x3(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n uint32x4(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n sint32(output, _, value: number) {\n output.writeInt32(value);\n },\n sint32x2(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n sint32x3(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n sint32x4(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n 'unorm10-10-10-2'(output, _, value: wgsl.v4f) {\n let packed = 0;\n packed |= ((value.x * 1023) & 1023) << 22; // r (10 bits)\n packed |= ((value.x * 1023) & 1023) << 12; // g (10 bits)\n packed |= ((value.y * 1023) & 1023) << 2; // b (10 bits)\n packed |= (value.z * 3) & 3; // a (2 bits)\n output.writeUint32(packed);\n },\n 'unorm8x4-bgra'(output, _, value: wgsl.v4f) {\n output.writeUint8(value.z * 255);\n output.writeUint8(value.y * 255);\n output.writeUint8(value.x * 255);\n output.writeUint8(value.w * 255);\n },\n\n disarray(output, schema: Disarray, value: unknown[]) {\n const alignment = alignmentOf(schema);\n\n alignIO(output, alignment);\n const beginning = output.currentByteOffset;\n for (let i = 0; i < Math.min(schema.elementCount, value.length); i++) {\n alignIO(output, alignment);\n dataWriters[(schema.elementType as AnyData)?.type]?.(\n output,\n schema.elementType,\n value[i],\n );\n }\n\n output.seekTo(beginning + sizeOf(schema));\n },\n\n unstruct(output, schema: Unstruct, value) {\n for (const [key, property] of Object.entries(schema.propTypes)) {\n dataWriters[property.type]?.(output, property, value[key]);\n }\n },\n\n 'loose-decorated'(output, schema: LooseDecorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n const writer = dataWriters[(schema.inner as AnyData)?.type];\n writer?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataWriters as Record<\n string,\n (output: ISerialOutput, schema: unknown, value: unknown) => void\n>;\n\nexport function writeData<TData extends wgsl.BaseData>(\n output: ISerialOutput,\n schema: TData,\n value: Infer<TData>,\n): void {\n const writer = dataWriters[schema.type];\n if (!writer) {\n throw new Error(`Cannot write data of type '${schema.type}'.`);\n }\n\n writer(output, schema, value);\n}\n\nconst dataReaders = {\n bool(input: ISerialInput): boolean {\n return input.readBool();\n },\n\n f32(input: ISerialInput): number {\n return input.readFloat32();\n },\n\n f16(input: ISerialInput): number {\n return input.readFloat16();\n },\n\n i32(input: ISerialInput): number {\n return input.readInt32();\n },\n\n u32(input: ISerialInput): number {\n return input.readUint32();\n },\n\n vec2f(input: ISerialInput): wgsl.v2f {\n return vec2f(input.readFloat32(), input.readFloat32());\n },\n\n vec3f(input: ISerialInput): wgsl.v3f {\n return vec3f(input.readFloat32(), input.readFloat32(), input.readFloat32());\n },\n\n vec4f(input: ISerialInput): wgsl.v4f {\n return vec4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n vec2h(input): wgsl.v2h {\n return vec2h(input.readFloat16(), input.readFloat16());\n },\n\n vec3h(input: ISerialInput): wgsl.v3h {\n return vec3h(input.readFloat16(), input.readFloat16(), input.readFloat16());\n },\n\n vec4h(input: ISerialInput): wgsl.v4h {\n return vec4h(\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n );\n },\n\n vec2i(input): wgsl.v2i {\n return vec2i(input.readInt32(), input.readInt32());\n },\n\n vec3i(input: ISerialInput): wgsl.v3i {\n return vec3i(input.readInt32(), input.readInt32(), input.readInt32());\n },\n\n vec4i(input: ISerialInput): wgsl.v4i {\n return vec4i(\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n );\n },\n\n vec2u(input): wgsl.v2u {\n return vec2u(input.readUint32(), input.readUint32());\n },\n\n vec3u(input: ISerialInput): wgsl.v3u {\n return vec3u(input.readUint32(), input.readUint32(), input.readUint32());\n },\n\n vec4u(input: ISerialInput): wgsl.v4u {\n return vec4u(\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n );\n },\n\n mat2x2f(input: ISerialInput): wgsl.m2x2f {\n return mat2x2f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n mat3x3f(input: ISerialInput): wgsl.m3x3f {\n const skipOneAfter = () => {\n const value = input.readFloat32();\n input.readFloat32(); // skipping;\n return value;\n };\n\n return mat3x3f(\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n );\n },\n\n mat4x4f(input: ISerialInput): wgsl.m4x4f {\n return mat4x4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n struct(input: ISerialInput, schema: WgslStruct) {\n const alignment = alignmentOf(schema);\n alignIO(input, alignment);\n const result = {} as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(input, alignmentOf(property));\n result[key] = readData(input, property);\n }\n\n alignIO(input, alignment);\n return result as InferRecord<Record<string, wgsl.BaseData>>;\n },\n\n array(input, schema) {\n if (schema.elementCount === 0) {\n throw new Error('Cannot read using a runtime-sized schema.');\n }\n\n const alignment = alignmentOf(schema);\n const elements: unknown[] = [];\n\n for (let i = 0; i < schema.elementCount; i++) {\n alignIO(input, alignment);\n const elementType = schema.elementType as wgsl.AnyWgslData;\n const value = readData(input, elementType);\n elements.push(value);\n }\n\n alignIO(input, alignment);\n return elements as never[];\n },\n\n ptrFn() {\n throw new Error('Pointers are not host-shareable');\n },\n\n atomic(input, schema: wgsl.Atomic): number {\n return readData(input, schema.inner);\n },\n\n decorated(input, schema: wgsl.Decorated) {\n const alignment = customAlignmentOf(schema);\n alignIO(input, alignment);\n\n const beginning = input.currentByteOffset;\n const value = readData(input, schema.inner);\n input.seekTo(beginning + sizeOf(schema));\n return value as never;\n },\n\n // Loose Types\n\n uint8: (i) => i.readUint8(),\n uint8x2: (i) => vec2u(i.readUint8(), i.readUint8()),\n uint8x4: (i) =>\n vec4u(i.readUint8(), i.readUint8(), i.readUint8(), i.readUint8()),\n sint8: (i) => i.readInt8(),\n sint8x2: (i) => {\n return vec2i(i.readInt8(), i.readInt8());\n },\n sint8x4: (i) => vec4i(i.readInt8(), i.readInt8(), i.readInt8(), i.readInt8()),\n unorm8: (i) => i.readUint8() / 255,\n unorm8x2: (i) => vec2f(i.readUint8() / 255, i.readUint8() / 255),\n unorm8x4: (i) =>\n vec4f(\n i.readUint8() / 255,\n i.readUint8() / 255,\n i.readUint8() / 255,\n i.readUint8() / 255,\n ),\n snorm8: (i) => (i.readUint8() - 128) / 127,\n snorm8x2: (i) =>\n vec2f((i.readUint8() - 128) / 127, (i.readUint8() - 128) / 127),\n snorm8x4: (i) =>\n vec4f(\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n ),\n uint16: (i) => i.readUint16(),\n uint16x2: (i) => vec2u(i.readUint16(), i.readUint16()),\n uint16x4: (i) =>\n vec4u(i.readUint16(), i.readUint16(), i.readUint16(), i.readUint16()),\n sint16: (i) => i.readInt16(),\n sint16x2: (i) => vec2i(i.readInt16(), i.readInt16()),\n sint16x4: (i) =>\n vec4i(i.readInt16(), i.readInt16(), i.readInt16(), i.readInt16()),\n unorm16: (i) => i.readUint16() / 65535,\n unorm16x2: (i) => vec2f(i.readUint16() / 65535, i.readUint16() / 65535),\n unorm16x4: (i) =>\n vec4f(\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n ),\n snorm16: (i) => (i.readUint16() - 32768) / 32767,\n snorm16x2: (i): wgsl.v2f =>\n vec2f(dataReaders.snorm16(i), dataReaders.snorm16(i)),\n snorm16x4: (i): wgsl.v4f =>\n vec4f(\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n ),\n float16(i) {\n return i.readFloat16();\n },\n float16x2: (i) => vec2f(i.readFloat16(), i.readFloat16()),\n float16x4: (i) =>\n vec4f(i.readFloat16(), i.readFloat16(), i.readFloat16(), i.readFloat16()),\n float32: (i) => i.readFloat32(),\n float32x2: (i) => vec2f(i.readFloat32(), i.readFloat32()),\n float32x3: (i) => vec3f(i.readFloat32(), i.readFloat32(), i.readFloat32()),\n float32x4: (i) =>\n vec4f(i.readFloat32(), i.readFloat32(), i.readFloat32(), i.readFloat32()),\n uint32: (i) => i.readUint32(),\n uint32x2: (i) => vec2u(i.readUint32(), i.readUint32()),\n uint32x3: (i) => vec3u(i.readUint32(), i.readUint32(), i.readUint32()),\n uint32x4: (i) =>\n vec4u(i.readUint32(), i.readUint32(), i.readUint32(), i.readUint32()),\n sint32: (i) => i.readInt32(),\n sint32x2: (i) => vec2i(i.readInt32(), i.readInt32()),\n sint32x3: (i) => vec3i(i.readInt32(), i.readInt32(), i.readInt32()),\n sint32x4: (i) =>\n vec4i(i.readInt32(), i.readInt32(), i.readInt32(), i.readInt32()),\n 'unorm10-10-10-2'(i) {\n const packed = i.readUint32();\n const r = (packed >> 22) / 1023;\n const g = ((packed >> 12) & 1023) / 1023;\n const b = ((packed >> 2) & 1023) / 1023;\n const a = (packed & 3) / 3;\n return vec4f(r, g, b, a);\n },\n 'unorm8x4-bgra'(i) {\n const b = i.readByte() / 255;\n const g = i.readByte() / 255;\n const r = i.readByte() / 255;\n const a = i.readByte() / 255;\n return vec4f(r, g, b, a);\n },\n\n unstruct(input, schema: Unstruct) {\n const result = {} as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n result[key] = readData(input, property);\n }\n\n return result as InferRecord<Record<string, wgsl.BaseData>>;\n },\n\n disarray(input, schema: Disarray) {\n const alignment = alignmentOf(schema);\n const elements: unknown[] = [];\n\n for (let i = 0; i < schema.elementCount; i++) {\n alignIO(input, alignment);\n elements.push(readData(input, schema.elementType));\n }\n\n alignIO(input, alignment);\n return elements;\n },\n\n 'loose-decorated'(input, schema: LooseDecorated) {\n alignIO(input, customAlignmentOf(schema));\n\n const beginning = input.currentByteOffset;\n const value = readData(input, schema.inner);\n input.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataReaders;\n\nexport function readData<TData extends wgsl.BaseData>(\n input: ISerialInput,\n schema: TData,\n): Infer<TData> {\n const reader = (dataReaders as Record<string, unknown>)[\n schema.type\n ] as DataReader<TData>;\n if (!reader) {\n throw new Error(`Cannot read data of type '${schema.type}'.`);\n }\n\n return reader(input, schema);\n}\n","import type { Block } from 'tinyest';\nimport { resolveData } from './core/resolve/resolveData';\nimport {\n type Eventual,\n type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n isDerived,\n isProviding,\n isSlot,\n} from './core/slot/slotTypes';\nimport { isLooseData } from './data';\nimport { getAttributesString } from './data/attributes';\nimport {\n type AnyWgslData,\n type BaseData,\n isWgslArray,\n isWgslData,\n isWgslStruct,\n} from './data/wgslTypes';\nimport { MissingSlotValueError, ResolutionError } from './errors';\nimport { provideCtx } from './gpuMode';\nimport type { JitTranspiler } from './jitTranspiler';\nimport type { NameRegistry } from './nameRegistry';\nimport { naturalsExcept } from './shared/generators';\nimport type { Infer } from './shared/repr';\nimport { generateFunction } from './smol';\nimport {\n type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n bindGroupLayout,\n} from './tgpuBindGroupLayout';\nimport type { FnToWgslOptions, ResolutionCtx, Resource, Wgsl } from './types';\nimport { UnknownData, isSelfResolvable, isWgsl } from './types';\n\n/**\n * Inserted into bind group entry definitions that belong\n * to the automatically generated catch-all bind group.\n *\n * A non-occupied group index can only be determined after\n * every resource has been resolved, so this acts as a placeholder\n * to be replaced with an actual numeric index at the very end\n * of the resolution process.\n */\nconst CATCHALL_BIND_GROUP_IDX_MARKER = '#CATCHALL#';\n\nexport type ResolutionCtxImplOptions = {\n readonly names: NameRegistry;\n readonly jitTranspiler?: JitTranspiler | undefined;\n};\n\ntype SlotToValueMap = Map<TgpuSlot<unknown>, unknown>;\n\ntype ItemLayer = {\n type: 'item';\n usedSlots: Set<TgpuSlot<unknown>>;\n};\n\ntype SlotBindingLayer = {\n type: 'slotBinding';\n bindingMap: WeakMap<TgpuSlot<unknown>, unknown>;\n};\n\ntype FunctionScopeLayer = {\n type: 'functionScope';\n args: Resource[];\n externalMap: Record<string, unknown>;\n returnType: AnyWgslData | undefined;\n};\n\ntype BlockScopeLayer = {\n type: 'blockScope';\n declarations: Map<string, AnyWgslData | UnknownData>;\n};\n\nclass 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 pushSlotBindings(pairs: SlotValuePair<unknown>[]) {\n this._stack.push({\n type: 'slotBinding',\n bindingMap: new WeakMap(pairs),\n });\n }\n\n pushFunctionScope(\n args: Resource[],\n returnType: AnyWgslData | undefined,\n externalMap: Record<string, unknown>,\n ) {\n this._stack.push({\n type: 'functionScope',\n args,\n returnType,\n externalMap,\n });\n }\n\n pop() {\n const layer = this._stack.pop();\n if (layer?.type === 'item') {\n this._itemDepth--;\n }\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n if (layer?.type === 'item') {\n // Binding not available yet, so this layer is dependent on the slot's value.\n layer.usedSlots.add(slot);\n } else if (layer?.type === 'slotBinding') {\n const boundValue = layer.bindingMap.get(slot);\n\n if (boundValue !== undefined) {\n return boundValue as T;\n }\n } else if (\n layer?.type === 'functionScope' ||\n layer?.type === 'blockScope'\n ) {\n // Skip\n } else {\n throw new Error('Unknown layer type.');\n }\n }\n\n return slot.defaultValue;\n }\n\n getResourceById(id: string): Resource | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'functionScope') {\n const arg = layer.args.find((a) => a.value === id);\n if (arg !== undefined) {\n return arg;\n }\n\n const external = layer.externalMap[id];\n if (external !== undefined) {\n // TODO: Extract the type of the external value.\n return { value: external, dataType: UnknownData };\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\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\nclass 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 ItemStateStack();\n private readonly _declarations: string[] = [];\n\n // -- Bindings\n /**\n * A map from registered bind group layouts to random strings put in\n * place of their group index. The whole tree has to be traversed to\n * collect every use of a typed bind group layout, since they can be\n * explicitly imposed group indices, and they cannot collide.\n */\n public readonly bindGroupLayoutsToPlaceholderMap = new Map<\n TgpuBindGroupLayout,\n string\n >();\n private _nextFreeLayoutPlaceholderIdx = 0;\n public readonly fixedBindings: FixedBindingConfig[] = [];\n // --\n\n public readonly callStack: unknown[] = [];\n public readonly names: NameRegistry;\n\n constructor(opts: ResolutionCtxImplOptions) {\n this.names = opts.names;\n this._jitTranspiler = opts.jitTranspiler;\n }\n\n get pre(): string {\n return this._indentController.pre;\n }\n\n indent(): string {\n return this._indentController.indent();\n }\n\n dedent(): string {\n return this._indentController.dedent();\n }\n\n getById(id: string): Resource {\n // TODO: Provide access to external values\n // TODO: Provide data type information\n return (\n this._itemStateStack.getResourceById(id) ?? {\n value: id,\n dataType: UnknownData,\n }\n );\n }\n\n transpileFn(fn: string): {\n argNames: string[];\n body: Block;\n externalNames: string[];\n } {\n if (!this._jitTranspiler) {\n throw new Error(\n 'Tried to execute a tgpu.fn function without providing a JIT transpiler, or transpiling at build time.',\n );\n }\n\n return this._jitTranspiler.transpileFn(fn);\n }\n\n fnToWgsl(options: FnToWgslOptions): { head: Wgsl; body: Wgsl } {\n this._itemStateStack.pushFunctionScope(\n options.args,\n options.returnType,\n options.externalMap,\n );\n const str = generateFunction(this, options.body);\n this._itemStateStack.pop();\n\n const argList = options.args\n .map(\n (arg) => `${arg.value}: ${this.resolve(arg.dataType as AnyWgslData)}`,\n )\n .join(', ');\n\n return {\n head:\n options.returnType !== undefined\n ? `(${argList}) -> ${getAttributesString(options.returnType)} ${this.resolve(options.returnType)}`\n : `(${argList})`,\n body: str,\n };\n }\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.pop();\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 const result = derived['~compute']();\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\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.pop();\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 (isWgslData(item) || isLooseData(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.pop();\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 const result = provideCtx(this, () => this._getOrInstantiate(item));\n return `${[...this._declarations].join('\\n\\n')}${result}`;\n }\n\n return this._getOrInstantiate(item);\n }\n\n return String(item);\n }\n\n resolveValue<T extends BaseData>(\n value: Infer<T>,\n schema?: T | undefined,\n ): string {\n if (isWgsl(value)) {\n return this.resolve(value);\n }\n\n if (schema && isWgslArray(schema)) {\n return `array(${(value as unknown[]).map((element) => this.resolveValue(element, schema.elementType))})`;\n }\n\n if (Array.isArray(value)) {\n return `array(${value.map((element) => this.resolveValue(element))})`;\n }\n\n if (schema && isWgslStruct(schema)) {\n return `${this.resolve(schema)}(${Object.entries(schema.propTypes).map(([key, type_]) => this.resolveValue((value as Infer<typeof schema>)[key], type_))})`;\n }\n\n throw new Error(\n `Value ${value} (as json: ${JSON.stringify(value)}) of schema ${schema} is not resolvable to WGSL`,\n );\n }\n}\n\nexport interface ResolutionResult {\n code: string;\n bindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | null;\n}\n\nexport function resolve(\n item: Wgsl,\n options: ResolutionCtxImplOptions,\n): ResolutionResult {\n const ctx = new ResolutionCtxImpl(options);\n let code = ctx.resolve(item);\n\n const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;\n const bindGroupLayouts: TgpuBindGroupLayout[] = [];\n const takenIndices = new Set<number>(\n [...memoMap.keys()]\n .map((layout) => layout.index)\n .filter((v): v is number => v !== undefined),\n );\n\n const automaticIds = naturalsExcept(takenIndices);\n\n const layoutEntries = ctx.fixedBindings.map(\n (binding, idx) =>\n [String(idx), binding.layoutEntry] as [string, TgpuLayoutEntry],\n );\n\n const createCatchallGroup = () => {\n const catchallIdx = automaticIds.next().value;\n const catchallLayout = bindGroupLayout(Object.fromEntries(layoutEntries));\n bindGroupLayouts[catchallIdx] = catchallLayout;\n code = code.replaceAll(CATCHALL_BIND_GROUP_IDX_MARKER, String(catchallIdx));\n\n return [\n catchallIdx,\n new TgpuBindGroupImpl(\n catchallLayout,\n Object.fromEntries(\n ctx.fixedBindings.map(\n (binding, idx) =>\n // biome-ignore lint/suspicious/noExplicitAny: <it's fine>\n [String(idx), binding.resource] as [string, any],\n ),\n ),\n ),\n ] as [number, TgpuBindGroup];\n };\n\n // Retrieving the catch-all binding index first, because it's inherently\n // the least swapped bind group (fixed and cannot be swapped).\n const catchall = layoutEntries.length > 0 ? createCatchallGroup() : null;\n\n for (const [layout, placeholder] of memoMap.entries()) {\n const idx = layout.index ?? automaticIds.next().value;\n bindGroupLayouts[idx] = layout;\n code = code.replaceAll(placeholder, String(idx));\n }\n\n return {\n code,\n bindGroupLayouts,\n catchall,\n };\n}\n","import type { JitTranspiler } from '../../jitTranspiler';\nimport { RandomNameRegistry, StrictNameRegistry } from '../../nameRegistry';\nimport { resolve as resolveImpl } from '../../resolutionCtx';\nimport type { SelfResolvable, Wgsl } from '../../types';\nimport { applyExternals, replaceExternalsInWgsl } from './externals';\n\nexport interface TgpuResolveOptions {\n /**\n * Map of external names to their resolvable values.\n */\n externals: Record<string, Wgsl | object>;\n /**\n * The code template to use for the resolution. All external names will be replaced with their resolved values.\n * @default ''\n */\n template?: string | undefined;\n /**\n * The naming strategy used for generating identifiers for resolved externals and their dependencies.\n * @default 'random'\n */\n names?: 'strict' | 'random' | undefined;\n /**\n * Optional JIT transpiler for resolving TGSL functions.\n * @experimental\n */\n unstable_jitTranspiler?: JitTranspiler | undefined;\n}\n\n/**\n * Resolves a template with external values. Each external will get resolved to a code string and replaced in the template.\n * Any dependencies of the externals will also be resolved and included in the output.\n * @param options - The options for the resolution.\n *\n * @returns The resolved code.\n *\n * @example\n * ```ts\n * const Gradient = d.struct({\n * from: d.vec3f,\n * to: d.vec3f,\n * });\n *\n * const resolved = tgpu.resolve({\n * template: `\n * fn getGradientAngle(gradient: Gradient) -> f32 {\n * return atan(gradient.to.y - gradient.from.y, gradient.to.x - gradient.from.x);\n * }\n * `,\n * externals: {\n * Gradient,\n * },\n * });\n *\n * console.log(resolved);\n * // struct Gradient_0 {\n * // from: vec3f,\n * // to: vec3f,\n * // }\n * // fn getGradientAngle(gradient: Gradient_0) -> f32 {\n * // return atan(gradient.to.y - gradient.from.y, gradient.to.x - gradient.from.x);\n * // }\n * ```\n */\nexport function resolve(options: TgpuResolveOptions): string {\n const {\n externals,\n template,\n names,\n unstable_jitTranspiler: jitTranspiler,\n } = options;\n\n const dependencies = {} as Record<string, Wgsl>;\n applyExternals(dependencies, externals ?? {});\n\n const resolutionObj: SelfResolvable = {\n '~resolve'(ctx) {\n return replaceExternalsInWgsl(ctx, dependencies, template ?? '');\n },\n\n toString: () => '<root>',\n };\n\n const { code } = resolveImpl(resolutionObj, {\n names:\n names === 'strict' ? new StrictNameRegistry() : new RandomNameRegistry(),\n jitTranspiler,\n });\n\n return code;\n}\n"]}
|
1
|
+
{"version":3,"sources":["/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","../src/core/slot/slotTypes.ts","../src/types.ts","../src/core/valueProxyUtils.ts","../src/core/resolve/resolveData.ts","../src/smol/wgslGenerator.ts","../src/data/dataIO.ts","../src/resolutionCtx.ts","../src/core/resolve/tgpuResolve.ts"],"names":["isSlot","value","isDerived","isProviding","isAccessor","UnknownData","isSelfResolvable","isWgsl","isWgslData","isGPUBuffer","isBufferUsage","valueProxyHandler","target","prop","ctx","op","output","code"],"mappings":"AAAA,iIAA8P,wDAAiR,SCiE/fA,CAAAA,CAAUC,CAAAA,CAAoD,CAC5E,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAuB,YAAA,CAAA,GAAiB,MAClD,CAEO,SAASC,CAAAA,CACdD,CAAAA,CACY,CACZ,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAa,YAAA,CAAA,GAAiB,SACxC,CAEO,SAASE,EAAAA,CACdF,CAAAA,CACsC,CACtC,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAwC,YAAA,CAAA,CAAA,GAAkB,KAAA,CACpE,CAEO,SAASG,EAAAA,CACdH,CAAAA,CAC0B,CAC1B,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAA2B,YAAA,CAAA,GAAiB,UACtD,CChBO,IAAMI,CAAAA,CAAc,CACzB,IAAA,CAAM,SACR,CAAA,CAiFO,SAASC,EAAAA,CAAiBL,CAAAA,CAAyC,CACxE,OAAO,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAA2B,UAAA,CAAA,CAAA,EAAgB,UAC5D,CAEO,SAASM,EAAAA,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,MAAA,CAAQA,CAAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAAA,CAAAA,CAAa,YAAA,CAAA,GAAiB,cACxC,CCpLO,IAAMU,CAAAA,CAA6D,CACxE,GAAA,CAAIC,CAAAA,CAAQC,CAAAA,CAAM,CAChB,EAAA,CAAIA,EAAAA,GAAQD,CAAAA,CACV,OAAO,OAAA,CAAQ,GAAA,CAAIA,CAAAA,CAAQC,CAAI,CAAA,CAGjC,EAAA,CAAIA,CAAAA,GAAS,YAAA,CAIb,OAAO,IAAI,KAAA,CACT,CACE,UAAA,CAAaC,CAAAA,EACX,CAAA,EAAA;AC+FW;AACV,OAAA;AAGA;AAwBU;AACV,OAAA;AAOA;AAAA;ACvGPC;AAkDiE;AAAK;AA6JtE;AA0IQ;AACH;AAPgB;AAkE8B;AAaxB;ACxUvBC;AC8ayC;ACvevCC","file":"/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import type { AnyWgslData } from '../../data';\nimport type { TgpuNamable } from '../../namable';\nimport type { Infer } from '../../shared/repr';\nimport type { Labelled } from '../../types';\nimport type { TgpuFn } from '../function/tgpuFn';\nimport type { TgpuBufferUsage } from './../buffer/bufferUsage';\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 { Block } from 'tinyest';\nimport type {\n TgpuBufferMutable,\n TgpuBufferReadonly,\n TgpuBufferUniform,\n TgpuBufferUsage,\n} from './core/buffer/bufferUsage';\nimport type { TgpuConst } from './core/constant/tgpuConstant';\nimport type { TgpuDeclare } from './core/declare/tgpuDeclare';\nimport type { TgpuComputeFn } from './core/function/tgpuComputeFn';\nimport type { TgpuFn } from './core/function/tgpuFn';\nimport type { TgpuFragmentFn } from './core/function/tgpuFragmentFn';\nimport type { TgpuVertexFn } from './core/function/tgpuVertexFn';\nimport type { TgpuComputePipeline } from './core/pipeline/computePipeline';\nimport type { TgpuRenderPipeline } from './core/pipeline/renderPipeline';\nimport type { TgpuSampler } from './core/sampler/sampler';\nimport {\n type Eventual,\n type SlotValuePair,\n type TgpuAccessor,\n isDerived,\n isProviding,\n isSlot,\n} from './core/slot/slotTypes';\nimport type { TgpuExternalTexture } from './core/texture/externalTexture';\nimport type { TgpuAnyTextureView, TgpuTexture } from './core/texture/texture';\nimport type { TgpuVar } from './core/variable/tgpuVariable';\nimport type { AnyData } from './data';\nimport {\n type AbstractFloat,\n type AbstractInt,\n type AnyMatInstance,\n type AnyVecInstance,\n type AnyWgslData,\n type BaseData,\n isWgslData,\n} from './data/wgslTypes';\nimport type { NameRegistry } from './nameRegistry';\nimport type { Infer } from './shared/repr';\nimport type {\n TgpuBindGroupLayout,\n TgpuLayoutEntry,\n} from './tgpuBindGroupLayout';\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',\n};\nexport type UnknownData = typeof UnknownData;\n\nexport type Resource = {\n value: unknown;\n dataType: AnyWgslData | UnknownData | AbstractInt | AbstractFloat;\n};\n\nexport type TgpuShaderStage = 'compute' | 'vertex' | 'fragment';\n\nexport interface FnToWgslOptions {\n args: Resource[];\n returnType: AnyWgslData;\n body: Block;\n externalMap: Record<string, unknown>;\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: string[];\n body: Block;\n externalNames: string[];\n };\n fnToWgsl(options: FnToWgslOptions): {\n head: Wgsl;\n body: Wgsl;\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","import {\n type Labelled,\n type ResolutionCtx,\n type SelfResolvable,\n isBufferUsage,\n} from '../types';\nimport { isAccessor, isDerived, isSlot } from './slot/slotTypes';\n\nexport const valueProxyHandler: ProxyHandler<SelfResolvable & Labelled> = {\n get(target, prop) {\n if (prop in target) {\n return Reflect.get(target, prop);\n }\n\n if (prop === '~providing') {\n return undefined;\n }\n\n return new Proxy(\n {\n '~resolve': (ctx: ResolutionCtx) =>\n `${ctx.resolve(target)}.${String(prop)}`,\n\n toString: () =>\n `.value(...).${String(prop)}:${target.label ?? '<unnamed>'}`,\n },\n valueProxyHandler,\n );\n },\n};\n\nexport function unwrapProxy<T>(value: unknown): T {\n let unwrapped = value;\n\n while (\n isSlot(unwrapped) ||\n isDerived(unwrapped) ||\n isAccessor(unwrapped) ||\n isBufferUsage(unwrapped)\n ) {\n unwrapped = unwrapped.value;\n }\n\n return unwrapped as T;\n}\n","import { getAttributesString } from '../../data/attributes';\nimport {\n type AnyData,\n type Disarray,\n type Unstruct,\n isLooseData,\n} from '../../data/dataTypes';\nimport type { WgslStruct } from '../../data/struct';\nimport { formatToWGSLType } from '../../data/vertexFormatData';\nimport type {\n AnyWgslData,\n BaseData,\n Bool,\n F16,\n F32,\n I32,\n Mat2x2f,\n Mat3x3f,\n Mat4x4f,\n U32,\n Vec2f,\n Vec2h,\n Vec2i,\n Vec2u,\n Vec3f,\n Vec3h,\n Vec3i,\n Vec3u,\n Vec4f,\n Vec4h,\n Vec4i,\n Vec4u,\n WgslArray,\n} from '../../data/wgslTypes';\nimport { assertExhaustive } from '../../shared/utilityTypes';\nimport type { ResolutionCtx } from '../../types';\nimport { isAttribute } from '../vertexLayout/connectAttributesToShader';\n\n/**\n * Schemas for which their `type` property directly\n * translates to the resulting WGSL code.\n */\nconst identityTypes = [\n 'bool',\n 'f32',\n 'f16',\n 'i32',\n 'u32',\n 'vec2f',\n 'vec3f',\n 'vec4f',\n 'vec2h',\n 'vec3h',\n 'vec4h',\n 'vec2i',\n 'vec3i',\n 'vec4i',\n 'vec2u',\n 'vec3u',\n 'vec4u',\n 'mat2x2f',\n 'mat3x3f',\n 'mat4x4f',\n];\n\ntype IdentityType =\n | Bool\n | F32\n | F16\n | I32\n | U32\n | Vec2f\n | Vec3f\n | Vec4f\n | Vec2h\n | Vec3h\n | Vec4h\n | Vec2i\n | Vec3i\n | Vec4i\n | Vec2u\n | Vec3u\n | Vec4u\n | Mat2x2f\n | Mat3x3f\n | Mat4x4f;\n\nfunction isIdentityType(data: AnyWgslData): data is IdentityType {\n return identityTypes.includes(data.type);\n}\n\n/**\n * Resolves a single property of a struct.\n * @param ctx - The resolution context.\n * @param key - The key of the property.\n * @param property - The property itself.\n *\n * @returns The resolved property string.\n */\nfunction resolveStructProperty(\n ctx: ResolutionCtx,\n [key, property]: [string, BaseData],\n) {\n return ` ${getAttributesString(property)}${key}: ${ctx.resolve(property as AnyWgslData)},\\n`;\n}\n\n/**\n * Resolves a struct and adds its declaration to the resolution context.\n * @param ctx - The resolution context.\n * @param struct - The struct to resolve.\n *\n * @returns The resolved struct name.\n */\nfunction resolveStruct(ctx: ResolutionCtx, struct: WgslStruct) {\n const id = ctx.names.makeUnique(struct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(struct.propTypes)\n .map((prop) => resolveStructProperty(ctx, prop))\n .join('')}\\\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an unstruct (struct that does not align data by default) to its struct data counterpart.\n * @param ctx - The resolution context.\n * @param unstruct - The unstruct to resolve.\n *\n * @returns The resolved unstruct name.\n *\n * @example\n * ```ts\n * resolveUnstruct(ctx, {\n * uv: d.float16x2, // -> d.vec2f after resolution\n * color: d.snorm8x4, -> d.vec4f after resolution\n * });\n * ```\n */\nfunction resolveUnstruct(ctx: ResolutionCtx, unstruct: Unstruct) {\n const id = ctx.names.makeUnique(unstruct.label);\n\n ctx.addDeclaration(`\nstruct ${id} {\n${Object.entries(unstruct.propTypes)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [prop[0], formatToWGSLType[prop[1].format]])\n : resolveStructProperty(ctx, prop),\n )\n .join('')}\n}\\n`);\n\n return id;\n}\n\n/**\n * Resolves an array.\n * @param ctx - The resolution context.\n * @param array - The array to resolve.\n *\n * @returns The resolved array name along with its element type and count (if not runtime-sized).\n *\n * @example\n * ```ts\n * resolveArray(ctx, d.arrayOf(d.u32, 0)); // 'array<u32>' (not a real pattern, a function is preferred)\n * resolveArray(ctx, d.arrayOf(d.u32, 5)); // 'array<u32, 5>'\n * ```\n */\nfunction resolveArray(ctx: ResolutionCtx, array: WgslArray) {\n const element = ctx.resolve(array.elementType as AnyWgslData);\n\n return array.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${array.elementCount}>`;\n}\n\nfunction resolveDisarray(ctx: ResolutionCtx, disarray: Disarray) {\n const element = ctx.resolve(\n isAttribute(disarray.elementType)\n ? formatToWGSLType[disarray.elementType.format]\n : (disarray.elementType as AnyWgslData),\n );\n\n return disarray.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${disarray.elementCount}>`;\n}\n\n/**\n * Resolves a WGSL data-type schema to a string.\n * @param ctx - The resolution context.\n * @param data - The data-type to resolve.\n *\n * @returns The resolved data-type string.\n */\nexport function resolveData(ctx: ResolutionCtx, data: AnyData): string {\n if (isLooseData(data)) {\n if (data.type === 'unstruct') {\n return resolveUnstruct(ctx, data);\n }\n\n if (data.type === 'disarray') {\n return resolveDisarray(ctx, data);\n }\n\n if (data.type === 'loose-decorated') {\n return ctx.resolve(\n isAttribute(data.inner)\n ? formatToWGSLType[data.inner.format]\n : data.inner,\n );\n }\n\n return ctx.resolve(formatToWGSLType[data.type]);\n }\n\n if (isIdentityType(data)) {\n return data.type;\n }\n\n if (data.type === 'struct') {\n return resolveStruct(ctx, data);\n }\n\n if (data.type === 'array') {\n return resolveArray(ctx, data);\n }\n\n if (data.type === 'atomic') {\n return `atomic<${resolveData(ctx, data.inner)}>`;\n }\n\n if (data.type === 'decorated') {\n return ctx.resolve(data.inner as AnyWgslData);\n }\n\n if (data.type === 'ptr') {\n if (data.addressSpace === 'storage') {\n return `ptr<storage, ${ctx.resolve(data.inner)}, ${data.access === 'read-write' ? 'read_write' : data.access}>`;\n }\n return `ptr<${data.addressSpace}, ${ctx.resolve(data.inner)}>`;\n }\n\n if (data.type === 'abstractInt' || data.type === 'abstractFloat') {\n throw new Error('Abstract types have no concrete representation in WGSL');\n }\n\n assertExhaustive(data, 'resolveData');\n}\n","import type * as smol from 'tinyest';\nimport * as d from '../data';\nimport { abstractFloat, abstractInt } from '../data/numeric';\nimport * as wgsl from '../data/wgslTypes';\nimport {\n type ResolutionCtx,\n type Resource,\n UnknownData,\n type Wgsl,\n isWgsl,\n} from '../types';\n\nconst parenthesizedOps = [\n '==',\n '!=',\n '<',\n '<=',\n '>',\n '>=',\n '<<',\n '>>',\n '+',\n '-',\n '*',\n '/',\n '%',\n '|',\n '^',\n '&',\n '&&',\n '||',\n];\n\nfunction binaryOperatorToType<\n TL extends wgsl.AnyWgslData | UnknownData,\n TR extends wgsl.AnyWgslData | UnknownData,\n>(\n lhs: TL,\n op: smol.BinaryOperator | smol.AssignmentOperator | smol.LogicalOperator,\n rhs: TR,\n): TL | TR | wgsl.Bool {\n if (\n op === '==' ||\n op === '!=' ||\n op === '<' ||\n op === '<=' ||\n op === '>' ||\n op === '>=' ||\n op === '&&' ||\n op === '||'\n ) {\n return d.bool;\n }\n\n if (op === '=') {\n return rhs;\n }\n\n return lhs;\n}\n\nexport type GenerationCtx = ResolutionCtx & {\n readonly pre: string;\n readonly callStack: unknown[];\n indent(): string;\n dedent(): string;\n pushBlockScope(): void;\n popBlockScope(): void;\n getById(id: string): Resource | null;\n defineVariable(\n id: string,\n dataType: wgsl.AnyWgslData | UnknownData,\n ): Resource;\n};\n\nexport function resolveRes(ctx: GenerationCtx, res: Resource): string {\n if (isWgsl(res.value) || wgsl.isWgslData(res.value)) {\n return ctx.resolve(res.value);\n }\n\n return String(res.value);\n}\n\nfunction assertExhaustive(value: never): never {\n throw new Error(\n `'${JSON.stringify(value)}' was not handled by the WGSL generator.`,\n );\n}\n\nexport function generateBoolean(ctx: GenerationCtx, value: boolean): Resource {\n return value\n ? { value: 'true', dataType: d.bool }\n : { value: 'false', dataType: d.bool };\n}\n\nexport function generateBlock(ctx: GenerationCtx, value: smol.Block): string {\n ctx.pushBlockScope();\n try {\n return `${ctx.indent()}{\n${value.b.map((statement) => generateStatement(ctx, statement)).join('\\n')}\n${ctx.dedent()}}`;\n } finally {\n ctx.popBlockScope();\n }\n}\n\nexport function registerBlockVariable(\n ctx: GenerationCtx,\n id: string,\n dataType: wgsl.AnyWgslData | UnknownData,\n): Resource {\n return ctx.defineVariable(id, dataType);\n}\n\nexport function generateIdentifier(ctx: GenerationCtx, id: string): Resource {\n const res = ctx.getById(id);\n if (!res) {\n throw new Error(`Identifier ${id} not found`);\n }\n\n return res;\n}\n\nexport function generateExpression(\n ctx: GenerationCtx,\n expression: smol.Expression,\n): Resource {\n if (typeof expression === 'string') {\n return generateIdentifier(ctx, expression);\n }\n\n if (typeof expression === 'boolean') {\n return generateBoolean(ctx, expression);\n }\n\n if ('x' in expression) {\n // Logical/Binary/Assignment Expression\n\n const [lhs, op, rhs] = expression.x;\n const lhsExpr = generateExpression(ctx, lhs);\n const lhsStr = resolveRes(ctx, lhsExpr);\n const rhsExpr = generateExpression(ctx, rhs);\n const rhsStr = resolveRes(ctx, rhsExpr);\n\n const type = binaryOperatorToType(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 ('u' in expression) {\n // Unary Expression\n\n const [op, arg] = expression.u;\n const argExpr = resolveRes(ctx, generateExpression(ctx, arg));\n return {\n value: `${op}${argExpr}`,\n // TODO: Infer data type from expression type and arguments.\n dataType: UnknownData,\n };\n }\n\n if ('a' in expression) {\n // Member Access\n\n const [targetId, property] = expression.a;\n const target = generateExpression(ctx, targetId);\n const propertyStr = resolveRes(ctx, generateExpression(ctx, property));\n\n if (typeof target.value === 'string') {\n return {\n value: `${target.value}.${propertyStr}`,\n // TODO: Infer data type\n dataType: UnknownData,\n };\n }\n\n if (isWgsl(target.value)) {\n return {\n // biome-ignore lint/suspicious/noExplicitAny: <sorry TypeScript>\n value: (target.value as any)[propertyStr],\n // TODO: Infer data type\n dataType: UnknownData,\n };\n }\n\n if (typeof target.value === 'object') {\n return {\n // biome-ignore lint/suspicious/noExplicitAny: <sorry TypeScript>\n value: (target.value as any)[propertyStr],\n // TODO: Infer data type\n dataType: UnknownData,\n };\n }\n\n throw new Error(`Cannot access member ${propertyStr} of ${target.value}`);\n }\n\n if ('i' in expression) {\n // Index Access\n\n const [target, property] = expression.i;\n const targetStr = resolveRes(ctx, generateExpression(ctx, target));\n const propertyStr = resolveRes(ctx, generateExpression(ctx, property));\n\n return {\n value: `${targetStr}[${propertyStr}]`,\n // TODO: Infer data type\n dataType: UnknownData,\n };\n }\n\n if ('n' in expression) {\n // Numeric Literal\n const value = expression.n;\n\n // Hex literals (since JS does not have float hex literals, we'll assume it's an int)\n const hexRegex = /^0x[0-9a-f]+$/i;\n if (hexRegex.test(value)) {\n return { value: value, dataType: abstractInt };\n }\n\n // Binary literals\n const binRegex = /^0b[01]+$/i;\n if (binRegex.test(value)) {\n // Since wgsl doesn't support binary literals, we'll convert it to a decimal number\n return {\n value: `${Number.parseInt(value.slice(2), 2)}`,\n dataType: abstractInt,\n };\n }\n\n const floatRegex = /^-?(?:\\d+\\.\\d*|\\d*\\.\\d+)$/;\n if (floatRegex.test(value)) {\n return { value, dataType: abstractFloat };\n }\n\n // Floating point literals with scientific notation\n const sciFloatRegex = /^-?\\d+\\.\\d+e-?\\d+$/;\n if (sciFloatRegex.test(value)) {\n return {\n value: value,\n dataType: abstractFloat,\n };\n }\n\n // Integer literals\n const intRegex = /^-?\\d+$/;\n if (intRegex.test(value)) {\n return { value: value, dataType: abstractInt };\n }\n\n throw new Error(`Invalid numeric literal ${value}`);\n }\n\n if ('f' in expression) {\n // Function Call\n\n const [callee, args] = expression.f;\n const id = generateExpression(ctx, callee);\n const idValue = id.value;\n\n ctx.callStack.push(idValue);\n\n const argResources = args.map((arg) => generateExpression(ctx, arg));\n const argValues = argResources.map((res) => resolveRes(ctx, res));\n\n ctx.callStack.pop();\n\n if (typeof idValue === 'string') {\n return {\n value: `${idValue}(${argValues.join(', ')})`,\n dataType: UnknownData,\n };\n }\n\n if (wgsl.isWgslStruct(idValue)) {\n const id = ctx.resolve(idValue);\n\n return {\n value: `${id}(${argValues.join(', ')})`,\n dataType: UnknownData,\n };\n }\n\n // Assuming that `id` is callable\n // TODO: Pass in resources, not just values.\n const result = (idValue as unknown as (...args: unknown[]) => unknown)(\n ...argValues,\n ) as Wgsl;\n // TODO: Make function calls return resources instead of just values.\n return { value: result, dataType: UnknownData };\n }\n\n if ('o' in expression) {\n const obj = expression.o;\n const callee = ctx.callStack[ctx.callStack.length - 1];\n\n const generateEntries = (values: smol.Expression[]) =>\n values\n .map((value) => {\n const valueRes = generateExpression(ctx, value);\n return resolveRes(ctx, valueRes);\n })\n .join(', ');\n\n if (wgsl.isWgslStruct(callee)) {\n const propKeys = Object.keys(callee.propTypes);\n const values = propKeys.map((key) => {\n const val = obj[key];\n if (val === undefined) {\n throw new Error(\n `Missing property ${key} in object literal for struct ${callee}`,\n );\n }\n return val;\n });\n\n return {\n value: generateEntries(values),\n dataType: callee,\n };\n }\n\n return {\n value: generateEntries(Object.values(obj)),\n dataType: UnknownData,\n };\n }\n\n if ('s' in expression) {\n throw new Error('Cannot use string literals in TGSL.');\n }\n\n assertExhaustive(expression);\n}\n\nexport function generateStatement(\n ctx: GenerationCtx,\n statement: smol.Statement,\n): string {\n if (typeof statement === 'string') {\n return `${ctx.pre}${resolveRes(ctx, generateIdentifier(ctx, statement))};`;\n }\n\n if (typeof statement === 'boolean') {\n return `${ctx.pre}${resolveRes(ctx, generateBoolean(ctx, statement))};`;\n }\n\n if ('r' in statement) {\n // check if the thing at the top of the call stack is a struct and the statement is a plain JS object\n // if so wrap the value returned in a constructor of the struct (its resolved name)\n if (\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1]) &&\n statement.r !== null &&\n typeof statement.r === 'object' &&\n 'o' in statement.r\n ) {\n const resource = resolveRes(ctx, generateExpression(ctx, statement.r));\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}return ${resolvedStruct}(${resource});`;\n }\n\n return statement.r === null\n ? `${ctx.pre}return;`\n : `${ctx.pre}return ${resolveRes(ctx, generateExpression(ctx, statement.r))};`;\n }\n\n if ('q' in statement) {\n const [cond, cons, alt] = statement.q;\n const condition = resolveRes(ctx, generateExpression(ctx, cond));\n\n ctx.indent(); // {\n const consequent = generateStatement(ctx, cons);\n ctx.dedent(); // }\n\n ctx.indent(); // {\n const alternate = alt ? generateStatement(ctx, alt) : undefined;\n ctx.dedent(); // }\n\n if (!alternate) {\n return `\\\n${ctx.pre}if (${condition})\n${consequent}`;\n }\n\n return `\\\n${ctx.pre}if (${condition})\n${consequent}\n${ctx.pre}else\n${alternate}`;\n }\n\n if ('l' in statement || 'c' in statement) {\n const [rawId, rawValue] = 'l' in statement ? statement.l : statement.c;\n const eq = rawValue ? generateExpression(ctx, rawValue) : undefined;\n\n if (!eq || !rawValue) {\n throw new Error('Cannot create variable without an initial value.');\n }\n\n registerBlockVariable(ctx, rawId, eq.dataType);\n const id = resolveRes(ctx, generateIdentifier(ctx, rawId));\n\n // If the value is a plain JS object it has to be an output struct\n if (\n typeof rawValue === 'object' &&\n 'o' in rawValue &&\n wgsl.isWgslStruct(ctx.callStack[ctx.callStack.length - 1])\n ) {\n const resolvedStruct = ctx.resolve(\n ctx.callStack[ctx.callStack.length - 1],\n );\n return `${ctx.pre}var ${id} = ${resolvedStruct}(${resolveRes(ctx, eq)});`;\n }\n\n return `${ctx.pre}var ${id} = ${resolveRes(ctx, eq)};`;\n }\n\n if ('b' in statement) {\n ctx.pushBlockScope();\n try {\n return generateBlock(ctx, statement);\n } finally {\n ctx.popBlockScope();\n }\n }\n\n // 'j' stands for for (trust me)\n if ('j' in statement) {\n const [init, condition, update, body] = statement.j;\n\n const initStatement = init ? generateStatement(ctx, init) : undefined;\n const initStr = initStatement ? initStatement.slice(0, -1) : '';\n\n const conditionExpr = condition\n ? generateExpression(ctx, condition)\n : undefined;\n const conditionStr = conditionExpr ? resolveRes(ctx, conditionExpr) : '';\n\n const updateStatement = update ? generateStatement(ctx, update) : undefined;\n const updateStr = updateStatement ? updateStatement.slice(0, -1) : '';\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}for (${initStr}; ${conditionStr}; ${updateStr})\n${bodyStr}`;\n }\n\n if ('w' in statement) {\n const [condition, body] = statement.w;\n const conditionStr = resolveRes(ctx, generateExpression(ctx, condition));\n\n ctx.indent();\n const bodyStr = generateStatement(ctx, body);\n ctx.dedent();\n\n return `\\\n${ctx.pre}while (${conditionStr})\n${bodyStr}`;\n }\n\n if ('k' in statement) {\n return `${ctx.pre}continue;`;\n }\n\n if ('d' in statement) {\n return `${ctx.pre}break;`;\n }\n\n return `${ctx.pre}${resolveRes(ctx, generateExpression(ctx, statement))};`;\n}\n\nexport function generateFunction(ctx: GenerationCtx, body: smol.Block): string {\n return generateBlock(ctx, body);\n}\n","import type { ISerialInput, ISerialOutput } from 'typed-binary';\nimport type { Infer, InferRecord } from '../shared/repr';\nimport alignIO from './alignIO';\nimport { alignmentOf, customAlignmentOf } from './alignmentOf';\nimport type {\n AnyConcreteData,\n AnyData,\n Disarray,\n LooseDecorated,\n Unstruct,\n} from './dataTypes';\nimport { mat2x2f, mat3x3f, mat4x4f } from './matrix';\nimport { sizeOf } from './sizeOf';\nimport type { WgslStruct } from './struct';\nimport {\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n} from './vector';\nimport type * as wgsl from './wgslTypes';\n\ntype DataWriter<TSchema extends wgsl.BaseData> = (\n output: ISerialOutput,\n schema: TSchema,\n value: Infer<TSchema>,\n) => void;\n\ntype DataReader<TSchema extends wgsl.BaseData> = (\n input: ISerialInput,\n schema: TSchema,\n) => Infer<TSchema>;\n\ntype CompleteDataWriters = {\n [TType in AnyConcreteData['type']]: DataWriter<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\ntype CompleteDataReaders = {\n [TType in AnyConcreteData['type']]: DataReader<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\nconst dataWriters = {\n bool(output, _schema: wgsl.Bool, value: boolean) {\n output.writeBool(value);\n },\n\n f32(output, _schema: wgsl.F32, value: number) {\n output.writeFloat32(value);\n },\n\n f16(output, _schema: wgsl.F16, value: number) {\n output.writeFloat16(value);\n },\n\n i32(output, _schema: wgsl.I32, value: number) {\n output.writeInt32(value);\n },\n\n u32(output, _schema: wgsl.U32, value: number) {\n output.writeUint32(value);\n },\n\n vec2f(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n\n vec2h(output, _, value: wgsl.v2h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n\n vec2i(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n\n vec2u(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n\n vec3f(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n\n vec3h(output, _, value: wgsl.v3h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n },\n\n vec3i(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n\n vec3u(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n\n vec4f(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n\n vec4h(output, _, value: wgsl.v4h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n\n vec4i(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n\n vec4u(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n\n mat2x2f(output, _, value: wgsl.m2x2f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat3x3f(output, _, value: wgsl.m3x3f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat4x4f(output, _, value: wgsl.m4x4f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n struct(\n output,\n schema: WgslStruct,\n value: InferRecord<Record<string, wgsl.BaseData>>,\n ) {\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(output, alignmentOf(property));\n writeData(output, property, value[key] as wgsl.BaseData);\n }\n\n alignIO(output, alignment);\n },\n\n array(output, schema: wgsl.WgslArray, value: Infer<wgsl.BaseData>[]) {\n if (schema.elementCount === 0) {\n throw new Error('Cannot write using a runtime-sized schema.');\n }\n\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n const beginning = output.currentByteOffset;\n for (let i = 0; i < Math.min(schema.elementCount, value.length); i++) {\n alignIO(output, alignment);\n writeData(output, schema.elementType, value[i]);\n }\n output.seekTo(beginning + sizeOf(schema));\n },\n\n ptr() {\n throw new Error('Pointers are not host-shareable');\n },\n\n atomic(output, schema: wgsl.Atomic, value: number) {\n dataWriters[schema.inner.type]?.(output, schema, value);\n },\n\n decorated(output, schema: wgsl.Decorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n dataWriters[(schema.inner as AnyData)?.type]?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n },\n\n // Loose Types\n\n uint8(output, _, value: number) {\n output.writeUint8(value);\n },\n uint8x2(output, _, value: wgsl.v2u) {\n output.writeUint8(value.x);\n output.writeUint8(value.y);\n },\n uint8x4(output, _, value: wgsl.v4u) {\n output.writeUint8(value.x);\n output.writeUint8(value.y);\n output.writeUint8(value.z);\n output.writeUint8(value.w);\n },\n sint8(output, _, value: number) {\n output.writeInt8(value);\n },\n sint8x2(output, _, value: wgsl.v2i) {\n output.writeInt8(value.x);\n output.writeInt8(value.y);\n },\n sint8x4(output, _, value: wgsl.v4i) {\n output.writeInt8(value.x);\n output.writeInt8(value.y);\n output.writeInt8(value.z);\n output.writeInt8(value.w);\n },\n unorm8(output, _, value: number) {\n output.writeUint8(value * 255);\n },\n unorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(value.x * 255);\n output.writeUint8(value.y * 255);\n },\n unorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(value.x * 255);\n output.writeUint8(value.y * 255);\n output.writeUint8(value.z * 255);\n output.writeUint8(value.w * 255);\n },\n snorm8(output, _, value: number) {\n output.writeUint8(value * 127 + 128);\n },\n snorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(value.x * 127 + 128);\n output.writeUint8(value.y * 127 + 128);\n },\n snorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(value.x * 127 + 128);\n output.writeUint8(value.y * 127 + 128);\n output.writeUint8(value.z * 127 + 128);\n output.writeUint8(value.w * 127 + 128);\n },\n uint16(output, _, value: number) {\n output.writeUint16(value);\n },\n uint16x2(output, _, value: wgsl.v2u) {\n output.writeUint16(value.x);\n output.writeUint16(value.y);\n },\n uint16x4(output, _, value: wgsl.v4u) {\n output.writeUint16(value.x);\n output.writeUint16(value.y);\n output.writeUint16(value.z);\n output.writeUint16(value.w);\n },\n sint16(output, _, value: number) {\n output.writeInt16(value);\n },\n sint16x2(output, _, value: wgsl.v2i) {\n output.writeInt16(value.x);\n output.writeInt16(value.y);\n },\n sint16x4(output, _, value: wgsl.v4i) {\n output.writeInt16(value.x);\n output.writeInt16(value.y);\n output.writeInt16(value.z);\n output.writeInt16(value.w);\n },\n unorm16(output, _, value: number) {\n output.writeUint16(value * 65535);\n },\n unorm16x2(output, _, value: wgsl.v2f) {\n output.writeUint16(value.x * 65535);\n output.writeUint16(value.y * 65535);\n },\n unorm16x4(output, _, value: wgsl.v4f) {\n output.writeUint16(value.x * 65535);\n output.writeUint16(value.y * 65535);\n output.writeUint16(value.z * 65535);\n output.writeUint16(value.w * 65535);\n },\n snorm16(output, _, value: number) {\n output.writeUint16(value * 32767 + 32768);\n },\n snorm16x2(output, _, value: wgsl.v2f) {\n output.writeUint16(value.x * 32767 + 32768);\n output.writeUint16(value.y * 32767 + 32768);\n },\n snorm16x4(output, _, value: wgsl.v4f) {\n output.writeUint16(value.x * 32767 + 32768);\n output.writeUint16(value.y * 32767 + 32768);\n output.writeUint16(value.z * 32767 + 32768);\n output.writeUint16(value.w * 32767 + 32768);\n },\n float16(output, _, value: number) {\n output.writeFloat16(value);\n },\n float16x2(output, _, value: wgsl.v2f) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n float16x4(output, _, value: wgsl.v4f) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n float32(output, _, value: number) {\n output.writeFloat32(value);\n },\n float32x2(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n float32x3(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n float32x4(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n uint32(output, _, value: number) {\n output.writeUint32(value);\n },\n uint32x2(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n uint32x3(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n uint32x4(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n sint32(output, _, value: number) {\n output.writeInt32(value);\n },\n sint32x2(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n sint32x3(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n sint32x4(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n 'unorm10-10-10-2'(output, _, value: wgsl.v4f) {\n let packed = 0;\n packed |= ((value.x * 1023) & 1023) << 22; // r (10 bits)\n packed |= ((value.x * 1023) & 1023) << 12; // g (10 bits)\n packed |= ((value.y * 1023) & 1023) << 2; // b (10 bits)\n packed |= (value.z * 3) & 3; // a (2 bits)\n output.writeUint32(packed);\n },\n 'unorm8x4-bgra'(output, _, value: wgsl.v4f) {\n output.writeUint8(value.z * 255);\n output.writeUint8(value.y * 255);\n output.writeUint8(value.x * 255);\n output.writeUint8(value.w * 255);\n },\n\n disarray(output, schema: Disarray, value: unknown[]) {\n const alignment = alignmentOf(schema);\n\n alignIO(output, alignment);\n const beginning = output.currentByteOffset;\n for (let i = 0; i < Math.min(schema.elementCount, value.length); i++) {\n alignIO(output, alignment);\n dataWriters[(schema.elementType as AnyData)?.type]?.(\n output,\n schema.elementType,\n value[i],\n );\n }\n\n output.seekTo(beginning + sizeOf(schema));\n },\n\n unstruct(output, schema: Unstruct, value) {\n for (const [key, property] of Object.entries(schema.propTypes)) {\n dataWriters[property.type]?.(output, property, value[key]);\n }\n },\n\n 'loose-decorated'(output, schema: LooseDecorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n const writer = dataWriters[(schema.inner as AnyData)?.type];\n writer?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataWriters as Record<\n string,\n (output: ISerialOutput, schema: unknown, value: unknown) => void\n>;\n\nexport function writeData<TData extends wgsl.BaseData>(\n output: ISerialOutput,\n schema: TData,\n value: Infer<TData>,\n): void {\n const writer = dataWriters[schema.type];\n if (!writer) {\n throw new Error(`Cannot write data of type '${schema.type}'.`);\n }\n\n writer(output, schema, value);\n}\n\nconst dataReaders = {\n bool(input: ISerialInput): boolean {\n return input.readBool();\n },\n\n f32(input: ISerialInput): number {\n return input.readFloat32();\n },\n\n f16(input: ISerialInput): number {\n return input.readFloat16();\n },\n\n i32(input: ISerialInput): number {\n return input.readInt32();\n },\n\n u32(input: ISerialInput): number {\n return input.readUint32();\n },\n\n vec2f(input: ISerialInput): wgsl.v2f {\n return vec2f(input.readFloat32(), input.readFloat32());\n },\n\n vec3f(input: ISerialInput): wgsl.v3f {\n return vec3f(input.readFloat32(), input.readFloat32(), input.readFloat32());\n },\n\n vec4f(input: ISerialInput): wgsl.v4f {\n return vec4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n vec2h(input): wgsl.v2h {\n return vec2h(input.readFloat16(), input.readFloat16());\n },\n\n vec3h(input: ISerialInput): wgsl.v3h {\n return vec3h(input.readFloat16(), input.readFloat16(), input.readFloat16());\n },\n\n vec4h(input: ISerialInput): wgsl.v4h {\n return vec4h(\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n );\n },\n\n vec2i(input): wgsl.v2i {\n return vec2i(input.readInt32(), input.readInt32());\n },\n\n vec3i(input: ISerialInput): wgsl.v3i {\n return vec3i(input.readInt32(), input.readInt32(), input.readInt32());\n },\n\n vec4i(input: ISerialInput): wgsl.v4i {\n return vec4i(\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n );\n },\n\n vec2u(input): wgsl.v2u {\n return vec2u(input.readUint32(), input.readUint32());\n },\n\n vec3u(input: ISerialInput): wgsl.v3u {\n return vec3u(input.readUint32(), input.readUint32(), input.readUint32());\n },\n\n vec4u(input: ISerialInput): wgsl.v4u {\n return vec4u(\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n );\n },\n\n mat2x2f(input: ISerialInput): wgsl.m2x2f {\n return mat2x2f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n mat3x3f(input: ISerialInput): wgsl.m3x3f {\n const skipOneAfter = () => {\n const value = input.readFloat32();\n input.readFloat32(); // skipping;\n return value;\n };\n\n return mat3x3f(\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n );\n },\n\n mat4x4f(input: ISerialInput): wgsl.m4x4f {\n return mat4x4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n struct(input: ISerialInput, schema: WgslStruct) {\n const alignment = alignmentOf(schema);\n alignIO(input, alignment);\n const result = {} as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(input, alignmentOf(property));\n result[key] = readData(input, property);\n }\n\n alignIO(input, alignment);\n return result as InferRecord<Record<string, wgsl.BaseData>>;\n },\n\n array(input, schema) {\n if (schema.elementCount === 0) {\n throw new Error('Cannot read using a runtime-sized schema.');\n }\n\n const alignment = alignmentOf(schema);\n const elements: unknown[] = [];\n\n for (let i = 0; i < schema.elementCount; i++) {\n alignIO(input, alignment);\n const elementType = schema.elementType as wgsl.AnyWgslData;\n const value = readData(input, elementType);\n elements.push(value);\n }\n\n alignIO(input, alignment);\n return elements as never[];\n },\n\n ptr() {\n throw new Error('Pointers are not host-shareable');\n },\n\n atomic(input, schema: wgsl.Atomic): number {\n return readData(input, schema.inner);\n },\n\n decorated(input, schema: wgsl.Decorated) {\n const alignment = customAlignmentOf(schema);\n alignIO(input, alignment);\n\n const beginning = input.currentByteOffset;\n const value = readData(input, schema.inner);\n input.seekTo(beginning + sizeOf(schema));\n return value as never;\n },\n\n // Loose Types\n\n uint8: (i) => i.readUint8(),\n uint8x2: (i) => vec2u(i.readUint8(), i.readUint8()),\n uint8x4: (i) =>\n vec4u(i.readUint8(), i.readUint8(), i.readUint8(), i.readUint8()),\n sint8: (i) => i.readInt8(),\n sint8x2: (i) => {\n return vec2i(i.readInt8(), i.readInt8());\n },\n sint8x4: (i) => vec4i(i.readInt8(), i.readInt8(), i.readInt8(), i.readInt8()),\n unorm8: (i) => i.readUint8() / 255,\n unorm8x2: (i) => vec2f(i.readUint8() / 255, i.readUint8() / 255),\n unorm8x4: (i) =>\n vec4f(\n i.readUint8() / 255,\n i.readUint8() / 255,\n i.readUint8() / 255,\n i.readUint8() / 255,\n ),\n snorm8: (i) => (i.readUint8() - 128) / 127,\n snorm8x2: (i) =>\n vec2f((i.readUint8() - 128) / 127, (i.readUint8() - 128) / 127),\n snorm8x4: (i) =>\n vec4f(\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n (i.readUint8() - 128) / 127,\n ),\n uint16: (i) => i.readUint16(),\n uint16x2: (i) => vec2u(i.readUint16(), i.readUint16()),\n uint16x4: (i) =>\n vec4u(i.readUint16(), i.readUint16(), i.readUint16(), i.readUint16()),\n sint16: (i) => i.readInt16(),\n sint16x2: (i) => vec2i(i.readInt16(), i.readInt16()),\n sint16x4: (i) =>\n vec4i(i.readInt16(), i.readInt16(), i.readInt16(), i.readInt16()),\n unorm16: (i) => i.readUint16() / 65535,\n unorm16x2: (i) => vec2f(i.readUint16() / 65535, i.readUint16() / 65535),\n unorm16x4: (i) =>\n vec4f(\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n i.readUint16() / 65535,\n ),\n snorm16: (i) => (i.readUint16() - 32768) / 32767,\n snorm16x2: (i): wgsl.v2f =>\n vec2f(dataReaders.snorm16(i), dataReaders.snorm16(i)),\n snorm16x4: (i): wgsl.v4f =>\n vec4f(\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n dataReaders.snorm16(i),\n ),\n float16(i) {\n return i.readFloat16();\n },\n float16x2: (i) => vec2f(i.readFloat16(), i.readFloat16()),\n float16x4: (i) =>\n vec4f(i.readFloat16(), i.readFloat16(), i.readFloat16(), i.readFloat16()),\n float32: (i) => i.readFloat32(),\n float32x2: (i) => vec2f(i.readFloat32(), i.readFloat32()),\n float32x3: (i) => vec3f(i.readFloat32(), i.readFloat32(), i.readFloat32()),\n float32x4: (i) =>\n vec4f(i.readFloat32(), i.readFloat32(), i.readFloat32(), i.readFloat32()),\n uint32: (i) => i.readUint32(),\n uint32x2: (i) => vec2u(i.readUint32(), i.readUint32()),\n uint32x3: (i) => vec3u(i.readUint32(), i.readUint32(), i.readUint32()),\n uint32x4: (i) =>\n vec4u(i.readUint32(), i.readUint32(), i.readUint32(), i.readUint32()),\n sint32: (i) => i.readInt32(),\n sint32x2: (i) => vec2i(i.readInt32(), i.readInt32()),\n sint32x3: (i) => vec3i(i.readInt32(), i.readInt32(), i.readInt32()),\n sint32x4: (i) =>\n vec4i(i.readInt32(), i.readInt32(), i.readInt32(), i.readInt32()),\n 'unorm10-10-10-2'(i) {\n const packed = i.readUint32();\n const r = (packed >> 22) / 1023;\n const g = ((packed >> 12) & 1023) / 1023;\n const b = ((packed >> 2) & 1023) / 1023;\n const a = (packed & 3) / 3;\n return vec4f(r, g, b, a);\n },\n 'unorm8x4-bgra'(i) {\n const b = i.readByte() / 255;\n const g = i.readByte() / 255;\n const r = i.readByte() / 255;\n const a = i.readByte() / 255;\n return vec4f(r, g, b, a);\n },\n\n unstruct(input, schema: Unstruct) {\n const result = {} as Record<string, unknown>;\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n result[key] = readData(input, property);\n }\n\n return result as InferRecord<Record<string, wgsl.BaseData>>;\n },\n\n disarray(input, schema: Disarray) {\n const alignment = alignmentOf(schema);\n const elements: unknown[] = [];\n\n for (let i = 0; i < schema.elementCount; i++) {\n alignIO(input, alignment);\n elements.push(readData(input, schema.elementType));\n }\n\n alignIO(input, alignment);\n return elements;\n },\n\n 'loose-decorated'(input, schema: LooseDecorated) {\n alignIO(input, customAlignmentOf(schema));\n\n const beginning = input.currentByteOffset;\n const value = readData(input, schema.inner);\n input.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataReaders;\n\nexport function readData<TData extends wgsl.BaseData>(\n input: ISerialInput,\n schema: TData,\n): Infer<TData> {\n const reader = (dataReaders as Record<string, unknown>)[\n schema.type\n ] as DataReader<TData>;\n if (!reader) {\n throw new Error(`Cannot read data of type '${schema.type}'.`);\n }\n\n return reader(input, schema);\n}\n","import type { Block } from 'tinyest';\nimport { resolveData } from './core/resolve/resolveData';\nimport {\n type Eventual,\n type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n isDerived,\n isProviding,\n isSlot,\n} from './core/slot/slotTypes';\nimport { isData } from './data';\nimport { getAttributesString } from './data/attributes';\nimport {\n type AnyWgslData,\n type BaseData,\n isWgslArray,\n isWgslStruct,\n} from './data/wgslTypes';\nimport { MissingSlotValueError, ResolutionError } from './errors';\nimport { RuntimeMode, popMode, provideCtx, pushMode } from './gpuMode';\nimport type { JitTranspiler } from './jitTranspiler';\nimport type { NameRegistry } from './nameRegistry';\nimport { naturalsExcept } from './shared/generators';\nimport type { Infer } from './shared/repr';\nimport { generateFunction } from './smol';\nimport {\n type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n bindGroupLayout,\n} from './tgpuBindGroupLayout';\nimport type { FnToWgslOptions, ResolutionCtx, Resource, Wgsl } from './types';\nimport { UnknownData, isSelfResolvable, isWgsl } from './types';\n\n/**\n * Inserted into bind group entry definitions that belong\n * to the automatically generated catch-all bind group.\n *\n * A non-occupied group index can only be determined after\n * every resource has been resolved, so this acts as a placeholder\n * to be replaced with an actual numeric index at the very end\n * of the resolution process.\n */\nconst CATCHALL_BIND_GROUP_IDX_MARKER = '#CATCHALL#';\n\nexport type ResolutionCtxImplOptions = {\n readonly names: NameRegistry;\n readonly jitTranspiler?: JitTranspiler | undefined;\n};\n\ntype SlotToValueMap = Map<TgpuSlot<unknown>, unknown>;\n\ntype ItemLayer = {\n type: 'item';\n usedSlots: Set<TgpuSlot<unknown>>;\n};\n\ntype SlotBindingLayer = {\n type: 'slotBinding';\n bindingMap: WeakMap<TgpuSlot<unknown>, unknown>;\n};\n\ntype FunctionScopeLayer = {\n type: 'functionScope';\n args: Resource[];\n externalMap: Record<string, unknown>;\n returnType: AnyWgslData | undefined;\n};\n\ntype BlockScopeLayer = {\n type: 'blockScope';\n declarations: Map<string, AnyWgslData | UnknownData>;\n};\n\nclass 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 pushSlotBindings(pairs: SlotValuePair<unknown>[]) {\n this._stack.push({\n type: 'slotBinding',\n bindingMap: new WeakMap(pairs),\n });\n }\n\n pushFunctionScope(\n args: Resource[],\n returnType: AnyWgslData | undefined,\n externalMap: Record<string, unknown>,\n ) {\n this._stack.push({\n type: 'functionScope',\n args,\n returnType,\n externalMap,\n });\n }\n\n pushBlockScope() {\n this._stack.push({\n type: 'blockScope',\n declarations: new Map<string, AnyWgslData | UnknownData>(),\n });\n }\n\n popBlockScope() {\n const layer = this._stack.pop();\n if (layer?.type !== 'blockScope') {\n throw new Error('Expected block scope layer to be on top.');\n }\n }\n\n pop() {\n const layer = this._stack.pop();\n if (layer?.type === 'item') {\n this._itemDepth--;\n }\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n if (layer?.type === 'item') {\n // Binding not available yet, so this layer is dependent on the slot's value.\n layer.usedSlots.add(slot);\n } else if (layer?.type === 'slotBinding') {\n const boundValue = layer.bindingMap.get(slot);\n\n if (boundValue !== undefined) {\n return boundValue as T;\n }\n } else if (\n layer?.type === 'functionScope' ||\n layer?.type === 'blockScope'\n ) {\n // Skip\n } else {\n throw new Error('Unknown layer type.');\n }\n }\n\n return slot.defaultValue;\n }\n\n getResourceById(id: string): Resource | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'functionScope') {\n const arg = layer.args.find((a) => a.value === id);\n if (arg !== undefined) {\n return arg;\n }\n\n const external = layer.externalMap[id];\n if (external !== undefined) {\n // TODO: Extract the type of the external value.\n return { value: external, dataType: UnknownData };\n }\n\n // Since functions cannot access resources from the calling scope, we\n // return early here.\n return undefined;\n }\n\n if (layer?.type === 'blockScope') {\n const declarationType = layer.declarations.get(id);\n if (declarationType !== undefined) {\n return { value: id, dataType: declarationType };\n }\n } else {\n // Skip\n }\n }\n\n return undefined;\n }\n\n defineBlockVariable(id: string, type: AnyWgslData | UnknownData): Resource {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'blockScope') {\n layer.declarations.set(id, type);\n\n return { value: id, dataType: type };\n }\n }\n\n throw new Error('No block scope found to define a variable in.');\n }\n}\n\nconst INDENT = [\n '', // 0\n ' ', // 1\n ' ', // 2\n ' ', // 3\n ' ', // 4\n ' ', // 5\n ' ', // 6\n ' ', // 7\n ' ', // 8\n];\n\nconst N = INDENT.length - 1;\n\nexport class IndentController {\n private identLevel = 0;\n\n get pre(): string {\n return (\n INDENT[this.identLevel] ??\n (INDENT[N] as string).repeat(this.identLevel / N) +\n INDENT[this.identLevel % N]\n );\n }\n\n indent(): string {\n const str = this.pre;\n this.identLevel++;\n return str;\n }\n\n dedent(): string {\n this.identLevel--;\n return this.pre;\n }\n}\n\ninterface FixedBindingConfig {\n layoutEntry: TgpuLayoutEntry;\n resource: object;\n}\n\nexport class ResolutionCtxImpl implements ResolutionCtx {\n private readonly _memoizedResolves = new WeakMap<\n // WeakMap because if the item does not exist anymore,\n // apart from this map, there is no way to access the cached value anyway.\n object,\n { slotToValueMap: SlotToValueMap; result: string }[]\n >();\n private readonly _memoizedDerived = new WeakMap<\n // WeakMap because if the \"derived\" does not exist anymore,\n // apart from this map, there is no way to access the cached value anyway.\n TgpuDerived<unknown>,\n { slotToValueMap: SlotToValueMap; result: unknown }[]\n >();\n\n private readonly _indentController = new IndentController();\n private readonly _jitTranspiler: JitTranspiler | undefined;\n private readonly _itemStateStack = new ItemStateStack();\n private readonly _declarations: string[] = [];\n\n // -- Bindings\n /**\n * A map from registered bind group layouts to random strings put in\n * place of their group index. The whole tree has to be traversed to\n * collect every use of a typed bind group layout, since they can be\n * explicitly imposed group indices, and they cannot collide.\n */\n public readonly bindGroupLayoutsToPlaceholderMap = new Map<\n TgpuBindGroupLayout,\n string\n >();\n private _nextFreeLayoutPlaceholderIdx = 0;\n public readonly fixedBindings: FixedBindingConfig[] = [];\n // --\n\n public readonly callStack: unknown[] = [];\n public readonly names: NameRegistry;\n\n constructor(opts: ResolutionCtxImplOptions) {\n this.names = opts.names;\n this._jitTranspiler = opts.jitTranspiler;\n }\n\n get pre(): string {\n return this._indentController.pre;\n }\n\n indent(): string {\n return this._indentController.indent();\n }\n\n dedent(): string {\n return this._indentController.dedent();\n }\n\n getById(id: string): Resource | null {\n // TODO: Provide access to external values\n // TODO: Provide data type information\n // TODO: Return null if no id is found (when we can properly handle it)\n return (\n this._itemStateStack.getResourceById(id) ?? {\n value: id,\n dataType: UnknownData,\n }\n );\n }\n\n defineVariable(id: string, dataType: AnyWgslData | UnknownData): Resource {\n // TODO: Bring this behavior back when we have type inference\n // const resource = this.getById(id);\n\n // if (resource) {\n // throw new Error(`Resource ${id} already exists in the current scope.`);\n // } else {\n // return this._itemStateStack.defineBlockVariable(id, dataType);\n // }\n return this._itemStateStack.defineBlockVariable(id, dataType);\n }\n\n pushBlockScope() {\n this._itemStateStack.pushBlockScope();\n }\n\n popBlockScope() {\n this._itemStateStack.popBlockScope();\n }\n\n transpileFn(fn: string): {\n argNames: string[];\n body: Block;\n externalNames: string[];\n } {\n if (!this._jitTranspiler) {\n throw new Error(\n 'Tried to execute a tgpu.fn function without providing a JIT transpiler, or transpiling at build time.',\n );\n }\n\n return this._jitTranspiler.transpileFn(fn);\n }\n\n fnToWgsl(options: FnToWgslOptions): { head: Wgsl; body: Wgsl } {\n this._itemStateStack.pushFunctionScope(\n options.args,\n options.returnType,\n options.externalMap,\n );\n const str = generateFunction(this, options.body);\n this._itemStateStack.pop();\n\n const argList = options.args\n .map(\n (arg) => `${arg.value}: ${this.resolve(arg.dataType as AnyWgslData)}`,\n )\n .join(', ');\n\n return {\n head:\n options.returnType !== undefined\n ? `(${argList}) -> ${getAttributesString(options.returnType)} ${this.resolve(options.returnType)}`\n : `(${argList})`,\n body: str,\n };\n }\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.pop();\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 const result = derived['~compute']();\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\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.pop();\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.pop();\n }\n }\n\n resolve(item: unknown): string {\n if (isProviding(item)) {\n return this.withSlots(item['~providing'].pairs, () =>\n this.resolve(item['~providing'].inner),\n );\n }\n\n if ((item && typeof item === 'object') || typeof item === 'function') {\n if (this._itemStateStack.itemDepth === 0) {\n try {\n pushMode(RuntimeMode.GPU);\n const result = provideCtx(this, () => this._getOrInstantiate(item));\n return `${[...this._declarations].join('\\n\\n')}${result}`;\n } finally {\n popMode(RuntimeMode.GPU);\n }\n }\n\n return this._getOrInstantiate(item);\n }\n\n return String(item);\n }\n\n resolveValue<T extends BaseData>(\n value: Infer<T>,\n schema?: T | undefined,\n ): string {\n if (isWgsl(value)) {\n return this.resolve(value);\n }\n\n if (schema && isWgslArray(schema)) {\n return `array(${(value as unknown[]).map((element) => this.resolveValue(element, schema.elementType))})`;\n }\n\n if (Array.isArray(value)) {\n return `array(${value.map((element) => this.resolveValue(element))})`;\n }\n\n if (schema && isWgslStruct(schema)) {\n return `${this.resolve(schema)}(${Object.entries(schema.propTypes).map(([key, type_]) => this.resolveValue((value as Infer<typeof schema>)[key], type_))})`;\n }\n\n throw new Error(\n `Value ${value} (as json: ${JSON.stringify(value)}) of schema ${schema} is not resolvable to WGSL`,\n );\n }\n}\n\nexport interface ResolutionResult {\n code: string;\n bindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | null;\n}\n\nexport function resolve(\n item: Wgsl,\n options: ResolutionCtxImplOptions,\n): ResolutionResult {\n const ctx = new ResolutionCtxImpl(options);\n let code = ctx.resolve(item);\n\n const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;\n const bindGroupLayouts: TgpuBindGroupLayout[] = [];\n const takenIndices = new Set<number>(\n [...memoMap.keys()]\n .map((layout) => layout.index)\n .filter((v): v is number => v !== undefined),\n );\n\n const automaticIds = naturalsExcept(takenIndices);\n\n const layoutEntries = ctx.fixedBindings.map(\n (binding, idx) =>\n [String(idx), binding.layoutEntry] as [string, TgpuLayoutEntry],\n );\n\n const createCatchallGroup = () => {\n const catchallIdx = automaticIds.next().value;\n const catchallLayout = bindGroupLayout(Object.fromEntries(layoutEntries));\n bindGroupLayouts[catchallIdx] = catchallLayout;\n code = code.replaceAll(CATCHALL_BIND_GROUP_IDX_MARKER, String(catchallIdx));\n\n return [\n catchallIdx,\n new TgpuBindGroupImpl(\n catchallLayout,\n Object.fromEntries(\n ctx.fixedBindings.map(\n (binding, idx) =>\n // biome-ignore lint/suspicious/noExplicitAny: <it's fine>\n [String(idx), binding.resource] as [string, any],\n ),\n ),\n ),\n ] as [number, TgpuBindGroup];\n };\n\n // Retrieving the catch-all binding index first, because it's inherently\n // the least swapped bind group (fixed and cannot be swapped).\n const catchall = layoutEntries.length > 0 ? createCatchallGroup() : null;\n\n for (const [layout, placeholder] of memoMap.entries()) {\n const idx = layout.index ?? automaticIds.next().value;\n bindGroupLayouts[idx] = layout;\n code = code.replaceAll(placeholder, String(idx));\n }\n\n return {\n code,\n bindGroupLayouts,\n catchall,\n };\n}\n","import type { JitTranspiler } from '../../jitTranspiler';\nimport { RandomNameRegistry, StrictNameRegistry } from '../../nameRegistry';\nimport { resolve as resolveImpl } from '../../resolutionCtx';\nimport type { SelfResolvable, Wgsl } from '../../types';\nimport { applyExternals, replaceExternalsInWgsl } from './externals';\n\nexport interface TgpuResolveOptions {\n /**\n * Map of external names to their resolvable values.\n */\n externals: Record<string, Wgsl | object>;\n /**\n * The code template to use for the resolution. All external names will be replaced with their resolved values.\n * @default ''\n */\n template?: string | undefined;\n /**\n * The naming strategy used for generating identifiers for resolved externals and their dependencies.\n * @default 'random'\n */\n names?: 'strict' | 'random' | undefined;\n /**\n * Optional JIT transpiler for resolving TGSL functions.\n * @experimental\n */\n unstable_jitTranspiler?: JitTranspiler | undefined;\n}\n\n/**\n * Resolves a template with external values. Each external will get resolved to a code string and replaced in the template.\n * Any dependencies of the externals will also be resolved and included in the output.\n * @param options - The options for the resolution.\n *\n * @returns The resolved code.\n *\n * @example\n * ```ts\n * const Gradient = d.struct({\n * from: d.vec3f,\n * to: d.vec3f,\n * });\n *\n * const resolved = tgpu.resolve({\n * template: `\n * fn getGradientAngle(gradient: Gradient) -> f32 {\n * return atan(gradient.to.y - gradient.from.y, gradient.to.x - gradient.from.x);\n * }\n * `,\n * externals: {\n * Gradient,\n * },\n * });\n *\n * console.log(resolved);\n * // struct Gradient_0 {\n * // from: vec3f,\n * // to: vec3f,\n * // }\n * // fn getGradientAngle(gradient: Gradient_0) -> f32 {\n * // return atan(gradient.to.y - gradient.from.y, gradient.to.x - gradient.from.x);\n * // }\n * ```\n */\nexport function resolve(options: TgpuResolveOptions): string {\n const {\n externals,\n template,\n names,\n unstable_jitTranspiler: jitTranspiler,\n } = options;\n\n const dependencies = {} as Record<string, Wgsl>;\n applyExternals(dependencies, externals ?? {});\n\n const resolutionObj: SelfResolvable = {\n '~resolve'(ctx) {\n return replaceExternalsInWgsl(ctx, dependencies, template ?? '');\n },\n\n toString: () => '<root>',\n };\n\n const { code } = resolveImpl(resolutionObj, {\n names:\n names === 'strict' ? new StrictNameRegistry() : new RandomNameRegistry(),\n jitTranspiler,\n });\n\n return code;\n}\n"]}
|
package/index.d.cts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { A as AnyWgslData, T as TgpuNamable, I as Infer, 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, B as BaseData, L as Location, W as WgslStruct, o as Default, p as UnionToIntersection, q as WgslArray, r as Disarray, s as Unstruct, t as VertexFormat, u as TgpuVertexAttrib, K as KindToDefaultFormatMap, v as KindToAcceptedAttribMap, w as AnyData, O as OmitProps, P as Prettify, M as Mutable, x as WgslTypeLiteral, y as InferPartial, z as MemIdentity, C as
|
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
|
1
|
+
import { A as AnyWgslData, T as TgpuNamable, I as Infer, 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, B as BaseData, L as Location, W as WgslStruct, o as Default, p as UnionToIntersection, q as WgslArray, r as Disarray, s as Unstruct, t as VertexFormat, u as TgpuVertexAttrib, K as KindToDefaultFormatMap, v as KindToAcceptedAttribMap, w as AnyData, O as OmitProps, P as Prettify, M as Mutable, x as WgslTypeLiteral, y as InferPartial, z as MemIdentity, C as InferGPU, E as AbstractInt, G as AbstractFloat, H as AnyVecInstance, J as AnyMatInstance } from './wgslTypes-VtSRoe90.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-DSOqT8yA.cjs';
|
3
3
|
import * as smol from 'tinyest';
|
4
4
|
import { Block } from 'tinyest';
|
5
5
|
|
@@ -523,7 +523,7 @@ type DataToContainedAttribs<T> = T extends AnyWgslStruct | Unstruct ? {
|
|
523
523
|
type: VertexFormat;
|
524
524
|
} ? TgpuVertexAttrib<T['type']> : T extends {
|
525
525
|
type: keyof KindToDefaultFormatMap;
|
526
|
-
} ? TgpuVertexAttrib<KindToDefaultFormatMap[T['type']]> : never;
|
526
|
+
} ? TgpuVertexAttrib<KindToDefaultFormatMap[T['type']]> : T extends Decorated<infer TInner> ? DataToContainedAttribs<TInner> : never;
|
527
527
|
/**
|
528
528
|
* Interprets an array as a set of vertex attributes.
|
529
529
|
*/
|
@@ -651,6 +651,100 @@ interface DepthStencilAttachment {
|
|
651
651
|
stencilReadOnly?: boolean;
|
652
652
|
}
|
653
653
|
|
654
|
+
interface SamplerProps {
|
655
|
+
addressModeU?: GPUAddressMode;
|
656
|
+
addressModeV?: GPUAddressMode;
|
657
|
+
/**
|
658
|
+
* Specifies the address modes for the texture width, height, and depth
|
659
|
+
* coordinates, respectively.
|
660
|
+
*/
|
661
|
+
addressModeW?: GPUAddressMode;
|
662
|
+
/**
|
663
|
+
* Specifies the sampling behavior when the sample footprint is smaller than or equal to one
|
664
|
+
* texel.
|
665
|
+
*/
|
666
|
+
magFilter?: GPUFilterMode;
|
667
|
+
/**
|
668
|
+
* Specifies the sampling behavior when the sample footprint is larger than one texel.
|
669
|
+
*/
|
670
|
+
minFilter?: GPUFilterMode;
|
671
|
+
/**
|
672
|
+
* Specifies behavior for sampling between mipmap levels.
|
673
|
+
*/
|
674
|
+
mipmapFilter?: GPUMipmapFilterMode;
|
675
|
+
lodMinClamp?: number;
|
676
|
+
/**
|
677
|
+
* Specifies the minimum and maximum levels of detail, respectively, used internally when
|
678
|
+
* sampling a texture.
|
679
|
+
*/
|
680
|
+
lodMaxClamp?: number;
|
681
|
+
/**
|
682
|
+
* Specifies the maximum anisotropy value clamp used by the sampler. Anisotropic filtering is
|
683
|
+
* enabled when {@link GPUSamplerDescriptor.maxAnisotropy} is > 1 and the implementation supports it.
|
684
|
+
* Anisotropic filtering improves the image quality of textures sampled at oblique viewing
|
685
|
+
* angles. Higher {@link GPUSamplerDescriptor.maxAnisotropy} values indicate the maximum ratio of
|
686
|
+
* anisotropy supported when filtering.
|
687
|
+
*
|
688
|
+
* Most implementations support {@link GPUSamplerDescriptor.maxAnisotropy} values in range
|
689
|
+
* between 1 and 16, inclusive. The used value of {@link GPUSamplerDescriptor.maxAnisotropy}
|
690
|
+
* will be clamped to the maximum value that the platform supports.
|
691
|
+
* The precise filtering behavior is implementation-dependent.
|
692
|
+
*/
|
693
|
+
maxAnisotropy?: number;
|
694
|
+
}
|
695
|
+
interface ComparisonSamplerProps {
|
696
|
+
compare: GPUCompareFunction;
|
697
|
+
addressModeU?: GPUAddressMode;
|
698
|
+
addressModeV?: GPUAddressMode;
|
699
|
+
/**
|
700
|
+
* Specifies the address modes for the texture width, height, and depth
|
701
|
+
* coordinates, respectively.
|
702
|
+
*/
|
703
|
+
addressModeW?: GPUAddressMode;
|
704
|
+
/**
|
705
|
+
* Specifies the sampling behavior when the sample footprint is smaller than or equal to one
|
706
|
+
* texel.
|
707
|
+
*/
|
708
|
+
magFilter?: GPUFilterMode;
|
709
|
+
/**
|
710
|
+
* Specifies the sampling behavior when the sample footprint is larger than one texel.
|
711
|
+
*/
|
712
|
+
minFilter?: GPUFilterMode;
|
713
|
+
/**
|
714
|
+
* Specifies behavior for sampling between mipmap levels.
|
715
|
+
*/
|
716
|
+
mipmapFilter?: GPUMipmapFilterMode;
|
717
|
+
lodMinClamp?: number;
|
718
|
+
/**
|
719
|
+
* Specifies the minimum and maximum levels of detail, respectively, used internally when
|
720
|
+
* sampling a texture.
|
721
|
+
*/
|
722
|
+
lodMaxClamp?: number;
|
723
|
+
/**
|
724
|
+
* Specifies the maximum anisotropy value clamp used by the sampler. Anisotropic filtering is
|
725
|
+
* enabled when {@link GPUSamplerDescriptor.maxAnisotropy} is > 1 and the implementation supports it.
|
726
|
+
* Anisotropic filtering improves the image quality of textures sampled at oblique viewing
|
727
|
+
* angles. Higher {@link GPUSamplerDescriptor.maxAnisotropy} values indicate the maximum ratio of
|
728
|
+
* anisotropy supported when filtering.
|
729
|
+
*
|
730
|
+
* Most implementations support {@link GPUSamplerDescriptor.maxAnisotropy} values in range
|
731
|
+
* between 1 and 16, inclusive. The used value of {@link GPUSamplerDescriptor.maxAnisotropy}
|
732
|
+
* will be clamped to the maximum value that the platform supports.
|
733
|
+
* The precise filtering behavior is implementation-dependent.
|
734
|
+
*/
|
735
|
+
maxAnisotropy?: number;
|
736
|
+
}
|
737
|
+
interface TgpuSampler {
|
738
|
+
readonly resourceType: 'sampler';
|
739
|
+
}
|
740
|
+
interface TgpuComparisonSampler {
|
741
|
+
readonly resourceType: 'sampler-comparison';
|
742
|
+
}
|
743
|
+
declare function sampler(props: SamplerProps): TgpuSampler;
|
744
|
+
declare function comparisonSampler(props: ComparisonSamplerProps): TgpuComparisonSampler;
|
745
|
+
declare function isSampler(resource: unknown): resource is TgpuSampler;
|
746
|
+
declare function isComparisonSampler(resource: unknown): resource is TgpuComparisonSampler;
|
747
|
+
|
654
748
|
interface Unwrapper {
|
655
749
|
readonly device: GPUDevice;
|
656
750
|
unwrap(resource: TgpuComputePipeline): GPUComputePipeline;
|
@@ -661,6 +755,8 @@ interface Unwrapper {
|
|
661
755
|
unwrap(resource: TgpuTexture): GPUTexture;
|
662
756
|
unwrap(resource: TgpuReadonlyTexture | TgpuWriteonlyTexture | TgpuMutableTexture | TgpuSampledTexture): GPUTextureView;
|
663
757
|
unwrap(resource: TgpuVertexLayout): GPUVertexBufferLayout;
|
758
|
+
unwrap(resource: TgpuSampler): GPUSampler;
|
759
|
+
unwrap(resource: TgpuComparisonSampler): GPUSampler;
|
664
760
|
}
|
665
761
|
|
666
762
|
interface WithCompute {
|
@@ -688,6 +784,7 @@ interface WithVertex<VertexOut extends IORecord = IORecord> {
|
|
688
784
|
interface WithFragment<Output extends FragmentOutConstrained = FragmentOutConstrained> {
|
689
785
|
withPrimitive(primitiveState: GPUPrimitiveState | undefined): WithFragment<Output>;
|
690
786
|
withDepthStencil(depthStencilState: GPUDepthStencilState | undefined): WithFragment<Output>;
|
787
|
+
withMultisample(multisampleState: GPUMultisampleState | undefined): WithFragment<Output>;
|
691
788
|
createPipeline(): TgpuRenderPipeline<Output>;
|
692
789
|
}
|
693
790
|
interface WithBinding {
|
@@ -959,100 +1056,6 @@ type RestrictVertexUsages<TData extends BaseData> = TData extends {
|
|
959
1056
|
readonly type: WgslTypeLiteral;
|
960
1057
|
} ? ('uniform' | 'storage' | 'vertex')[] : 'vertex'[];
|
961
1058
|
|
962
|
-
interface SamplerProps {
|
963
|
-
addressModeU?: GPUAddressMode;
|
964
|
-
addressModeV?: GPUAddressMode;
|
965
|
-
/**
|
966
|
-
* Specifies the address modes for the texture width, height, and depth
|
967
|
-
* coordinates, respectively.
|
968
|
-
*/
|
969
|
-
addressModeW?: GPUAddressMode;
|
970
|
-
/**
|
971
|
-
* Specifies the sampling behavior when the sample footprint is smaller than or equal to one
|
972
|
-
* texel.
|
973
|
-
*/
|
974
|
-
magFilter?: GPUFilterMode;
|
975
|
-
/**
|
976
|
-
* Specifies the sampling behavior when the sample footprint is larger than one texel.
|
977
|
-
*/
|
978
|
-
minFilter?: GPUFilterMode;
|
979
|
-
/**
|
980
|
-
* Specifies behavior for sampling between mipmap levels.
|
981
|
-
*/
|
982
|
-
mipmapFilter?: GPUMipmapFilterMode;
|
983
|
-
lodMinClamp?: number;
|
984
|
-
/**
|
985
|
-
* Specifies the minimum and maximum levels of detail, respectively, used internally when
|
986
|
-
* sampling a texture.
|
987
|
-
*/
|
988
|
-
lodMaxClamp?: number;
|
989
|
-
/**
|
990
|
-
* Specifies the maximum anisotropy value clamp used by the sampler. Anisotropic filtering is
|
991
|
-
* enabled when {@link GPUSamplerDescriptor.maxAnisotropy} is > 1 and the implementation supports it.
|
992
|
-
* Anisotropic filtering improves the image quality of textures sampled at oblique viewing
|
993
|
-
* angles. Higher {@link GPUSamplerDescriptor.maxAnisotropy} values indicate the maximum ratio of
|
994
|
-
* anisotropy supported when filtering.
|
995
|
-
*
|
996
|
-
* Most implementations support {@link GPUSamplerDescriptor.maxAnisotropy} values in range
|
997
|
-
* between 1 and 16, inclusive. The used value of {@link GPUSamplerDescriptor.maxAnisotropy}
|
998
|
-
* will be clamped to the maximum value that the platform supports.
|
999
|
-
* The precise filtering behavior is implementation-dependent.
|
1000
|
-
*/
|
1001
|
-
maxAnisotropy?: number;
|
1002
|
-
}
|
1003
|
-
interface ComparisonSamplerProps {
|
1004
|
-
compare: GPUCompareFunction;
|
1005
|
-
addressModeU?: GPUAddressMode;
|
1006
|
-
addressModeV?: GPUAddressMode;
|
1007
|
-
/**
|
1008
|
-
* Specifies the address modes for the texture width, height, and depth
|
1009
|
-
* coordinates, respectively.
|
1010
|
-
*/
|
1011
|
-
addressModeW?: GPUAddressMode;
|
1012
|
-
/**
|
1013
|
-
* Specifies the sampling behavior when the sample footprint is smaller than or equal to one
|
1014
|
-
* texel.
|
1015
|
-
*/
|
1016
|
-
magFilter?: GPUFilterMode;
|
1017
|
-
/**
|
1018
|
-
* Specifies the sampling behavior when the sample footprint is larger than one texel.
|
1019
|
-
*/
|
1020
|
-
minFilter?: GPUFilterMode;
|
1021
|
-
/**
|
1022
|
-
* Specifies behavior for sampling between mipmap levels.
|
1023
|
-
*/
|
1024
|
-
mipmapFilter?: GPUMipmapFilterMode;
|
1025
|
-
lodMinClamp?: number;
|
1026
|
-
/**
|
1027
|
-
* Specifies the minimum and maximum levels of detail, respectively, used internally when
|
1028
|
-
* sampling a texture.
|
1029
|
-
*/
|
1030
|
-
lodMaxClamp?: number;
|
1031
|
-
/**
|
1032
|
-
* Specifies the maximum anisotropy value clamp used by the sampler. Anisotropic filtering is
|
1033
|
-
* enabled when {@link GPUSamplerDescriptor.maxAnisotropy} is > 1 and the implementation supports it.
|
1034
|
-
* Anisotropic filtering improves the image quality of textures sampled at oblique viewing
|
1035
|
-
* angles. Higher {@link GPUSamplerDescriptor.maxAnisotropy} values indicate the maximum ratio of
|
1036
|
-
* anisotropy supported when filtering.
|
1037
|
-
*
|
1038
|
-
* Most implementations support {@link GPUSamplerDescriptor.maxAnisotropy} values in range
|
1039
|
-
* between 1 and 16, inclusive. The used value of {@link GPUSamplerDescriptor.maxAnisotropy}
|
1040
|
-
* will be clamped to the maximum value that the platform supports.
|
1041
|
-
* The precise filtering behavior is implementation-dependent.
|
1042
|
-
*/
|
1043
|
-
maxAnisotropy?: number;
|
1044
|
-
}
|
1045
|
-
interface TgpuSampler {
|
1046
|
-
readonly resourceType: 'sampler';
|
1047
|
-
}
|
1048
|
-
interface TgpuComparisonSampler {
|
1049
|
-
readonly resourceType: 'sampler-comparison';
|
1050
|
-
}
|
1051
|
-
declare function sampler(props: SamplerProps): TgpuSampler;
|
1052
|
-
declare function comparisonSampler(props: ComparisonSamplerProps): TgpuComparisonSampler;
|
1053
|
-
declare function isSampler(resource: unknown): resource is TgpuSampler;
|
1054
|
-
declare function isComparisonSampler(resource: unknown): resource is TgpuComparisonSampler;
|
1055
|
-
|
1056
1059
|
type TgpuLayoutEntryBase = {
|
1057
1060
|
/**
|
1058
1061
|
* Limits this resource's visibility to specific shader stages.
|
@@ -1180,13 +1183,13 @@ interface TgpuBufferUsage<TData extends BaseData = BaseData, TUsage extends Bind
|
|
1180
1183
|
readonly resourceType: 'buffer-usage';
|
1181
1184
|
readonly usage: TUsage;
|
1182
1185
|
readonly '~repr': Infer<TData>;
|
1183
|
-
value:
|
1186
|
+
value: InferGPU<TData>;
|
1184
1187
|
}
|
1185
1188
|
interface TgpuBufferUniform<TData extends BaseData> extends TgpuBufferUsage<TData, 'uniform'> {
|
1186
|
-
readonly value:
|
1189
|
+
readonly value: InferGPU<TData>;
|
1187
1190
|
}
|
1188
1191
|
interface TgpuBufferReadonly<TData extends BaseData> extends TgpuBufferUsage<TData, 'readonly'> {
|
1189
|
-
readonly value:
|
1192
|
+
readonly value: InferGPU<TData>;
|
1190
1193
|
}
|
1191
1194
|
interface TgpuFixedBufferUsage<TData extends BaseData> extends TgpuNamable {
|
1192
1195
|
readonly buffer: TgpuBuffer<TData>;
|
@@ -1234,11 +1237,13 @@ declare function workgroupVar<TDataType extends AnyWgslData>(dataType: TDataType
|
|
1234
1237
|
|
1235
1238
|
type ResolvableObject = SelfResolvable | TgpuBufferUsage | TgpuConst | TgpuDeclare | TgpuFn | TgpuComputeFn | TgpuFragmentFn | TgpuComputePipeline | TgpuRenderPipeline | TgpuVertexFn | TgpuSampler | TgpuAccessor | TgpuExternalTexture | TgpuTexture | TgpuAnyTextureView | TgpuVar | AnyVecInstance | AnyMatInstance | AnyData | TgpuFn<any, any>;
|
1236
1239
|
type Wgsl = Eventual<string | number | boolean | ResolvableObject>;
|
1237
|
-
declare const UnknownData:
|
1240
|
+
declare const UnknownData: {
|
1241
|
+
type: string;
|
1242
|
+
};
|
1238
1243
|
type UnknownData = typeof UnknownData;
|
1239
1244
|
type Resource = {
|
1240
1245
|
value: unknown;
|
1241
|
-
dataType: AnyWgslData | UnknownData;
|
1246
|
+
dataType: AnyWgslData | UnknownData | AbstractInt | AbstractFloat;
|
1242
1247
|
};
|
1243
1248
|
type TgpuShaderStage = 'compute' | 'vertex' | 'fragment';
|
1244
1249
|
interface FnToWgslOptions {
|