typegpu 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/chunk-2VTISQYP.cjs +10 -0
- package/chunk-2VTISQYP.cjs.map +1 -0
- package/chunk-CYZBRBPD.cjs +6 -0
- package/chunk-CYZBRBPD.cjs.map +1 -0
- package/chunk-SAMYKEUN.js +6 -0
- package/chunk-SAMYKEUN.js.map +1 -0
- package/chunk-WP6W72RY.js +10 -0
- package/chunk-WP6W72RY.js.map +1 -0
- package/data/index.cjs +1 -1
- package/data/index.cjs.map +1 -1
- package/data/index.d.cts +17 -5
- package/data/index.d.ts +17 -5
- package/data/index.js +1 -1
- package/index.cjs +14 -14
- package/index.cjs.map +1 -1
- package/index.d.cts +2 -2
- package/index.d.ts +2 -2
- package/index.js +14 -14
- package/index.js.map +1 -1
- package/{matrix-BXuR-7-A.d.ts → matrix-4h_aOtIE.d.cts} +9 -9
- package/{matrix-DzXuhOr3.d.cts → matrix-Domrg-ap.d.ts} +9 -9
- package/package.json +1 -1
- package/std/index.cjs +1 -1
- package/std/index.cjs.map +1 -1
- package/std/index.d.cts +266 -117
- package/std/index.d.ts +266 -117
- package/std/index.js +1 -1
- package/std/index.js.map +1 -1
- package/{tgpuConstant-BAmV6caN.d.cts → tgpuConstant-BSgcF4zi.d.cts} +383 -464
- package/{tgpuConstant-BAmV6caN.d.ts → tgpuConstant-BSgcF4zi.d.ts} +383 -464
- package/chunk-3K53YJSU.cjs +0 -10
- package/chunk-3K53YJSU.cjs.map +0 -1
- package/chunk-BF35NEOX.cjs +0 -43
- package/chunk-BF35NEOX.cjs.map +0 -1
- package/chunk-IH2QU6TV.js +0 -43
- package/chunk-IH2QU6TV.js.map +0 -1
- package/chunk-S27MYQ6U.js +0 -10
- package/chunk-S27MYQ6U.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/extractGpuValueGetter.ts","../src/core/valueProxyUtils.ts","../src/core/function/fnCore.ts","../src/core/resolve/resolveData.ts","../src/core/root/configurableImpl.ts","../src/data/compiledIO.ts","../src/data/dataIO.ts","../src/core/buffer/buffer.ts","../src/resolutionCtx.ts"],"names":["extractGpuValueGetter","object","$gpuValueOf","valueProxyHandler","target","prop","$providing","ctx","createCatchallGroup"],"mappings":"AAAA,+pCAAuP,wDAAkoB,SCKz2BA,EAAAA,CACdC,CAAAA,CAC4B,CAE5B,EAAA,CAAI,uBAAQA,CAAAA,4BAAAA,CAAiBC,mBAAW,GAAA,EAAM,UAAA,CAC5C,OAAQD,CAAAA,CAA6CC,mBAAW,CAAA,CAAE,IAAA,CAChED,CACF,CAGJ,CCJO,IAAME,CAAAA,CAGT,CACF,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,GAASC,mBAAAA,CAIb,OACED,CAAAA,GAAS,UAAA,EACTA,CAAAA,GAAS,MAAA,CAAO,WAAA,EAChBA,CAAAA,GAAS,MAAA,CAAO,WAAA,CAET,CAAA,CAAA,EAAMD,CAAAA,CAAO,QAAA,CAAS,CAAA,CAGxB,IAAI,KAAA,CACT,CACE,UAAA,CAAaG,CAAAA,EACX,CAAA,EAAA;ACyGM;ACXL;AA8BA;AAYT;ACvJ6B,CAAA;AC0HrB;AAsC0C;AAMxC;AAeN;AAsBE;AA6CS;AAea;AAkDsB;AAE9C,QAAA;AC7LK,8BAAA;ACoP0C,8BAAA;AC4OJ;AAkIHC","file":"/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import { $gpuValueOf } from './shared/symbols.ts';\nimport type { ResolutionCtx } from './types.ts';\n\nexport type GpuValueGetter = (ctx: ResolutionCtx) => unknown;\n\nexport function extractGpuValueGetter(\n object: unknown,\n): GpuValueGetter | undefined {\n // biome-ignore lint/suspicious/noExplicitAny: we're inspecting the value\n if (typeof (object as any)?.[$gpuValueOf] === 'function') {\n return (object as { [$gpuValueOf]: GpuValueGetter })[$gpuValueOf].bind(\n object,\n );\n }\n return undefined;\n}\n","import type { AnyData } from '../data/dataTypes.ts';\nimport type { BaseData } from '../data/wgslTypes.ts';\nimport {\n extractGpuValueGetter,\n type GpuValueGetter,\n} from '../extractGpuValueGetter.ts';\nimport { getName } from '../shared/meta.ts';\nimport { $providing, $wgslDataType } from '../shared/symbols.ts';\nimport { getTypeForPropAccess } from '../tgsl/generationHelpers.ts';\nimport type { ResolutionCtx, SelfResolvable } from '../types.ts';\n\nexport const valueProxyHandler: ProxyHandler<\n & SelfResolvable\n & { readonly [$wgslDataType]: BaseData }\n> = {\n get(target, prop) {\n if (prop in target) {\n return Reflect.get(target, prop);\n }\n\n if (prop === $providing) {\n return undefined;\n }\n\n if (\n prop === 'toString' ||\n prop === Symbol.toStringTag ||\n prop === Symbol.toPrimitive\n ) {\n return () => target.toString();\n }\n\n return new Proxy(\n {\n '~resolve': (ctx: ResolutionCtx) =>\n `${ctx.resolve(target)}.${String(prop)}`,\n\n toString: () =>\n `.value(...).${String(prop)}:${getName(target) ?? '<unnamed>'}`,\n\n [$wgslDataType]: getTypeForPropAccess(\n target[$wgslDataType] as AnyData,\n String(prop),\n ) as BaseData,\n },\n valueProxyHandler,\n );\n },\n};\n\nexport function getGpuValueRecursively<T>(\n ctx: ResolutionCtx,\n value: unknown,\n): T {\n let unwrapped = value;\n let valueGetter: GpuValueGetter | undefined;\n\n // biome-ignore lint/suspicious/noAssignInExpressions: it's exactly what we want biome\n while (valueGetter = extractGpuValueGetter(unwrapped)) {\n unwrapped = valueGetter(ctx);\n }\n\n return unwrapped as T;\n}\n","import { FuncParameterType } from 'tinyest';\nimport { getAttributesString } from '../../data/attributes.ts';\nimport { undecorate } from '../../data/decorateUtils.ts';\nimport type { AnyData } from '../../data/dataTypes.ts';\nimport { snip } from '../../data/snippet.ts';\nimport {\n isWgslData,\n isWgslStruct,\n Void,\n type WgslStruct,\n} from '../../data/wgslTypes.ts';\nimport { MissingLinksError } from '../../errors.ts';\nimport { getMetaData, getName, setName } from '../../shared/meta.ts';\nimport type { ResolutionCtx } from '../../types.ts';\nimport {\n applyExternals,\n type ExternalMap,\n replaceExternalsInWgsl,\n} from '../resolve/externals.ts';\nimport { extractArgs } from './extractArgs.ts';\nimport type { Implementation } from './fnTypes.ts';\n\nexport interface FnCore {\n applyExternals(newExternals: ExternalMap): void;\n resolve(\n ctx: ResolutionCtx,\n argTypes: AnyData[],\n returnType: AnyData,\n ): string;\n}\n\nexport function createFnCore(\n implementation: Implementation,\n fnAttribute = '',\n): FnCore {\n /**\n * External application has to be deferred until resolution because\n * some externals can reference the owner function which has not been\n * initialized yet (like when accessing the Output struct of a vertex\n * entry fn).\n */\n const externalsToApply: ExternalMap[] = [];\n\n const core = {\n applyExternals(newExternals: ExternalMap): void {\n externalsToApply.push(newExternals);\n },\n\n resolve(\n ctx: ResolutionCtx,\n argTypes: AnyData[],\n returnType: AnyData,\n ): string {\n const externalMap: ExternalMap = {};\n\n for (const externals of externalsToApply) {\n applyExternals(externalMap, externals);\n }\n\n const id = ctx.names.makeUnique(getName(this));\n\n if (typeof implementation === 'string') {\n const replacedImpl = replaceExternalsInWgsl(\n ctx,\n externalMap,\n implementation,\n );\n\n let header = '';\n let body = '';\n\n if (fnAttribute !== '') {\n const input = isWgslStruct(argTypes[0])\n ? `(in: ${ctx.resolve(argTypes[0])})`\n : '()';\n\n const attributes = isWgslData(returnType)\n ? getAttributesString(returnType)\n : '';\n const output = returnType !== Void\n ? isWgslStruct(returnType)\n ? `-> ${ctx.resolve(returnType)}`\n : `-> ${attributes !== '' ? attributes : '@location(0)'} ${\n ctx.resolve(returnType)\n }`\n : '';\n\n header = `${input} ${output} `;\n body = replacedImpl;\n } else {\n const providedArgs = extractArgs(replacedImpl);\n\n if (providedArgs.args.length !== argTypes.length) {\n throw new Error(\n `WGSL implementation has ${providedArgs.args.length} arguments, while the shell has ${argTypes.length} arguments.`,\n );\n }\n\n const input = providedArgs.args.map((argInfo, i) =>\n `${argInfo.identifier}: ${\n checkAndReturnType(\n ctx,\n `parameter ${argInfo.identifier}`,\n argInfo.type,\n argTypes[i],\n )\n }`\n ).join(', ');\n\n const output = returnType === Void ? '' : `-> ${\n checkAndReturnType(\n ctx,\n 'return type',\n providedArgs.ret?.type,\n returnType,\n )\n }`;\n\n header = `(${input}) ${output}`;\n\n body = replacedImpl.slice(providedArgs.range.end);\n }\n\n ctx.addDeclaration(`${fnAttribute}fn ${id}${header}${body}`);\n } else {\n // get data generated by the plugin\n const pluginData = getMetaData(implementation);\n\n if (pluginData?.externals) {\n const missing = Object.fromEntries(\n Object.entries(pluginData.externals).filter(\n ([name]) => !(name in externalMap),\n ),\n );\n\n applyExternals(externalMap, missing);\n }\n\n const ast = pluginData?.ast;\n if (!ast) {\n throw new Error(\n \"Missing metadata for tgpu.fn function body (either missing 'kernel' directive, or misconfigured `unplugin-typegpu`)\",\n );\n }\n\n // verify all required externals are present\n const missingExternals = ast.externalNames.filter(\n (name) => !(name in externalMap),\n );\n if (missingExternals.length > 0) {\n throw new MissingLinksError(getName(this), missingExternals);\n }\n\n // If an entrypoint implementation has a second argument, it represents the output schema.\n // We look at the identifier chosen by the user and add it to externals.\n const maybeSecondArg = ast.params[1];\n if (\n maybeSecondArg && maybeSecondArg.type === 'i' && fnAttribute !== ''\n ) {\n applyExternals(\n externalMap,\n {\n [maybeSecondArg.name]: undecorate(returnType),\n },\n );\n }\n\n // generate wgsl string\n const { head, body } = ctx.fnToWgsl({\n args: argTypes.map((arg, i) =>\n snip(\n ast.params[i]?.type === FuncParameterType.identifier\n ? ast.params[i].name\n : `_arg_${i}`,\n arg,\n )\n ),\n argAliases: Object.fromEntries(\n ast.params.flatMap((param, i) =>\n param.type === FuncParameterType.destructuredObject\n ? param.props.map(({ name, alias }) => [\n alias,\n snip(\n `_arg_${i}.${name}`,\n (argTypes[i] as WgslStruct)\n .propTypes[name],\n ),\n ])\n : []\n ),\n ),\n returnType,\n body: ast.body,\n externalMap,\n });\n\n ctx.addDeclaration(\n `${fnAttribute}fn ${id}${ctx.resolve(head)}${ctx.resolve(body)}`,\n );\n }\n\n return id;\n },\n };\n\n // The implementation could have been given a name by a bundler plugin,\n // so we try to transfer it to the core.\n const maybeName = getName(implementation);\n if (maybeName !== undefined) {\n setName(core, maybeName);\n }\n\n return core;\n}\n\nfunction checkAndReturnType(\n ctx: ResolutionCtx,\n name: string,\n wgslType: string | undefined,\n jsType: unknown,\n) {\n const resolvedJsType = ctx.resolve(jsType).replace(/\\s/g, '');\n\n if (!wgslType) {\n return resolvedJsType;\n }\n\n const resolvedWgslType = wgslType.replace(/\\s/g, '');\n\n if (resolvedWgslType !== resolvedJsType) {\n throw new Error(\n `Type mismatch between TGPU shell and WGSL code string: ${name}, JS type \"${resolvedJsType}\", WGSL type \"${resolvedWgslType}\".`,\n );\n }\n\n return wgslType;\n}\n","import { getAttributesString } from '../../data/attributes.ts';\nimport {\n type AnyData,\n type Disarray,\n isLooseData,\n type Unstruct,\n} from '../../data/dataTypes.ts';\nimport { formatToWGSLType } from '../../data/vertexFormatData.ts';\nimport type {\n AnyWgslData,\n BaseData,\n Bool,\n F16,\n F32,\n I32,\n Mat2x2f,\n Mat3x3f,\n Mat4x4f,\n U32,\n Vec2b,\n Vec2f,\n Vec2h,\n Vec2i,\n Vec2u,\n Vec3b,\n Vec3f,\n Vec3h,\n Vec3i,\n Vec3u,\n Vec4b,\n Vec4f,\n Vec4h,\n Vec4i,\n Vec4u,\n WgslArray,\n WgslStruct,\n} from '../../data/wgslTypes.ts';\nimport { getName } from '../../shared/meta.ts';\nimport { assertExhaustive } from '../../shared/utilityTypes.ts';\nimport type { ResolutionCtx } from '../../types.ts';\nimport { isAttribute } from '../vertexLayout/connectAttributesToShader.ts';\n\n/**\n * Schemas for which their `type` property directly\n * translates to the resulting WGSL code.\n */\nconst identityTypes = [\n 'bool',\n 'f32',\n 'f16',\n 'i32',\n 'u32',\n 'vec2f',\n 'vec3f',\n 'vec4f',\n 'vec2h',\n 'vec3h',\n 'vec4h',\n 'vec2i',\n 'vec3i',\n 'vec4i',\n 'vec2u',\n 'vec3u',\n 'vec4u',\n 'vec2<bool>',\n 'vec3<bool>',\n 'vec4<bool>',\n 'mat2x2f',\n 'mat3x3f',\n 'mat4x4f',\n];\n\ntype IdentityType =\n | Bool\n | F32\n | F16\n | I32\n | U32\n | Vec2f\n | Vec3f\n | Vec4f\n | Vec2h\n | Vec3h\n | Vec4h\n | Vec2i\n | Vec3i\n | Vec4i\n | Vec2u\n | Vec3u\n | Vec4u\n | Vec2b\n | Vec3b\n | Vec4b\n | Mat2x2f\n | Mat3x3f\n | Mat4x4f;\n\nfunction isIdentityType(data: AnyWgslData): data is IdentityType {\n return identityTypes.includes(data.type);\n}\n\n/**\n * Resolves a single property of a struct.\n * @param ctx - The resolution context.\n * @param key - The key of the property.\n * @param property - The property itself.\n *\n * @returns The resolved property string.\n */\nfunction resolveStructProperty(\n ctx: ResolutionCtx,\n [key, property]: [string, BaseData],\n) {\n return ` ${getAttributesString(property)}${key}: ${\n ctx.resolve(property as AnyWgslData)\n },\\n`;\n}\n\n/**\n * Resolves a struct and adds its declaration to the resolution context.\n * @param ctx - The resolution context.\n * @param struct - The struct to resolve.\n *\n * @returns The resolved struct name.\n */\nfunction resolveStruct(ctx: ResolutionCtx, struct: WgslStruct) {\n const id = ctx.names.makeUnique(getName(struct));\n\n ctx.addDeclaration(`\\\nstruct ${id} {\n${\n Object.entries(struct.propTypes as Record<string, BaseData>)\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(getName(unstruct));\n\n ctx.addDeclaration(`\\\nstruct ${id} {\n${\n Object.entries(unstruct.propTypes as Record<string, BaseData>)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [\n prop[0],\n formatToWGSLType[prop[1].format],\n ])\n : resolveStructProperty(ctx, prop)\n )\n .join('')\n }\n}`);\n\n return id;\n}\n\n/**\n * Resolves an array.\n * @param ctx - The resolution context.\n * @param array - The array to resolve.\n *\n * @returns The resolved array name along with its element type and count (if not runtime-sized).\n *\n * @example\n * ```ts\n * resolveArray(ctx, d.arrayOf(d.u32, 0)); // 'array<u32>' (not a real pattern, a function is preferred)\n * resolveArray(ctx, d.arrayOf(d.u32, 5)); // 'array<u32, 5>'\n * ```\n */\nfunction resolveArray(ctx: ResolutionCtx, array: WgslArray) {\n const element = ctx.resolve(array.elementType as AnyWgslData);\n\n return array.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${array.elementCount}>`;\n}\n\nfunction resolveDisarray(ctx: ResolutionCtx, disarray: Disarray) {\n const element = ctx.resolve(\n isAttribute(disarray.elementType)\n ? formatToWGSLType[disarray.elementType.format]\n : (disarray.elementType as AnyWgslData),\n );\n\n return disarray.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${disarray.elementCount}>`;\n}\n\n/**\n * Resolves a WGSL data-type schema to a string.\n * @param ctx - The resolution context.\n * @param data - The data-type to resolve.\n *\n * @returns The resolved data-type string.\n */\nexport function resolveData(ctx: ResolutionCtx, data: AnyData): string {\n if (isLooseData(data)) {\n if (data.type === 'unstruct') {\n return resolveUnstruct(ctx, data);\n }\n\n if (data.type === 'disarray') {\n return resolveDisarray(ctx, data);\n }\n\n if (data.type === 'loose-decorated') {\n return ctx.resolve(\n isAttribute(data.inner)\n ? formatToWGSLType[data.inner.format]\n : data.inner,\n );\n }\n\n return ctx.resolve(formatToWGSLType[data.type]);\n }\n\n if (isIdentityType(data)) {\n return data.type;\n }\n\n if (data.type === 'struct') {\n return resolveStruct(ctx, data);\n }\n\n if (data.type === 'array') {\n return resolveArray(ctx, data);\n }\n\n if (data.type === 'atomic') {\n return `atomic<${resolveData(ctx, data.inner)}>`;\n }\n\n if (data.type === 'decorated') {\n return ctx.resolve(data.inner as AnyWgslData);\n }\n\n if (data.type === 'ptr') {\n if (data.addressSpace === 'storage') {\n return `ptr<storage, ${ctx.resolve(data.inner)}, ${\n data.access === 'read-write' ? 'read_write' : data.access\n }>`;\n }\n return `ptr<${data.addressSpace}, ${ctx.resolve(data.inner)}>`;\n }\n\n if (\n data.type === 'abstractInt' || data.type === 'abstractFloat' ||\n data.type === 'void' || data.type === 'u16'\n ) {\n throw new Error(`${data.type} has no representation in WGSL`);\n }\n\n assertExhaustive(data, 'resolveData');\n}\n","import type { Infer } from '../../shared/repr.ts';\nimport type { AnyWgslData } from '../../data/wgslTypes.ts';\nimport type { TgpuBufferUsage } from '../buffer/bufferUsage.ts';\nimport type { TgpuFn } from '../function/tgpuFn.ts';\nimport {\n isAccessor,\n type TgpuAccessor,\n type TgpuSlot,\n} from '../slot/slotTypes.ts';\nimport type { Configurable } from './rootTypes.ts';\n\nexport class ConfigurableImpl implements Configurable {\n constructor(readonly bindings: [TgpuSlot<unknown>, unknown][]) {}\n\n with<T extends AnyWgslData>(\n slot: TgpuSlot<T> | TgpuAccessor<T>,\n value: T | TgpuFn<() => T> | TgpuBufferUsage<T> | Infer<T>,\n ): Configurable {\n return new ConfigurableImpl([\n ...this.bindings,\n [isAccessor(slot) ? slot.slot : slot, value],\n ]);\n }\n\n pipe(transform: (cfg: Configurable) => Configurable): Configurable {\n const newCfg = transform(this);\n return new ConfigurableImpl([\n ...this.bindings,\n ...newCfg.bindings,\n ]);\n }\n}\n","import { roundUp } from '../mathUtils.ts';\nimport type { Infer } from '../shared/repr.ts';\nimport { alignmentOf } from './alignmentOf.ts';\nimport { isDisarray, isUnstruct } from './dataTypes.ts';\nimport { offsetsForProps } from './offsets.ts';\nimport { sizeOf } from './sizeOf.ts';\nimport { formatToWGSLType, isPackedData } from './vertexFormatData.ts';\nimport * as wgsl from './wgslTypes.ts';\n\nexport const EVAL_ALLOWED_IN_ENV: boolean = (() => {\n try {\n new Function('return true');\n return true;\n } catch {\n return false;\n }\n})();\n\nconst compiledWriters = new WeakMap<\n wgsl.BaseData,\n (\n output: DataView,\n offset: number,\n value: unknown,\n littleEndian?: boolean,\n ) => void\n>();\n\nconst typeToPrimitive = {\n u32: 'u32',\n vec2u: 'u32',\n vec3u: 'u32',\n vec4u: 'u32',\n u16: 'u16',\n\n i32: 'i32',\n vec2i: 'i32',\n vec3i: 'i32',\n vec4i: 'i32',\n\n f32: 'f32',\n vec2f: 'f32',\n vec3f: 'f32',\n vec4f: 'f32',\n\n f16: 'f16',\n vec2h: 'f16',\n vec3h: 'f16',\n vec4h: 'f16',\n\n mat2x2f: 'f32',\n mat3x3f: 'f32',\n mat4x4f: 'f32',\n} as const;\n\nconst vertexFormatToPrimitive = {\n uint8: 'u8',\n uint8x2: 'u8',\n uint8x4: 'u8',\n sint8: 'i8',\n sint8x2: 'i8',\n sint8x4: 'i8',\n unorm8: 'u8',\n unorm8x2: 'u8',\n unorm8x4: 'u8',\n snorm8: 'i8',\n snorm8x2: 'i8',\n snorm8x4: 'i8',\n uint16: 'u16',\n uint16x2: 'u16',\n uint16x4: 'u16',\n sint16: 'i16',\n sint16x2: 'i16',\n sint16x4: 'i16',\n unorm16: 'u16',\n unorm16x2: 'u16',\n unorm16x4: 'u16',\n snorm16: 'i16',\n snorm16x2: 'i16',\n snorm16x4: 'i16',\n float16: 'f16',\n float16x2: 'f16',\n float16x4: 'f16',\n float32: 'f32',\n float32x2: 'f32',\n float32x3: 'f32',\n float32x4: 'f32',\n uint32: 'u32',\n uint32x2: 'u32',\n uint32x3: 'u32',\n uint32x4: 'u32',\n sint32: 'i32',\n sint32x2: 'i32',\n sint32x3: 'i32',\n sint32x4: 'i32',\n} as const;\n\nconst primitiveToWriteFunction = {\n u32: 'setUint32',\n i32: 'setInt32',\n f32: 'setFloat32',\n u16: 'setUint16',\n i16: 'setInt16',\n f16: 'setFloat16',\n u8: 'setUint8',\n i8: 'setInt8',\n} as const;\n\n/**\n * @privateRemarks\n * based on the `Channel Formats` table https://www.w3.org/TR/WGSL/#texel-formats\n */\nconst vertexFormatValueTransform = {\n unorm8: (value: string) => `Math.round(${value} * 255)`,\n unorm8x2: (value: string) => `Math.round(${value} * 255)`,\n unorm8x4: (value: string) => `Math.round(${value} * 255)`,\n snorm8: (value: string) => `Math.round(${value} * 127)`,\n snorm8x2: (value: string) => `Math.round(${value} * 127)`,\n snorm8x4: (value: string) => `Math.round(${value} * 127)`,\n unorm16: (value: string) => `Math.round(${value} * 65535)`,\n unorm16x2: (value: string) => `Math.round(${value} * 65535)`,\n unorm16x4: (value: string) => `Math.round(${value} * 65535)`,\n snorm16: (value: string) => `Math.round(${value} * 32767)`,\n snorm16x2: (value: string) => `Math.round(${value} * 32767)`,\n snorm16x4: (value: string) => `Math.round(${value} * 32767)`,\n} as const;\n\nconst specialPackedFormats = {\n 'unorm10-10-10-2': {\n writeFunction: 'setUint32',\n generator: (offsetExpr: string, valueExpr: string) =>\n `output.setUint32(${offsetExpr}, ((${valueExpr}.x*1023&0x3FF)<<22)|((${valueExpr}.y*1023&0x3FF)<<12)|((${valueExpr}.z*1023&0x3FF)<<2)|(${valueExpr}.w*3&3), littleEndian);\\n`,\n },\n 'unorm8x4-bgra': {\n writeFunction: 'setUint8',\n generator: (offsetExpr: string, valueExpr: string) => {\n const bgraComponents = ['z', 'y', 'x', 'w'];\n let code = '';\n for (let i = 0; i < 4; i++) {\n code +=\n `output.setUint8((${offsetExpr} + ${i}), Math.round(${valueExpr}.${\n bgraComponents[i]\n } * 255), littleEndian);\\n`;\n }\n return code;\n },\n },\n} as const;\n\nexport function buildWriter(\n node: wgsl.BaseData,\n offsetExpr: string,\n valueExpr: string,\n): string {\n if (wgsl.isAtomic(node) || wgsl.isDecorated(node)) {\n return buildWriter(node.inner, offsetExpr, valueExpr);\n }\n\n if (wgsl.isWgslStruct(node) || isUnstruct(node)) {\n const propOffsets = offsetsForProps(node);\n let code = '';\n for (const [key, propOffset] of Object.entries(propOffsets)) {\n const subSchema = node.propTypes[key];\n if (!subSchema) continue;\n code += buildWriter(\n subSchema,\n `(${offsetExpr} + ${propOffset.offset})`,\n `${valueExpr}.${key}`,\n );\n }\n return code;\n }\n\n if (wgsl.isWgslArray(node) || isDisarray(node)) {\n const elementSize = roundUp(\n sizeOf(node.elementType),\n alignmentOf(node),\n );\n let code = '';\n\n code += `for (let i = 0; i < ${node.elementCount}; i++) {\\n`;\n code += buildWriter(\n node.elementType,\n `(${offsetExpr} + i * ${elementSize})`,\n `${valueExpr}[i]`,\n );\n code += '}\\n';\n\n return code;\n }\n\n if (wgsl.isVec(node)) {\n const primitive = typeToPrimitive[node.type];\n let code = '';\n const writeFunc = primitiveToWriteFunction[primitive];\n const components = ['x', 'y', 'z', 'w'];\n const count = wgsl.isVec2(node) ? 2 : wgsl.isVec3(node) ? 3 : 4;\n\n for (let i = 0; i < count; i++) {\n code += `output.${writeFunc}((${offsetExpr} + ${i * 4}), ${valueExpr}.${\n components[i]\n }, littleEndian);\\n`;\n }\n return code;\n }\n\n if (wgsl.isMat(node)) {\n const primitive = typeToPrimitive[node.type];\n const writeFunc = primitiveToWriteFunction[primitive];\n\n const matSize = wgsl.isMat2x2f(node) ? 2 : wgsl.isMat3x3f(node) ? 3 : 4;\n const elementCount = matSize * matSize;\n const rowStride = roundUp(matSize * 4, 8);\n\n let code = '';\n for (let i = 0; i < elementCount; i++) {\n const colIndex = Math.floor(i / matSize);\n const rowIndex = i % matSize;\n const byteOffset = colIndex * rowStride + rowIndex * 4;\n\n code +=\n `output.${writeFunc}((${offsetExpr} + ${byteOffset}), ${valueExpr}.columns[${colIndex}].${\n ['x', 'y', 'z', 'w'][rowIndex]\n }, littleEndian);\\n`;\n }\n\n return code;\n }\n\n if (isPackedData(node)) {\n const formatName = node.type;\n\n if (formatName in specialPackedFormats) {\n const handler =\n specialPackedFormats[formatName as keyof typeof specialPackedFormats];\n return handler.generator(offsetExpr, valueExpr);\n }\n\n const primitive = vertexFormatToPrimitive[\n formatName as keyof typeof vertexFormatToPrimitive\n ];\n const writeFunc = primitiveToWriteFunction[primitive];\n const wgslType = formatToWGSLType[formatName];\n const componentCount = wgsl.isVec4(wgslType)\n ? 4\n : wgsl.isVec3(wgslType)\n ? 3\n : wgsl.isVec2(wgslType)\n ? 2\n : 1;\n const componentSize = primitive === 'u8' || primitive === 'i8'\n ? 1\n : primitive === 'u16' || primitive === 'i16' || primitive === 'f16'\n ? 2\n : 4;\n const components = ['x', 'y', 'z', 'w'];\n const transform = vertexFormatValueTransform[\n formatName as keyof typeof vertexFormatValueTransform\n ];\n\n let code = '';\n for (let i = 0; i < componentCount; i++) {\n const accessor = componentCount === 1\n ? valueExpr\n : `${valueExpr}.${components[i]}`;\n const value = transform ? transform(accessor) : accessor;\n code += `output.${writeFunc}((${offsetExpr} + ${\n i * componentSize\n }), ${value}, littleEndian);\\n`;\n }\n\n return code;\n }\n\n if (!Object.hasOwn(typeToPrimitive, node.type)) {\n throw new Error(\n `Primitive ${node.type} is unsupported by compiled writer`,\n );\n }\n\n const primitive = typeToPrimitive[node.type as keyof typeof typeToPrimitive];\n return `output.${\n primitiveToWriteFunction[primitive]\n }(${offsetExpr}, ${valueExpr}, littleEndian);\\n`;\n}\n\nexport function getCompiledWriterForSchema<T extends wgsl.BaseData>(\n schema: T,\n):\n | ((\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n ) => void)\n | undefined {\n if (!EVAL_ALLOWED_IN_ENV) {\n console.warn(\n 'This environment does not allow eval - using default writer as fallback',\n );\n return undefined;\n }\n\n if (compiledWriters.has(schema)) {\n return compiledWriters.get(schema) as (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n ) => void;\n }\n\n try {\n const body = buildWriter(schema, 'offset', 'value');\n\n const fn = new Function(\n 'output',\n 'offset',\n 'value',\n 'littleEndian=true',\n body,\n ) as (\n output: DataView,\n offset: number,\n value: Infer<T> | unknown,\n littleEndian?: boolean,\n ) => void;\n\n compiledWriters.set(schema, fn);\n\n return fn;\n } catch (error) {\n console.warn(\n `Failed to compile writer for schema: ${schema}\\nReason: ${\n error instanceof Error ? error.message : String(error)\n }\\nFalling back to default writer`,\n );\n }\n}\n","import type { ISerialInput, ISerialOutput } from 'typed-binary';\nimport type { Infer, InferRecord } from '../shared/repr.ts';\nimport alignIO from './alignIO.ts';\nimport { alignmentOf, customAlignmentOf } from './alignmentOf.ts';\nimport type {\n AnyConcreteData,\n AnyData,\n Disarray,\n LooseDecorated,\n Unstruct,\n} from './dataTypes.ts';\nimport { mat2x2f, mat3x3f, mat4x4f } from './matrix.ts';\nimport { sizeOf } from './sizeOf.ts';\nimport {\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n} from './vector.ts';\nimport type * as wgsl from './wgslTypes.ts';\n\ntype DataWriter<TSchema extends wgsl.BaseData> = (\n output: ISerialOutput,\n schema: TSchema,\n value: Infer<TSchema>,\n) => void;\n\ntype DataReader<TSchema extends wgsl.BaseData> = (\n input: ISerialInput,\n schema: TSchema,\n) => Infer<TSchema>;\n\ntype CompleteDataWriters = {\n [TType in AnyConcreteData['type']]: DataWriter<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\ntype CompleteDataReaders = {\n [TType in AnyConcreteData['type']]: DataReader<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\nconst dataWriters = {\n bool() {\n throw new Error('Booleans are not host-shareable');\n },\n\n f32(output, _schema: wgsl.F32, value: number) {\n output.writeFloat32(value);\n },\n\n f16(output, _schema: wgsl.F16, value: number) {\n output.writeFloat16(value);\n },\n\n i32(output, _schema: wgsl.I32, value: number) {\n output.writeInt32(value);\n },\n\n u32(output, _schema: wgsl.U32, value: number) {\n output.writeUint32(value);\n },\n\n u16(output, _schema: wgsl.U16, value: number) {\n output.writeUint16(value);\n },\n\n vec2f(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n\n vec2h(output, _, value: wgsl.v2h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n\n vec2i(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n\n vec2u(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n\n 'vec2<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n vec3f(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n\n vec3h(output, _, value: wgsl.v3h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n },\n\n vec3i(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n\n vec3u(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n\n 'vec3<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n vec4f(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n\n vec4h(output, _, value: wgsl.v4h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n\n vec4i(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n\n vec4u(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n\n 'vec4<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n mat2x2f(output, _, value: wgsl.m2x2f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat3x3f(output, _, value: wgsl.m3x3f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat4x4f(output, _, value: wgsl.m4x4f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n struct(\n output,\n schema: wgsl.WgslStruct,\n value: InferRecord<Record<string, wgsl.BaseData>>,\n ) {\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(output, alignmentOf(property as wgsl.BaseData));\n writeData(output, property as wgsl.BaseData, value[key]);\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(Math.round(value * 255));\n },\n unorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(Math.round(value.x * 255));\n output.writeUint8(Math.round(value.y * 255));\n },\n unorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(Math.round(value.x * 255));\n output.writeUint8(Math.round(value.y * 255));\n output.writeUint8(Math.round(value.z * 255));\n output.writeUint8(Math.round(value.w * 255));\n },\n snorm8(output, _, value: number) {\n output.writeInt8(Math.round(value * 127));\n },\n snorm8x2(output, _, value: wgsl.v2f) {\n output.writeInt8(Math.round(value.x * 127));\n output.writeInt8(Math.round(value.y * 127));\n },\n snorm8x4(output, _, value: wgsl.v4f) {\n output.writeInt8(Math.round(value.x * 127));\n output.writeInt8(Math.round(value.y * 127));\n output.writeInt8(Math.round(value.z * 127));\n output.writeInt8(Math.round(value.w * 127));\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.writeInt16(Math.round(value * 32767));\n },\n snorm16x2(output, _, value: wgsl.v2f) {\n output.writeInt16(Math.round(value.x * 32767));\n output.writeInt16(Math.round(value.y * 32767));\n },\n snorm16x4(output, _, value: wgsl.v4f) {\n output.writeInt16(Math.round(value.x * 32767));\n output.writeInt16(Math.round(value.y * 32767));\n output.writeInt16(Math.round(value.z * 32767));\n output.writeInt16(Math.round(value.w * 32767));\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.y * 1023) & 1023) << 12; // g (10 bits)\n packed |= ((value.z * 1023) & 1023) << 2; // b (10 bits)\n packed |= (value.w * 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 const propTypes = schema.propTypes as Record<string, wgsl.BaseData>;\n for (const [key, property] of Object.entries(propTypes)) {\n dataWriters[property.type]?.(output, property, value[key]);\n }\n },\n\n 'loose-decorated'(output, schema: LooseDecorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n const writer = dataWriters[(schema.inner as AnyData)?.type];\n writer?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataWriters as Record<\n string,\n (output: ISerialOutput, schema: unknown, value: unknown) => void\n>;\n\nexport function writeData<TData extends wgsl.BaseData>(\n output: ISerialOutput,\n schema: TData,\n value: Infer<TData>,\n): void {\n const writer = dataWriters[schema.type];\n if (!writer) {\n throw new Error(`Cannot write data of type '${schema.type}'.`);\n }\n\n writer(output, schema, value);\n}\n\nconst dataReaders = {\n bool(): boolean {\n throw new Error('Booleans are not host-shareable');\n },\n\n f32(input: ISerialInput): number {\n return input.readFloat32();\n },\n\n f16(input: ISerialInput): number {\n return input.readFloat16();\n },\n\n i32(input: ISerialInput): number {\n return input.readInt32();\n },\n\n u32(input: ISerialInput): number {\n return input.readUint32();\n },\n\n u16(input: ISerialInput): number {\n return input.readUint16();\n },\n\n vec2f(input: ISerialInput): wgsl.v2f {\n return vec2f(input.readFloat32(), input.readFloat32());\n },\n\n vec3f(input: ISerialInput): wgsl.v3f {\n return vec3f(input.readFloat32(), input.readFloat32(), input.readFloat32());\n },\n\n vec4f(input: ISerialInput): wgsl.v4f {\n return vec4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n vec2h(input): wgsl.v2h {\n return vec2h(input.readFloat16(), input.readFloat16());\n },\n\n vec3h(input: ISerialInput): wgsl.v3h {\n return vec3h(input.readFloat16(), input.readFloat16(), input.readFloat16());\n },\n\n vec4h(input: ISerialInput): wgsl.v4h {\n return vec4h(\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n );\n },\n\n vec2i(input): wgsl.v2i {\n return vec2i(input.readInt32(), input.readInt32());\n },\n\n vec3i(input: ISerialInput): wgsl.v3i {\n return vec3i(input.readInt32(), input.readInt32(), input.readInt32());\n },\n\n vec4i(input: ISerialInput): wgsl.v4i {\n return vec4i(\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n );\n },\n\n vec2u(input): wgsl.v2u {\n return vec2u(input.readUint32(), input.readUint32());\n },\n\n vec3u(input: ISerialInput): wgsl.v3u {\n return vec3u(input.readUint32(), input.readUint32(), input.readUint32());\n },\n\n vec4u(input: ISerialInput): wgsl.v4u {\n return vec4u(\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n );\n },\n\n 'vec2<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n 'vec3<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n 'vec4<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n mat2x2f(input: ISerialInput): wgsl.m2x2f {\n return mat2x2f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n mat3x3f(input: ISerialInput): wgsl.m3x3f {\n const skipOneAfter = () => {\n const value = input.readFloat32();\n input.readFloat32(); // skipping;\n return value;\n };\n\n return mat3x3f(\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n );\n },\n\n mat4x4f(input: ISerialInput): wgsl.m4x4f {\n return mat4x4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n struct(input: ISerialInput, schema: wgsl.WgslStruct) {\n const alignment = alignmentOf(schema);\n alignIO(input, alignment);\n const result = {} as Record<string, unknown>;\n\n const propTypes = schema.propTypes as Record<string, wgsl.BaseData>;\n for (const [key, property] of Object.entries(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.readInt8() / 127,\n snorm8x2: (i) => vec2f(i.readInt8() / 127, i.readInt8() / 127),\n snorm8x4: (i) =>\n vec4f(\n i.readInt8() / 127,\n i.readInt8() / 127,\n i.readInt8() / 127,\n i.readInt8() / 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.readInt16() / 32767,\n snorm16x2: (i): wgsl.v2f =>\n vec2f(i.readInt16() / 32767, i.readInt16() / 32767),\n snorm16x4: (i): wgsl.v4f =>\n vec4f(\n i.readInt16() / 32767,\n i.readInt16() / 32767,\n i.readInt16() / 32767,\n i.readInt16() / 32767,\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 const propTypes = schema.propTypes as Record<string, wgsl.BaseData>;\n for (const [key, property] of Object.entries(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 { BufferReader, BufferWriter, getSystemEndianness } from 'typed-binary';\nimport { getCompiledWriterForSchema } from '../../data/compiledIO.ts';\nimport { readData, writeData } from '../../data/dataIO.ts';\nimport { getWriteInstructions } from '../../data/partialIO.ts';\nimport { sizeOf } from '../../data/sizeOf.ts';\nimport type { BaseData } from '../../data/wgslTypes.ts';\nimport { isWgslData } from '../../data/wgslTypes.ts';\nimport type { StorageFlag } from '../../extension.ts';\nimport type { TgpuNamable } from '../../shared/meta.ts';\nimport { getName, setName } from '../../shared/meta.ts';\nimport type {\n Infer,\n InferPartial,\n IsValidIndexSchema,\n IsValidStorageSchema,\n IsValidUniformSchema,\n IsValidVertexSchema,\n MemIdentity,\n} from '../../shared/repr.ts';\nimport { $internal } from '../../shared/symbols.ts';\nimport type {\n Prettify,\n UnionToIntersection,\n} from '../../shared/utilityTypes.ts';\nimport { isGPUBuffer } from '../../types.ts';\nimport type { ExperimentalTgpuRoot } from '../root/rootTypes.ts';\nimport {\n asMutable,\n asReadonly,\n asUniform,\n type TgpuBufferMutable,\n type TgpuBufferReadonly,\n type TgpuBufferUniform,\n type TgpuFixedBufferUsage,\n} from './bufferUsage.ts';\nimport type { AnyData } from '../../data/dataTypes.ts';\n\n// ----------\n// Public API\n// ----------\n\nexport interface UniformFlag {\n usableAsUniform: true;\n}\n\n/**\n * @deprecated Use UniformFlag instead.\n */\nexport type Uniform = UniformFlag;\n\nexport interface VertexFlag {\n usableAsVertex: true;\n}\n\nexport interface IndexFlag {\n usableAsIndex: true;\n}\n\n/**\n * @deprecated Use VertexFlag instead.\n */\nexport type Vertex = VertexFlag;\n\ntype LiteralToUsageType<T extends 'uniform' | 'storage' | 'vertex' | 'index'> =\n T extends 'uniform' ? UniformFlag\n : T extends 'storage' ? StorageFlag\n : T extends 'vertex' ? VertexFlag\n : T extends 'index' ? IndexFlag\n : never;\n\ntype ViewUsages<TBuffer extends TgpuBuffer<BaseData>> =\n | (boolean extends TBuffer['usableAsUniform'] ? never : 'uniform')\n | (boolean extends TBuffer['usableAsStorage'] ? never\n : 'readonly' | 'mutable');\n\ntype UsageTypeToBufferUsage<TData extends BaseData> = {\n uniform: TgpuBufferUniform<TData> & TgpuFixedBufferUsage<TData>;\n mutable: TgpuBufferMutable<TData> & TgpuFixedBufferUsage<TData>;\n readonly: TgpuBufferReadonly<TData> & TgpuFixedBufferUsage<TData>;\n};\n\nconst usageToUsageConstructor = {\n uniform: asUniform,\n mutable: asMutable,\n readonly: asReadonly,\n};\n\n/**\n * Done as an object to later Prettify it\n */\ntype InnerValidUsagesFor<T> = {\n usage:\n | (IsValidStorageSchema<T> extends true ? 'storage' : never)\n | (IsValidUniformSchema<T> extends true ? 'uniform' : never)\n | (IsValidVertexSchema<T> extends true ? 'vertex' : never)\n | (IsValidIndexSchema<T> extends true ? 'index' : never);\n};\n\nexport type ValidUsagesFor<T> = InnerValidUsagesFor<T>['usage'];\n\nexport interface TgpuBuffer<TData extends BaseData> extends TgpuNamable {\n readonly [$internal]: true;\n readonly resourceType: 'buffer';\n readonly dataType: TData;\n readonly initial?: Infer<TData> | undefined;\n\n readonly buffer: GPUBuffer;\n readonly destroyed: boolean;\n\n usableAsUniform: boolean;\n usableAsStorage: boolean;\n usableAsVertex: boolean;\n usableAsIndex: boolean;\n\n $usage<\n T extends [\n Prettify<InnerValidUsagesFor<TData>>['usage'],\n ...Prettify<InnerValidUsagesFor<TData>>['usage'][],\n ],\n >(\n ...usages: T\n ): this & UnionToIntersection<LiteralToUsageType<T[number]>>;\n $addFlags(flags: GPUBufferUsageFlags): this;\n\n as<T extends ViewUsages<this>>(usage: T): UsageTypeToBufferUsage<TData>[T];\n\n compileWriter(): void;\n write(data: Infer<TData>): void;\n writePartial(data: InferPartial<TData>): void;\n copyFrom(srcBuffer: TgpuBuffer<MemIdentity<TData>>): void;\n read(): Promise<Infer<TData>>;\n destroy(): void;\n}\n\nexport function INTERNAL_createBuffer<TData extends AnyData>(\n group: ExperimentalTgpuRoot,\n typeSchema: TData,\n initialOrBuffer?: Infer<TData> | GPUBuffer,\n): TgpuBuffer<TData> {\n if (!isWgslData(typeSchema)) {\n return new TgpuBufferImpl(group, typeSchema, initialOrBuffer, [\n 'storage',\n 'uniform',\n ]);\n }\n\n return new TgpuBufferImpl(group, typeSchema, initialOrBuffer);\n}\n\nexport function isBuffer<T extends TgpuBuffer<AnyData>>(\n value: T | unknown,\n): value is T {\n return (value as TgpuBuffer<AnyData>).resourceType === 'buffer';\n}\n\nexport function isUsableAsVertex<T extends TgpuBuffer<AnyData>>(\n buffer: T,\n): buffer is T & VertexFlag {\n return !!(buffer as unknown as VertexFlag).usableAsVertex;\n}\n\nexport function isUsableAsIndex<T extends TgpuBuffer<AnyData>>(\n buffer: T,\n): buffer is T & IndexFlag {\n return !!(buffer as unknown as IndexFlag).usableAsIndex;\n}\n\n// --------------\n// Implementation\n// --------------\nconst endianness = getSystemEndianness();\n\nclass TgpuBufferImpl<TData extends AnyData> implements TgpuBuffer<TData> {\n public readonly [$internal] = true;\n public readonly resourceType = 'buffer';\n public flags: GPUBufferUsageFlags = GPUBufferUsage.COPY_DST |\n GPUBufferUsage.COPY_SRC;\n private _buffer: GPUBuffer | null = null;\n private _ownBuffer: boolean;\n private _destroyed = false;\n private _hostBuffer: ArrayBuffer | undefined;\n\n readonly initial: Infer<TData> | undefined;\n\n usableAsUniform = false;\n usableAsStorage = false;\n usableAsVertex = false;\n usableAsIndex = false;\n\n constructor(\n private readonly _group: ExperimentalTgpuRoot,\n public readonly dataType: TData,\n public readonly initialOrBuffer?: Infer<TData> | GPUBuffer | undefined,\n private readonly _disallowedUsages?:\n ('uniform' | 'storage' | 'vertex' | 'index')[],\n ) {\n if (isGPUBuffer(initialOrBuffer)) {\n this._ownBuffer = false;\n this._buffer = initialOrBuffer;\n } else {\n this._ownBuffer = true;\n this.initial = initialOrBuffer;\n }\n }\n\n get buffer() {\n const device = this._group.device;\n\n if (this._destroyed) {\n throw new Error('This buffer has been destroyed');\n }\n\n if (!this._buffer) {\n this._buffer = device.createBuffer({\n size: sizeOf(this.dataType),\n usage: this.flags,\n mappedAtCreation: !!this.initial,\n label: getName(this) ?? '<unnamed>',\n });\n\n if (this.initial) {\n const writer = new BufferWriter(this._buffer.getMappedRange());\n writeData(writer, this.dataType, this.initial);\n this._buffer.unmap();\n }\n }\n\n return this._buffer;\n }\n\n get destroyed() {\n return this._destroyed;\n }\n\n $name(label: string) {\n setName(this, label);\n if (this._buffer) {\n this._buffer.label = label;\n }\n return this;\n }\n\n $usage<T extends ('uniform' | 'storage' | 'vertex' | 'index')[]>(\n ...usages: T\n ): this & UnionToIntersection<LiteralToUsageType<T[number]>> {\n for (const usage of usages) {\n if (this._disallowedUsages?.includes(usage)) {\n throw new Error(\n `Buffer of type ${this.dataType.type} cannot be used as ${usage}`,\n );\n }\n\n this.flags |= usage === 'uniform' ? GPUBufferUsage.UNIFORM : 0;\n this.flags |= usage === 'storage' ? GPUBufferUsage.STORAGE : 0;\n this.flags |= usage === 'vertex' ? GPUBufferUsage.VERTEX : 0;\n this.flags |= usage === 'index' ? GPUBufferUsage.INDEX : 0;\n this.usableAsUniform = this.usableAsUniform || usage === 'uniform';\n this.usableAsStorage = this.usableAsStorage || usage === 'storage';\n this.usableAsVertex = this.usableAsVertex || usage === 'vertex';\n this.usableAsIndex = this.usableAsIndex || usage === 'index';\n }\n return this as this & UnionToIntersection<LiteralToUsageType<T[number]>>;\n }\n\n $addFlags(flags: GPUBufferUsageFlags) {\n if (!this._ownBuffer) {\n throw new Error(\n 'Cannot add flags to a buffer that is not managed by TypeGPU.',\n );\n }\n\n if (flags & GPUBufferUsage.MAP_READ) {\n this.flags = GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ;\n return this;\n }\n\n if (flags & GPUBufferUsage.MAP_WRITE) {\n this.flags = GPUBufferUsage.COPY_SRC | GPUBufferUsage.MAP_WRITE;\n return this;\n }\n\n this.flags |= flags;\n return this;\n }\n\n compileWriter(): void {\n getCompiledWriterForSchema(this.dataType);\n }\n\n private _writeToTarget(\n target: ArrayBuffer,\n data: Infer<TData>,\n ): void {\n const compiledWriter = getCompiledWriterForSchema(this.dataType);\n\n if (compiledWriter) {\n try {\n compiledWriter(\n new DataView(target),\n 0,\n data,\n endianness === 'little',\n );\n return;\n } catch (error) {\n console.error(\n `Error when using compiled writer for buffer ${\n getName(this) ?? '<unnamed>'\n } - this is likely a bug, please submit an issue at https://github.com/software-mansion/TypeGPU/issues\\nUsing fallback writer instead.`,\n error,\n );\n }\n }\n\n writeData(new BufferWriter(target), this.dataType, data);\n }\n\n write(data: Infer<TData>): void {\n const gpuBuffer = this.buffer;\n const device = this._group.device;\n\n if (gpuBuffer.mapState === 'mapped') {\n const mapped = gpuBuffer.getMappedRange();\n this._writeToTarget(mapped, data);\n return;\n }\n\n const size = sizeOf(this.dataType);\n if (!this._hostBuffer) {\n this._hostBuffer = new ArrayBuffer(size);\n }\n\n // Flushing any commands yet to be encoded.\n this._group.flush();\n\n this._writeToTarget(this._hostBuffer, data);\n device.queue.writeBuffer(gpuBuffer, 0, this._hostBuffer, 0, size);\n }\n\n public writePartial(data: InferPartial<TData>): void {\n const gpuBuffer = this.buffer;\n const device = this._group.device;\n\n const instructions = getWriteInstructions(this.dataType, data);\n\n if (gpuBuffer.mapState === 'mapped') {\n const mappedRange = gpuBuffer.getMappedRange();\n const mappedView = new Uint8Array(mappedRange);\n\n for (const instruction of instructions) {\n mappedView.set(instruction.data, instruction.data.byteOffset);\n }\n } else {\n for (const instruction of instructions) {\n device.queue.writeBuffer(\n gpuBuffer,\n instruction.data.byteOffset,\n instruction.data,\n 0,\n instruction.data.byteLength,\n );\n }\n }\n }\n\n copyFrom(srcBuffer: TgpuBuffer<MemIdentity<TData>>): void {\n if (this.buffer.mapState === 'mapped') {\n throw new Error('Cannot copy to a mapped buffer.');\n }\n\n const size = sizeOf(this.dataType);\n const encoder = this._group.commandEncoder;\n encoder.copyBufferToBuffer(srcBuffer.buffer, 0, this.buffer, 0, size);\n }\n\n async read(): Promise<Infer<TData>> {\n // Flushing any commands yet to be encoded.\n this._group.flush();\n\n const gpuBuffer = this.buffer;\n const device = this._group.device;\n\n if (gpuBuffer.mapState === 'mapped') {\n const mapped = gpuBuffer.getMappedRange();\n return readData(new BufferReader(mapped), this.dataType);\n }\n\n if (gpuBuffer.usage & GPUBufferUsage.MAP_READ) {\n await gpuBuffer.mapAsync(GPUMapMode.READ);\n const mapped = gpuBuffer.getMappedRange();\n const res = readData(new BufferReader(mapped), this.dataType);\n gpuBuffer.unmap();\n return res;\n }\n\n const stagingBuffer = device.createBuffer({\n size: sizeOf(this.dataType),\n usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,\n });\n\n const commandEncoder = device.createCommandEncoder();\n commandEncoder.copyBufferToBuffer(\n gpuBuffer,\n 0,\n stagingBuffer,\n 0,\n sizeOf(this.dataType),\n );\n\n device.queue.submit([commandEncoder.finish()]);\n await device.queue.onSubmittedWorkDone();\n await stagingBuffer.mapAsync(GPUMapMode.READ, 0, sizeOf(this.dataType));\n\n const res = readData(\n new BufferReader(stagingBuffer.getMappedRange()),\n this.dataType,\n );\n\n stagingBuffer.unmap();\n stagingBuffer.destroy();\n\n return res;\n }\n\n as<T extends ViewUsages<this>>(usage: T): UsageTypeToBufferUsage<TData>[T] {\n return usageToUsageConstructor[usage]?.(\n this as never,\n ) as UsageTypeToBufferUsage<TData>[T];\n }\n\n destroy() {\n if (this._destroyed) {\n return;\n }\n this._destroyed = true;\n if (this._ownBuffer) {\n this._buffer?.destroy();\n }\n }\n\n toString(): string {\n return `buffer:${getName(this) ?? '<unnamed>'}`;\n }\n}\n","import { resolveData } from './core/resolve/resolveData.ts';\nimport { ConfigurableImpl } from './core/root/configurableImpl.ts';\nimport type { Configurable } from './core/root/rootTypes.ts';\nimport {\n type Eventual,\n isDerived,\n isProviding,\n isSlot,\n type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n} from './core/slot/slotTypes.ts';\nimport { getAttributesString } from './data/attributes.ts';\nimport { type AnyData, isData, type UnknownData } from './data/dataTypes.ts';\nimport { snip, type Snippet } from './data/snippet.ts';\nimport { type BaseData, isWgslArray, isWgslStruct } from './data/wgslTypes.ts';\nimport { invariant, MissingSlotValueError, ResolutionError } from './errors.ts';\nimport { provideCtx, topLevelState } from './execMode.ts';\nimport type { NameRegistry } from './nameRegistry.ts';\nimport { naturalsExcept } from './shared/generators.ts';\nimport type { Infer } from './shared/repr.ts';\nimport { $internal, $providing } from './shared/symbols.ts';\nimport {\n bindGroupLayout,\n type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n} from './tgpuBindGroupLayout.ts';\nimport { coerceToSnippet } from './tgsl/generationHelpers.ts';\nimport { generateFunction } from './tgsl/wgslGenerator.ts';\nimport type {\n ExecMode,\n ExecState,\n FnToWgslOptions,\n ItemLayer,\n ItemStateStack,\n ResolutionCtx,\n Wgsl,\n} from './types.ts';\nimport {\n CodegenState,\n isSelfResolvable,\n isWgsl,\n NormalState,\n} from './types.ts';\n\n/**\n * Inserted into bind group entry definitions that belong\n * to the automatically generated catch-all bind group.\n *\n * A non-occupied group index can only be determined after\n * every resource has been resolved, so this acts as a placeholder\n * to be replaced with an actual numeric index at the very end\n * of the resolution process.\n */\nconst CATCHALL_BIND_GROUP_IDX_MARKER = '#CATCHALL#';\n\nexport type ResolutionCtxImplOptions = {\n readonly names: NameRegistry;\n};\n\ntype SlotToValueMap = Map<TgpuSlot<unknown>, unknown>;\n\ntype SlotBindingLayer = {\n type: 'slotBinding';\n bindingMap: WeakMap<TgpuSlot<unknown>, unknown>;\n};\n\ntype FunctionScopeLayer = {\n type: 'functionScope';\n args: Snippet[];\n argAliases: Record<string, Snippet>;\n externalMap: Record<string, unknown>;\n returnType: AnyData;\n};\n\ntype BlockScopeLayer = {\n type: 'blockScope';\n declarations: Map<string, AnyData | UnknownData>;\n};\n\nclass ItemStateStackImpl implements ItemStateStack {\n private _stack: (\n | ItemLayer\n | SlotBindingLayer\n | FunctionScopeLayer\n | BlockScopeLayer\n )[] = [];\n private _itemDepth = 0;\n\n get itemDepth(): number {\n return this._itemDepth;\n }\n\n get topItem(): ItemLayer {\n const state = this._stack[this._stack.length - 1];\n if (!state || state.type !== 'item') {\n throw new Error('Internal error, expected item layer to be on top.');\n }\n return state;\n }\n\n get topFunctionReturnType(): AnyData {\n const scope = this._stack.findLast((e) => e.type === 'functionScope');\n if (!scope) {\n throw new Error('Internal error, expected function scope to be present.');\n }\n return scope.returnType;\n }\n\n pushItem() {\n this._itemDepth++;\n this._stack.push({\n type: 'item',\n usedSlots: new Set(),\n });\n }\n\n popItem() {\n this.pop('item');\n }\n\n pushSlotBindings(pairs: SlotValuePair<unknown>[]) {\n this._stack.push({\n type: 'slotBinding',\n bindingMap: new WeakMap(pairs),\n });\n }\n\n popSlotBindings() {\n this.pop('slotBinding');\n }\n\n pushFunctionScope(\n args: Snippet[],\n argAliases: Record<string, Snippet>,\n returnType: AnyData,\n externalMap: Record<string, unknown>,\n ) {\n this._stack.push({\n type: 'functionScope',\n args,\n argAliases,\n returnType,\n externalMap,\n });\n }\n\n popFunctionScope() {\n this.pop('functionScope');\n }\n\n pushBlockScope() {\n this._stack.push({\n type: 'blockScope',\n declarations: new Map<string, AnyData | UnknownData>(),\n });\n }\n\n popBlockScope() {\n this.pop('blockScope');\n }\n\n pop(type?: (typeof this._stack)[number]['type']) {\n const layer = this._stack[this._stack.length - 1];\n if (!layer || (type && layer.type !== type)) {\n throw new Error(`Internal error, expected a ${type} layer to be on top.`);\n }\n\n this._stack.pop();\n if (type === 'item') {\n this._itemDepth--;\n }\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n if (layer?.type === 'item') {\n // Binding not available yet, so this layer is dependent on the slot's value.\n layer.usedSlots.add(slot);\n } else if (layer?.type === 'slotBinding') {\n const boundValue = layer.bindingMap.get(slot);\n\n if (boundValue !== undefined) {\n return boundValue as T;\n }\n } else if (\n layer?.type === 'functionScope' ||\n layer?.type === 'blockScope'\n ) {\n // Skip\n } else {\n throw new Error('Unknown layer type.');\n }\n }\n\n return slot.defaultValue;\n }\n\n getSnippetById(id: string): Snippet | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'functionScope') {\n const arg = layer.args.find((a) => a.value === id);\n if (arg !== undefined) {\n return arg;\n }\n\n if (layer.argAliases[id]) {\n return layer.argAliases[id];\n }\n\n const external = layer.externalMap[id];\n\n if (external !== undefined && external !== null) {\n return coerceToSnippet(external);\n }\n\n // Since functions cannot access resources from the calling scope, we\n // return early here.\n return undefined;\n }\n\n if (layer?.type === 'blockScope') {\n const declarationType = layer.declarations.get(id);\n if (declarationType !== undefined) {\n return snip(id, declarationType);\n }\n } else {\n // Skip\n }\n }\n\n return undefined;\n }\n\n defineBlockVariable(id: string, type: AnyData | UnknownData): Snippet {\n if (type.type === 'unknown') {\n throw Error(`Tried to define variable '${id}' of unknown type`);\n }\n\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'blockScope') {\n layer.declarations.set(id, type);\n\n return snip(id, type);\n }\n }\n\n throw new Error('No block scope found to define a variable in.');\n }\n}\n\nconst INDENT = [\n '', // 0\n ' ', // 1\n ' ', // 2\n ' ', // 3\n ' ', // 4\n ' ', // 5\n ' ', // 6\n ' ', // 7\n ' ', // 8\n];\n\nconst N = INDENT.length - 1;\n\nexport class IndentController {\n private identLevel = 0;\n\n get pre(): string {\n return (\n INDENT[this.identLevel] ??\n (INDENT[N] as string).repeat(this.identLevel / N) +\n INDENT[this.identLevel % N]\n );\n }\n\n indent(): string {\n const str = this.pre;\n this.identLevel++;\n return str;\n }\n\n dedent(): string {\n this.identLevel--;\n return this.pre;\n }\n\n withResetLevel<T>(callback: () => T): T {\n const savedLevel = this.identLevel;\n this.identLevel = 0;\n try {\n return callback();\n } finally {\n this.identLevel = savedLevel;\n }\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 _itemStateStack = new ItemStateStackImpl();\n readonly #modeStack: ExecState[] = [];\n private readonly _declarations: string[] = [];\n private _varyingLocations: Record<string, number> | undefined;\n\n get varyingLocations() {\n return this._varyingLocations;\n }\n\n readonly [$internal] = {\n itemStateStack: this._itemStateStack,\n };\n\n // -- Bindings\n /**\n * A map from registered bind group layouts to random strings put in\n * place of their group index. The whole tree has to be traversed to\n * collect every use of a typed bind group layout, since they can be\n * explicitly imposed group indices, and they cannot collide.\n */\n public readonly bindGroupLayoutsToPlaceholderMap = new Map<\n TgpuBindGroupLayout,\n string\n >();\n private _nextFreeLayoutPlaceholderIdx = 0;\n public readonly fixedBindings: FixedBindingConfig[] = [];\n // --\n\n public readonly names: NameRegistry;\n public expectedType: AnyData | undefined;\n\n constructor(opts: ResolutionCtxImplOptions) {\n this.names = opts.names;\n }\n\n get pre(): string {\n return this._indentController.pre;\n }\n\n get topFunctionReturnType() {\n return this._itemStateStack.topFunctionReturnType;\n }\n\n indent(): string {\n return this._indentController.indent();\n }\n\n dedent(): string {\n return this._indentController.dedent();\n }\n\n withResetIndentLevel<T>(callback: () => T): T {\n return this._indentController.withResetLevel(callback);\n }\n\n getById(id: string): Snippet | null {\n const item = this._itemStateStack.getSnippetById(id);\n\n if (item === undefined) {\n return null;\n }\n\n return item;\n }\n\n defineVariable(id: string, dataType: AnyData | UnknownData): Snippet {\n return this._itemStateStack.defineBlockVariable(id, dataType);\n }\n\n pushBlockScope() {\n this._itemStateStack.pushBlockScope();\n }\n\n popBlockScope() {\n this._itemStateStack.popBlockScope();\n }\n\n fnToWgsl(options: FnToWgslOptions): { head: Wgsl; body: Wgsl } {\n this._itemStateStack.pushFunctionScope(\n options.args,\n options.argAliases,\n options.returnType,\n options.externalMap,\n );\n\n try {\n return {\n head: resolveFunctionHeader(this, options.args, options.returnType),\n body: generateFunction(this, options.body),\n };\n } finally {\n this._itemStateStack.popFunctionScope();\n }\n }\n\n addDeclaration(declaration: string): void {\n this._declarations.push(declaration);\n }\n\n allocateLayoutEntry(layout: TgpuBindGroupLayout): string {\n const memoMap = this.bindGroupLayoutsToPlaceholderMap;\n let placeholderKey = memoMap.get(layout);\n\n if (!placeholderKey) {\n placeholderKey = `#BIND_GROUP_LAYOUT_${this\n ._nextFreeLayoutPlaceholderIdx++}#`;\n memoMap.set(layout, placeholderKey);\n }\n\n return placeholderKey;\n }\n\n allocateFixedEntry(\n layoutEntry: TgpuLayoutEntry,\n resource: object,\n ): { group: string; binding: number } {\n const binding = this.fixedBindings.length;\n this.fixedBindings.push({ layoutEntry, resource });\n\n return {\n group: CATCHALL_BIND_GROUP_IDX_MARKER,\n binding,\n };\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T {\n const value = this._itemStateStack.readSlot(slot);\n\n if (value === undefined) {\n throw new MissingSlotValueError(slot);\n }\n\n return value;\n }\n\n withSlots<T>(pairs: SlotValuePair<unknown>[], callback: () => T): T {\n this._itemStateStack.pushSlotBindings(pairs);\n\n try {\n return callback();\n } finally {\n this._itemStateStack.popSlotBindings();\n }\n }\n\n withVaryingLocations<T>(\n locations: Record<string, number>,\n callback: () => T,\n ): T {\n this._varyingLocations = locations;\n\n try {\n return callback();\n } finally {\n this._varyingLocations = undefined;\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 // Getting out of codegen or simulation mode so we can execute JS normally.\n this.pushMode(new NormalState());\n\n let result: T;\n try {\n result = derived['~compute']();\n } finally {\n this.popMode('normal');\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedDerived.set(derived, instances);\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(derived);\n }\n\n throw new ResolutionError(err, [derived]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n /**\n * @param item The item whose resolution should be either retrieved from the cache (if there is a cache hit), or resolved.\n */\n _getOrInstantiate(item: object): string {\n // All memoized versions of `item`\n const instances = this._memoizedResolves.get(item) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue)\n )\n ) {\n return instance.result;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n let result: string;\n if (isData(item)) {\n result = resolveData(this, item);\n } else if (isDerived(item) || isSlot(item)) {\n result = this.resolve(this.unwrap(item));\n } else if (isSelfResolvable(item)) {\n result = item['~resolve'](this);\n } else {\n result = this.resolveValue(item);\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedResolves.set(item, instances);\n\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(item);\n }\n\n throw new ResolutionError(err, [item]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n resolve(item: unknown): string {\n if (isProviding(item)) {\n return this.withSlots(\n item[$providing].pairs,\n () => this.resolve(item[$providing].inner),\n );\n }\n\n if ((item && typeof item === 'object') || typeof item === 'function') {\n if (this._itemStateStack.itemDepth === 0) {\n try {\n this.pushMode(new CodegenState());\n const result = provideCtx(this, () => this._getOrInstantiate(item));\n\n return `${[...this._declarations].join('\\n\\n')}${result}`;\n } finally {\n this.popMode('codegen');\n }\n }\n\n return this._getOrInstantiate(item);\n }\n\n return String(item);\n }\n\n resolveValue<T extends BaseData>(\n value: Infer<T>,\n schema?: T | undefined,\n ): string {\n if (isWgsl(value)) {\n return this.resolve(value);\n }\n\n if (schema && isWgslArray(schema)) {\n return `array(${\n (value as unknown[]).map((element) =>\n this.resolveValue(element, schema.elementType)\n )\n })`;\n }\n\n if (Array.isArray(value)) {\n return `array(${value.map((element) => this.resolveValue(element))})`;\n }\n\n if (schema && isWgslStruct(schema)) {\n return `${this.resolve(schema)}(${\n Object.entries(schema.propTypes).map(([key, type_]) =>\n this.resolveValue(\n (value as Infer<typeof schema>)[key],\n type_ as BaseData,\n )\n )\n })`;\n }\n\n throw new Error(\n `Value ${value} (as json: ${\n JSON.stringify(value)\n }) of schema ${schema} is not resolvable to WGSL`,\n );\n }\n\n pushMode(mode: ExecState) {\n this.#modeStack.push(mode);\n }\n\n popMode(expected?: ExecMode) {\n const mode = this.#modeStack.pop();\n if (expected !== undefined) {\n invariant(mode?.type === expected, 'Unexpected mode');\n }\n }\n\n get mode(): ExecState {\n return this.#modeStack[this.#modeStack.length - 1] ?? topLevelState;\n }\n}\n\n/**\n * The results of a WGSL resolution.\n *\n * @param code - The resolved code.\n * @param usedBindGroupLayouts - List of used `tgpu.bindGroupLayout`s.\n * @param catchall - Automatically constructed bind group for buffer usages and buffer shorthands, preceded by its index.\n */\nexport interface ResolutionResult {\n code: string;\n usedBindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | undefined;\n}\n\nexport function resolve(\n item: Wgsl,\n options: ResolutionCtxImplOptions,\n config?: (cfg: Configurable) => Configurable,\n): ResolutionResult {\n const ctx = new ResolutionCtxImpl(options);\n let code = config\n ? ctx.withSlots(\n config(new ConfigurableImpl([])).bindings,\n () => ctx.resolve(item),\n )\n : ctx.resolve(item);\n\n const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;\n const usedBindGroupLayouts: 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 usedBindGroupLayouts[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() : undefined;\n\n for (const [layout, placeholder] of memoMap.entries()) {\n const idx = layout.index ?? automaticIds.next().value;\n usedBindGroupLayouts[idx] = layout;\n code = code.replaceAll(placeholder, String(idx));\n }\n\n return {\n code,\n usedBindGroupLayouts,\n catchall,\n };\n}\n\nexport function resolveFunctionHeader(\n ctx: ResolutionCtx,\n args: Snippet[],\n returnType: AnyData,\n) {\n const argList = args\n .map((arg) => `${arg.value}: ${ctx.resolve(arg.dataType as AnyData)}`)\n .join(', ');\n\n return returnType.type !== 'void'\n ? `(${argList}) -> ${getAttributesString(returnType)}${\n ctx.resolve(returnType)\n } `\n : `(${argList}) `;\n}\n"]}
|
1
|
+
{"version":3,"sources":["/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","../src/extractGpuValueGetter.ts","../src/core/valueProxyUtils.ts","../src/core/function/fnCore.ts","../src/core/resolve/resolveData.ts","../src/core/root/configurableImpl.ts","../src/data/compiledIO.ts","../src/data/dataIO.ts","../src/core/buffer/buffer.ts","../src/resolutionCtx.ts"],"names":["extractGpuValueGetter","object","$gpuValueOf","valueProxyHandler","target","prop","$providing","$internal","ctx","slot"],"mappings":"AAAA,ysCAAqG,wDAA0wB,SCK/1BA,EAAAA,CACdC,CAAAA,CAC4B,CAE5B,EAAA,CAAI,uBAAQA,CAAAA,4BAAAA,CAAiBC,mBAAW,GAAA,EAAM,UAAA,CAC5C,OAAQD,CAAAA,CAA6CC,mBAAW,CAAA,CAAE,IAAA,CAChED,CACF,CAGJ,CCJO,IAAME,CAAAA,CAGT,CACF,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,GAASC,mBAAAA,CAIb,OACED,CAAAA,GAAS,UAAA,EACTA,CAAAA,GAAS,MAAA,CAAO,WAAA,EAChBA,CAAAA,GAAS,MAAA,CAAO,WAAA,CAET,CAAA,CAAA,EAAMD,CAAAA,CAAO,QAAA,CAAS,CAAA,CAGxB,IAAI,KAAA,CACT,CACE,CAACG,mBAAS,CAAA,CAAG,CAAA,CAAA,CAEb,UAAA,CAAaC,CAAAA,EACX,CAAA,EAAA;ACuGA;ACPC;AA8BA;AAYT;AC3JwBC,CAAAA;AC0HhB;AAsC0C;AAMxC;AAeN;AAsBE;AA6CS;AAea;AAkDsB;AAE9C,QAAA;AC7LK,8BAAA;ACoPuC,8BAAA;ACsPD;AA2G3C","file":"/Users/iwo/Projects/wigsill/packages/typegpu/dist/index.cjs","sourcesContent":[null,"import { $gpuValueOf } from './shared/symbols.ts';\nimport type { ResolutionCtx } from './types.ts';\n\nexport type GpuValueGetter = (ctx: ResolutionCtx) => unknown;\n\nexport function extractGpuValueGetter(\n object: unknown,\n): GpuValueGetter | undefined {\n // biome-ignore lint/suspicious/noExplicitAny: we're inspecting the value\n if (typeof (object as any)?.[$gpuValueOf] === 'function') {\n return (object as { [$gpuValueOf]: GpuValueGetter })[$gpuValueOf].bind(\n object,\n );\n }\n return undefined;\n}\n","import type { AnyData } from '../data/dataTypes.ts';\nimport type { BaseData } from '../data/wgslTypes.ts';\nimport {\n extractGpuValueGetter,\n type GpuValueGetter,\n} from '../extractGpuValueGetter.ts';\nimport { getName } from '../shared/meta.ts';\nimport { $internal, $providing, $wgslDataType } from '../shared/symbols.ts';\nimport { getTypeForPropAccess } from '../tgsl/generationHelpers.ts';\nimport type { ResolutionCtx, SelfResolvable } from '../types.ts';\n\nexport const valueProxyHandler: ProxyHandler<\n & SelfResolvable\n & { readonly [$wgslDataType]: BaseData }\n> = {\n get(target, prop) {\n if (prop in target) {\n return Reflect.get(target, prop);\n }\n\n if (prop === $providing) {\n return undefined;\n }\n\n if (\n prop === 'toString' ||\n prop === Symbol.toStringTag ||\n prop === Symbol.toPrimitive\n ) {\n return () => target.toString();\n }\n\n return new Proxy(\n {\n [$internal]: true,\n\n '~resolve': (ctx: ResolutionCtx) =>\n `${ctx.resolve(target)}.${String(prop)}`,\n\n toString: () =>\n `.value(...).${String(prop)}:${getName(target) ?? '<unnamed>'}`,\n\n [$wgslDataType]: getTypeForPropAccess(\n target[$wgslDataType] as AnyData,\n String(prop),\n ) as BaseData,\n },\n valueProxyHandler,\n );\n },\n};\n\nexport function getGpuValueRecursively<T>(\n ctx: ResolutionCtx,\n value: unknown,\n): T {\n let unwrapped = value;\n let valueGetter: GpuValueGetter | undefined;\n\n // biome-ignore lint/suspicious/noAssignInExpressions: it's exactly what we want biome\n while (valueGetter = extractGpuValueGetter(unwrapped)) {\n unwrapped = valueGetter(ctx);\n }\n\n return unwrapped as T;\n}\n","import { FuncParameterType } from 'tinyest';\nimport { getAttributesString } from '../../data/attributes.ts';\nimport { undecorate } from '../../data/decorateUtils.ts';\nimport type { AnyData } from '../../data/dataTypes.ts';\nimport { snip } from '../../data/snippet.ts';\nimport {\n isWgslData,\n isWgslStruct,\n Void,\n type WgslStruct,\n} from '../../data/wgslTypes.ts';\nimport { MissingLinksError } from '../../errors.ts';\nimport { getMetaData, getName, setName } from '../../shared/meta.ts';\nimport type { ResolutionCtx } from '../../types.ts';\nimport {\n applyExternals,\n type ExternalMap,\n replaceExternalsInWgsl,\n} from '../resolve/externals.ts';\nimport { extractArgs } from './extractArgs.ts';\nimport type { Implementation } from './fnTypes.ts';\n\nexport interface FnCore {\n applyExternals(newExternals: ExternalMap): void;\n resolve(\n ctx: ResolutionCtx,\n argTypes: AnyData[],\n returnType: AnyData,\n ): string;\n}\n\nexport function createFnCore(\n implementation: Implementation,\n fnAttribute = '',\n): FnCore {\n /**\n * External application has to be deferred until resolution because\n * some externals can reference the owner function which has not been\n * initialized yet (like when accessing the Output struct of a vertex\n * entry fn).\n */\n const externalsToApply: ExternalMap[] = [];\n\n const core = {\n applyExternals(newExternals: ExternalMap): void {\n externalsToApply.push(newExternals);\n },\n\n resolve(\n ctx: ResolutionCtx,\n argTypes: AnyData[],\n returnType: AnyData,\n ): string {\n const externalMap: ExternalMap = {};\n\n for (const externals of externalsToApply) {\n applyExternals(externalMap, externals);\n }\n\n const id = ctx.names.makeUnique(getName(this));\n\n if (typeof implementation === 'string') {\n const replacedImpl = replaceExternalsInWgsl(\n ctx,\n externalMap,\n implementation,\n );\n\n let header = '';\n let body = '';\n\n if (fnAttribute !== '') {\n const input = isWgslStruct(argTypes[0])\n ? `(in: ${ctx.resolve(argTypes[0])})`\n : '()';\n\n const attributes = isWgslData(returnType)\n ? getAttributesString(returnType)\n : '';\n const output = returnType !== Void\n ? isWgslStruct(returnType)\n ? `-> ${ctx.resolve(returnType)}`\n : `-> ${attributes !== '' ? attributes : '@location(0)'} ${\n ctx.resolve(returnType)\n }`\n : '';\n\n header = `${input} ${output} `;\n body = replacedImpl;\n } else {\n const providedArgs = extractArgs(replacedImpl);\n\n if (providedArgs.args.length !== argTypes.length) {\n throw new Error(\n `WGSL implementation has ${providedArgs.args.length} arguments, while the shell has ${argTypes.length} arguments.`,\n );\n }\n\n const input = providedArgs.args.map((argInfo, i) =>\n `${argInfo.identifier}: ${\n checkAndReturnType(\n ctx,\n `parameter ${argInfo.identifier}`,\n argInfo.type,\n argTypes[i],\n )\n }`\n ).join(', ');\n\n const output = returnType === Void ? '' : `-> ${\n checkAndReturnType(\n ctx,\n 'return type',\n providedArgs.ret?.type,\n returnType,\n )\n }`;\n\n header = `(${input}) ${output}`;\n\n body = replacedImpl.slice(providedArgs.range.end);\n }\n\n ctx.addDeclaration(`${fnAttribute}fn ${id}${header}${body}`);\n } else {\n // get data generated by the plugin\n const pluginData = getMetaData(implementation);\n\n if (pluginData?.externals) {\n const missing = Object.fromEntries(\n Object.entries(pluginData.externals).filter(\n ([name]) => !(name in externalMap),\n ),\n );\n\n applyExternals(externalMap, missing);\n }\n\n const ast = pluginData?.ast;\n if (!ast) {\n throw new Error(\n \"Missing metadata for tgpu.fn function body (either missing 'kernel' directive, or misconfigured `unplugin-typegpu`)\",\n );\n }\n\n // verify all required externals are present\n const missingExternals = ast.externalNames.filter(\n (name) => !(name in externalMap),\n );\n if (missingExternals.length > 0) {\n throw new MissingLinksError(getName(this), missingExternals);\n }\n\n // If an entrypoint implementation has a second argument, it represents the output schema.\n // We look at the identifier chosen by the user and add it to externals.\n const maybeSecondArg = ast.params[1];\n if (\n maybeSecondArg && maybeSecondArg.type === 'i' && fnAttribute !== ''\n ) {\n applyExternals(\n externalMap,\n {\n [maybeSecondArg.name]: undecorate(returnType),\n },\n );\n }\n\n // generate wgsl string\n const { head, body } = ctx.fnToWgsl({\n args: argTypes.map((arg, i) =>\n snip(\n ast.params[i]?.type === FuncParameterType.identifier\n ? ast.params[i].name\n : `_arg_${i}`,\n arg,\n )\n ),\n argAliases: Object.fromEntries(\n ast.params.flatMap((param, i) =>\n param.type === FuncParameterType.destructuredObject\n ? param.props.map(({ name, alias }) => [\n alias,\n snip(\n `_arg_${i}.${name}`,\n (argTypes[i] as WgslStruct)\n .propTypes[name],\n ),\n ])\n : []\n ),\n ),\n returnType,\n body: ast.body,\n externalMap,\n });\n\n ctx.addDeclaration(\n `${fnAttribute}fn ${id}${ctx.resolve(head)}${ctx.resolve(body)}`,\n );\n }\n\n return id;\n },\n };\n\n // The implementation could have been given a name by a bundler plugin,\n // so we try to transfer it to the core.\n const maybeName = getName(implementation);\n if (maybeName !== undefined) {\n setName(core, maybeName);\n }\n\n return core;\n}\n\nfunction checkAndReturnType(\n ctx: ResolutionCtx,\n name: string,\n wgslType: string | undefined,\n jsType: unknown,\n) {\n const resolvedJsType = ctx.resolve(jsType).replace(/\\s/g, '');\n\n if (!wgslType) {\n return resolvedJsType;\n }\n\n const resolvedWgslType = wgslType.replace(/\\s/g, '');\n\n if (resolvedWgslType !== resolvedJsType) {\n throw new Error(\n `Type mismatch between TGPU shell and WGSL code string: ${name}, JS type \"${resolvedJsType}\", WGSL type \"${resolvedWgslType}\".`,\n );\n }\n\n return wgslType;\n}\n","import { getAttributesString } from '../../data/attributes.ts';\nimport {\n type AnyData,\n type Disarray,\n isLooseData,\n type Unstruct,\n} from '../../data/dataTypes.ts';\nimport { formatToWGSLType } from '../../data/vertexFormatData.ts';\nimport type {\n AnyWgslData,\n BaseData,\n Bool,\n F16,\n F32,\n I32,\n Mat2x2f,\n Mat3x3f,\n Mat4x4f,\n U32,\n Vec2b,\n Vec2f,\n Vec2h,\n Vec2i,\n Vec2u,\n Vec3b,\n Vec3f,\n Vec3h,\n Vec3i,\n Vec3u,\n Vec4b,\n Vec4f,\n Vec4h,\n Vec4i,\n Vec4u,\n WgslArray,\n WgslStruct,\n} from '../../data/wgslTypes.ts';\nimport { getName } from '../../shared/meta.ts';\nimport { $internal } from '../../shared/symbols.ts';\nimport { assertExhaustive } from '../../shared/utilityTypes.ts';\nimport type { ResolutionCtx } from '../../types.ts';\nimport { isAttribute } from '../vertexLayout/connectAttributesToShader.ts';\n\n/**\n * Schemas for which their `type` property directly\n * translates to the resulting WGSL code.\n */\nconst identityTypes = [\n 'bool',\n 'f32',\n 'f16',\n 'i32',\n 'u32',\n 'vec2f',\n 'vec3f',\n 'vec4f',\n 'vec2h',\n 'vec3h',\n 'vec4h',\n 'vec2i',\n 'vec3i',\n 'vec4i',\n 'vec2u',\n 'vec3u',\n 'vec4u',\n 'vec2<bool>',\n 'vec3<bool>',\n 'vec4<bool>',\n 'mat2x2f',\n 'mat3x3f',\n 'mat4x4f',\n];\n\ntype IdentityType =\n | Bool\n | F32\n | F16\n | I32\n | U32\n | Vec2f\n | Vec3f\n | Vec4f\n | Vec2h\n | Vec3h\n | Vec4h\n | Vec2i\n | Vec3i\n | Vec4i\n | Vec2u\n | Vec3u\n | Vec4u\n | Vec2b\n | Vec3b\n | Vec4b\n | Mat2x2f\n | Mat3x3f\n | Mat4x4f;\n\nfunction isIdentityType(data: AnyWgslData): data is IdentityType {\n return identityTypes.includes(data.type);\n}\n\n/**\n * Resolves a single property of a struct.\n * @param ctx - The resolution context.\n * @param key - The key of the property.\n * @param property - The property itself.\n *\n * @returns The resolved property string.\n */\nfunction resolveStructProperty(\n ctx: ResolutionCtx,\n [key, property]: [string, BaseData],\n) {\n return ` ${getAttributesString(property)}${key}: ${\n ctx.resolve(property as AnyWgslData)\n },\\n`;\n}\n\n/**\n * Resolves a struct and adds its declaration to the resolution context.\n * @param ctx - The resolution context.\n * @param struct - The struct to resolve.\n *\n * @returns The resolved struct name.\n */\nfunction resolveStruct(ctx: ResolutionCtx, struct: WgslStruct) {\n if (struct[$internal].isAbstruct) {\n throw new Error('Cannot resolve abstract struct types to WGSL.');\n }\n const id = ctx.names.makeUnique(getName(struct));\n\n ctx.addDeclaration(`\\\nstruct ${id} {\n${\n Object.entries(struct.propTypes as Record<string, BaseData>)\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(getName(unstruct));\n\n ctx.addDeclaration(`\\\nstruct ${id} {\n${\n Object.entries(unstruct.propTypes as Record<string, BaseData>)\n .map((prop) =>\n isAttribute(prop[1])\n ? resolveStructProperty(ctx, [\n prop[0],\n formatToWGSLType[prop[1].format],\n ])\n : resolveStructProperty(ctx, prop)\n )\n .join('')\n }\n}`);\n\n return id;\n}\n\n/**\n * Resolves an array.\n * @param ctx - The resolution context.\n * @param array - The array to resolve.\n *\n * @returns The resolved array name along with its element type and count (if not runtime-sized).\n *\n * @example\n * ```ts\n * resolveArray(ctx, d.arrayOf(d.u32, 0)); // 'array<u32>' (not a real pattern, a function is preferred)\n * resolveArray(ctx, d.arrayOf(d.u32, 5)); // 'array<u32, 5>'\n * ```\n */\nfunction resolveArray(ctx: ResolutionCtx, array: WgslArray) {\n const element = ctx.resolve(array.elementType as AnyWgslData);\n\n return array.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${array.elementCount}>`;\n}\n\nfunction resolveDisarray(ctx: ResolutionCtx, disarray: Disarray) {\n const element = ctx.resolve(\n isAttribute(disarray.elementType)\n ? formatToWGSLType[disarray.elementType.format]\n : (disarray.elementType as AnyWgslData),\n );\n\n return disarray.elementCount === 0\n ? `array<${element}>`\n : `array<${element}, ${disarray.elementCount}>`;\n}\n\n/**\n * Resolves a WGSL data-type schema to a string.\n * @param ctx - The resolution context.\n * @param data - The data-type to resolve.\n *\n * @returns The resolved data-type string.\n */\nexport function resolveData(ctx: ResolutionCtx, data: AnyData): string {\n if (isLooseData(data)) {\n if (data.type === 'unstruct') {\n return resolveUnstruct(ctx, data);\n }\n\n if (data.type === 'disarray') {\n return resolveDisarray(ctx, data);\n }\n\n if (data.type === 'loose-decorated') {\n return ctx.resolve(\n isAttribute(data.inner)\n ? formatToWGSLType[data.inner.format]\n : data.inner,\n );\n }\n\n return ctx.resolve(formatToWGSLType[data.type]);\n }\n\n if (isIdentityType(data)) {\n return data.type;\n }\n\n if (data.type === 'struct') {\n return resolveStruct(ctx, data);\n }\n\n if (data.type === 'array') {\n return resolveArray(ctx, data);\n }\n\n if (data.type === 'atomic') {\n return `atomic<${resolveData(ctx, data.inner)}>`;\n }\n\n if (data.type === 'decorated') {\n return ctx.resolve(data.inner as AnyWgslData);\n }\n\n if (data.type === 'ptr') {\n if (data.addressSpace === 'storage') {\n return `ptr<storage, ${ctx.resolve(data.inner)}, ${\n data.access === 'read-write' ? 'read_write' : data.access\n }>`;\n }\n return `ptr<${data.addressSpace}, ${ctx.resolve(data.inner)}>`;\n }\n\n if (\n data.type === 'abstractInt' || data.type === 'abstractFloat' ||\n data.type === 'void' || data.type === 'u16'\n ) {\n throw new Error(`${data.type} has no representation in WGSL`);\n }\n\n assertExhaustive(data, 'resolveData');\n}\n","import type { Infer } from '../../shared/repr.ts';\nimport type { AnyWgslData } from '../../data/wgslTypes.ts';\nimport type { TgpuBufferUsage } from '../buffer/bufferUsage.ts';\nimport type { TgpuFn } from '../function/tgpuFn.ts';\nimport {\n isAccessor,\n type TgpuAccessor,\n type TgpuSlot,\n} from '../slot/slotTypes.ts';\nimport type { Configurable } from './rootTypes.ts';\n\nexport class ConfigurableImpl implements Configurable {\n constructor(readonly bindings: [TgpuSlot<unknown>, unknown][]) {}\n\n with<T extends AnyWgslData>(\n slot: TgpuSlot<T> | TgpuAccessor<T>,\n value: T | TgpuFn<() => T> | TgpuBufferUsage<T> | Infer<T>,\n ): Configurable {\n return new ConfigurableImpl([\n ...this.bindings,\n [isAccessor(slot) ? slot.slot : slot, value],\n ]);\n }\n\n pipe(transform: (cfg: Configurable) => Configurable): Configurable {\n const newCfg = transform(this);\n return new ConfigurableImpl([\n ...this.bindings,\n ...newCfg.bindings,\n ]);\n }\n}\n","import { roundUp } from '../mathUtils.ts';\nimport type { Infer } from '../shared/repr.ts';\nimport { alignmentOf } from './alignmentOf.ts';\nimport { isDisarray, isUnstruct } from './dataTypes.ts';\nimport { offsetsForProps } from './offsets.ts';\nimport { sizeOf } from './sizeOf.ts';\nimport { formatToWGSLType, isPackedData } from './vertexFormatData.ts';\nimport * as wgsl from './wgslTypes.ts';\n\nexport const EVAL_ALLOWED_IN_ENV: boolean = (() => {\n try {\n new Function('return true');\n return true;\n } catch {\n return false;\n }\n})();\n\nconst compiledWriters = new WeakMap<\n wgsl.BaseData,\n (\n output: DataView,\n offset: number,\n value: unknown,\n littleEndian?: boolean,\n ) => void\n>();\n\nconst typeToPrimitive = {\n u32: 'u32',\n vec2u: 'u32',\n vec3u: 'u32',\n vec4u: 'u32',\n u16: 'u16',\n\n i32: 'i32',\n vec2i: 'i32',\n vec3i: 'i32',\n vec4i: 'i32',\n\n f32: 'f32',\n vec2f: 'f32',\n vec3f: 'f32',\n vec4f: 'f32',\n\n f16: 'f16',\n vec2h: 'f16',\n vec3h: 'f16',\n vec4h: 'f16',\n\n mat2x2f: 'f32',\n mat3x3f: 'f32',\n mat4x4f: 'f32',\n} as const;\n\nconst vertexFormatToPrimitive = {\n uint8: 'u8',\n uint8x2: 'u8',\n uint8x4: 'u8',\n sint8: 'i8',\n sint8x2: 'i8',\n sint8x4: 'i8',\n unorm8: 'u8',\n unorm8x2: 'u8',\n unorm8x4: 'u8',\n snorm8: 'i8',\n snorm8x2: 'i8',\n snorm8x4: 'i8',\n uint16: 'u16',\n uint16x2: 'u16',\n uint16x4: 'u16',\n sint16: 'i16',\n sint16x2: 'i16',\n sint16x4: 'i16',\n unorm16: 'u16',\n unorm16x2: 'u16',\n unorm16x4: 'u16',\n snorm16: 'i16',\n snorm16x2: 'i16',\n snorm16x4: 'i16',\n float16: 'f16',\n float16x2: 'f16',\n float16x4: 'f16',\n float32: 'f32',\n float32x2: 'f32',\n float32x3: 'f32',\n float32x4: 'f32',\n uint32: 'u32',\n uint32x2: 'u32',\n uint32x3: 'u32',\n uint32x4: 'u32',\n sint32: 'i32',\n sint32x2: 'i32',\n sint32x3: 'i32',\n sint32x4: 'i32',\n} as const;\n\nconst primitiveToWriteFunction = {\n u32: 'setUint32',\n i32: 'setInt32',\n f32: 'setFloat32',\n u16: 'setUint16',\n i16: 'setInt16',\n f16: 'setFloat16',\n u8: 'setUint8',\n i8: 'setInt8',\n} as const;\n\n/**\n * @privateRemarks\n * based on the `Channel Formats` table https://www.w3.org/TR/WGSL/#texel-formats\n */\nconst vertexFormatValueTransform = {\n unorm8: (value: string) => `Math.round(${value} * 255)`,\n unorm8x2: (value: string) => `Math.round(${value} * 255)`,\n unorm8x4: (value: string) => `Math.round(${value} * 255)`,\n snorm8: (value: string) => `Math.round(${value} * 127)`,\n snorm8x2: (value: string) => `Math.round(${value} * 127)`,\n snorm8x4: (value: string) => `Math.round(${value} * 127)`,\n unorm16: (value: string) => `Math.round(${value} * 65535)`,\n unorm16x2: (value: string) => `Math.round(${value} * 65535)`,\n unorm16x4: (value: string) => `Math.round(${value} * 65535)`,\n snorm16: (value: string) => `Math.round(${value} * 32767)`,\n snorm16x2: (value: string) => `Math.round(${value} * 32767)`,\n snorm16x4: (value: string) => `Math.round(${value} * 32767)`,\n} as const;\n\nconst specialPackedFormats = {\n 'unorm10-10-10-2': {\n writeFunction: 'setUint32',\n generator: (offsetExpr: string, valueExpr: string) =>\n `output.setUint32(${offsetExpr}, ((${valueExpr}.x*1023&0x3FF)<<22)|((${valueExpr}.y*1023&0x3FF)<<12)|((${valueExpr}.z*1023&0x3FF)<<2)|(${valueExpr}.w*3&3), littleEndian);\\n`,\n },\n 'unorm8x4-bgra': {\n writeFunction: 'setUint8',\n generator: (offsetExpr: string, valueExpr: string) => {\n const bgraComponents = ['z', 'y', 'x', 'w'];\n let code = '';\n for (let i = 0; i < 4; i++) {\n code +=\n `output.setUint8((${offsetExpr} + ${i}), Math.round(${valueExpr}.${\n bgraComponents[i]\n } * 255), littleEndian);\\n`;\n }\n return code;\n },\n },\n} as const;\n\nexport function buildWriter(\n node: wgsl.BaseData,\n offsetExpr: string,\n valueExpr: string,\n): string {\n if (wgsl.isAtomic(node) || wgsl.isDecorated(node)) {\n return buildWriter(node.inner, offsetExpr, valueExpr);\n }\n\n if (wgsl.isWgslStruct(node) || isUnstruct(node)) {\n const propOffsets = offsetsForProps(node);\n let code = '';\n for (const [key, propOffset] of Object.entries(propOffsets)) {\n const subSchema = node.propTypes[key];\n if (!subSchema) continue;\n code += buildWriter(\n subSchema,\n `(${offsetExpr} + ${propOffset.offset})`,\n `${valueExpr}.${key}`,\n );\n }\n return code;\n }\n\n if (wgsl.isWgslArray(node) || isDisarray(node)) {\n const elementSize = roundUp(\n sizeOf(node.elementType),\n alignmentOf(node),\n );\n let code = '';\n\n code += `for (let i = 0; i < ${node.elementCount}; i++) {\\n`;\n code += buildWriter(\n node.elementType,\n `(${offsetExpr} + i * ${elementSize})`,\n `${valueExpr}[i]`,\n );\n code += '}\\n';\n\n return code;\n }\n\n if (wgsl.isVec(node)) {\n const primitive = typeToPrimitive[node.type];\n let code = '';\n const writeFunc = primitiveToWriteFunction[primitive];\n const components = ['x', 'y', 'z', 'w'];\n const count = wgsl.isVec2(node) ? 2 : wgsl.isVec3(node) ? 3 : 4;\n\n for (let i = 0; i < count; i++) {\n code += `output.${writeFunc}((${offsetExpr} + ${i * 4}), ${valueExpr}.${\n components[i]\n }, littleEndian);\\n`;\n }\n return code;\n }\n\n if (wgsl.isMat(node)) {\n const primitive = typeToPrimitive[node.type];\n const writeFunc = primitiveToWriteFunction[primitive];\n\n const matSize = wgsl.isMat2x2f(node) ? 2 : wgsl.isMat3x3f(node) ? 3 : 4;\n const elementCount = matSize * matSize;\n const rowStride = roundUp(matSize * 4, 8);\n\n let code = '';\n for (let i = 0; i < elementCount; i++) {\n const colIndex = Math.floor(i / matSize);\n const rowIndex = i % matSize;\n const byteOffset = colIndex * rowStride + rowIndex * 4;\n\n code +=\n `output.${writeFunc}((${offsetExpr} + ${byteOffset}), ${valueExpr}.columns[${colIndex}].${\n ['x', 'y', 'z', 'w'][rowIndex]\n }, littleEndian);\\n`;\n }\n\n return code;\n }\n\n if (isPackedData(node)) {\n const formatName = node.type;\n\n if (formatName in specialPackedFormats) {\n const handler =\n specialPackedFormats[formatName as keyof typeof specialPackedFormats];\n return handler.generator(offsetExpr, valueExpr);\n }\n\n const primitive = vertexFormatToPrimitive[\n formatName as keyof typeof vertexFormatToPrimitive\n ];\n const writeFunc = primitiveToWriteFunction[primitive];\n const wgslType = formatToWGSLType[formatName];\n const componentCount = wgsl.isVec4(wgslType)\n ? 4\n : wgsl.isVec3(wgslType)\n ? 3\n : wgsl.isVec2(wgslType)\n ? 2\n : 1;\n const componentSize = primitive === 'u8' || primitive === 'i8'\n ? 1\n : primitive === 'u16' || primitive === 'i16' || primitive === 'f16'\n ? 2\n : 4;\n const components = ['x', 'y', 'z', 'w'];\n const transform = vertexFormatValueTransform[\n formatName as keyof typeof vertexFormatValueTransform\n ];\n\n let code = '';\n for (let i = 0; i < componentCount; i++) {\n const accessor = componentCount === 1\n ? valueExpr\n : `${valueExpr}.${components[i]}`;\n const value = transform ? transform(accessor) : accessor;\n code += `output.${writeFunc}((${offsetExpr} + ${\n i * componentSize\n }), ${value}, littleEndian);\\n`;\n }\n\n return code;\n }\n\n if (!Object.hasOwn(typeToPrimitive, node.type)) {\n throw new Error(\n `Primitive ${node.type} is unsupported by compiled writer`,\n );\n }\n\n const primitive = typeToPrimitive[node.type as keyof typeof typeToPrimitive];\n return `output.${\n primitiveToWriteFunction[primitive]\n }(${offsetExpr}, ${valueExpr}, littleEndian);\\n`;\n}\n\nexport function getCompiledWriterForSchema<T extends wgsl.BaseData>(\n schema: T,\n):\n | ((\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n ) => void)\n | undefined {\n if (!EVAL_ALLOWED_IN_ENV) {\n console.warn(\n 'This environment does not allow eval - using default writer as fallback',\n );\n return undefined;\n }\n\n if (compiledWriters.has(schema)) {\n return compiledWriters.get(schema) as (\n output: DataView,\n offset: number,\n value: Infer<T>,\n littleEndian?: boolean,\n ) => void;\n }\n\n try {\n const body = buildWriter(schema, 'offset', 'value');\n\n const fn = new Function(\n 'output',\n 'offset',\n 'value',\n 'littleEndian=true',\n body,\n ) as (\n output: DataView,\n offset: number,\n value: Infer<T> | unknown,\n littleEndian?: boolean,\n ) => void;\n\n compiledWriters.set(schema, fn);\n\n return fn;\n } catch (error) {\n console.warn(\n `Failed to compile writer for schema: ${schema}\\nReason: ${\n error instanceof Error ? error.message : String(error)\n }\\nFalling back to default writer`,\n );\n }\n}\n","import type { ISerialInput, ISerialOutput } from 'typed-binary';\nimport type { Infer, InferRecord } from '../shared/repr.ts';\nimport alignIO from './alignIO.ts';\nimport { alignmentOf, customAlignmentOf } from './alignmentOf.ts';\nimport type {\n AnyConcreteData,\n AnyData,\n Disarray,\n LooseDecorated,\n Unstruct,\n} from './dataTypes.ts';\nimport { mat2x2f, mat3x3f, mat4x4f } from './matrix.ts';\nimport { sizeOf } from './sizeOf.ts';\nimport {\n vec2f,\n vec2h,\n vec2i,\n vec2u,\n vec3f,\n vec3h,\n vec3i,\n vec3u,\n vec4f,\n vec4h,\n vec4i,\n vec4u,\n} from './vector.ts';\nimport type * as wgsl from './wgslTypes.ts';\n\ntype DataWriter<TSchema extends wgsl.BaseData> = (\n output: ISerialOutput,\n schema: TSchema,\n value: Infer<TSchema>,\n) => void;\n\ntype DataReader<TSchema extends wgsl.BaseData> = (\n input: ISerialInput,\n schema: TSchema,\n) => Infer<TSchema>;\n\ntype CompleteDataWriters = {\n [TType in AnyConcreteData['type']]: DataWriter<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\ntype CompleteDataReaders = {\n [TType in AnyConcreteData['type']]: DataReader<\n Extract<AnyData, { readonly type: TType }>\n >;\n};\n\nconst dataWriters = {\n bool() {\n throw new Error('Booleans are not host-shareable');\n },\n\n f32(output, _schema: wgsl.F32, value: number) {\n output.writeFloat32(value);\n },\n\n f16(output, _schema: wgsl.F16, value: number) {\n output.writeFloat16(value);\n },\n\n i32(output, _schema: wgsl.I32, value: number) {\n output.writeInt32(value);\n },\n\n u32(output, _schema: wgsl.U32, value: number) {\n output.writeUint32(value);\n },\n\n u16(output, _schema: wgsl.U16, value: number) {\n output.writeUint16(value);\n },\n\n vec2f(output, _, value: wgsl.v2f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n },\n\n vec2h(output, _, value: wgsl.v2h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n },\n\n vec2i(output, _, value: wgsl.v2i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n },\n\n vec2u(output, _, value: wgsl.v2u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n },\n\n 'vec2<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n vec3f(output, _, value: wgsl.v3f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n },\n\n vec3h(output, _, value: wgsl.v3h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n },\n\n vec3i(output, _, value: wgsl.v3i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n },\n\n vec3u(output, _, value: wgsl.v3u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n },\n\n 'vec3<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n vec4f(output, _, value: wgsl.v4f) {\n output.writeFloat32(value.x);\n output.writeFloat32(value.y);\n output.writeFloat32(value.z);\n output.writeFloat32(value.w);\n },\n\n vec4h(output, _, value: wgsl.v4h) {\n output.writeFloat16(value.x);\n output.writeFloat16(value.y);\n output.writeFloat16(value.z);\n output.writeFloat16(value.w);\n },\n\n vec4i(output, _, value: wgsl.v4i) {\n output.writeInt32(value.x);\n output.writeInt32(value.y);\n output.writeInt32(value.z);\n output.writeInt32(value.w);\n },\n\n vec4u(output, _, value: wgsl.v4u) {\n output.writeUint32(value.x);\n output.writeUint32(value.y);\n output.writeUint32(value.z);\n output.writeUint32(value.w);\n },\n\n 'vec4<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n mat2x2f(output, _, value: wgsl.m2x2f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat3x3f(output, _, value: wgsl.m3x3f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n mat4x4f(output, _, value: wgsl.m4x4f) {\n for (let i = 0; i < value.length; ++i) {\n output.writeFloat32(value[i] as number);\n }\n },\n\n struct(\n output,\n schema: wgsl.WgslStruct,\n value: InferRecord<Record<string, wgsl.BaseData>>,\n ) {\n const alignment = alignmentOf(schema);\n alignIO(output, alignment);\n\n for (const [key, property] of Object.entries(schema.propTypes)) {\n alignIO(output, alignmentOf(property as wgsl.BaseData));\n writeData(output, property as wgsl.BaseData, value[key]);\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(Math.round(value * 255));\n },\n unorm8x2(output, _, value: wgsl.v2f) {\n output.writeUint8(Math.round(value.x * 255));\n output.writeUint8(Math.round(value.y * 255));\n },\n unorm8x4(output, _, value: wgsl.v4f) {\n output.writeUint8(Math.round(value.x * 255));\n output.writeUint8(Math.round(value.y * 255));\n output.writeUint8(Math.round(value.z * 255));\n output.writeUint8(Math.round(value.w * 255));\n },\n snorm8(output, _, value: number) {\n output.writeInt8(Math.round(value * 127));\n },\n snorm8x2(output, _, value: wgsl.v2f) {\n output.writeInt8(Math.round(value.x * 127));\n output.writeInt8(Math.round(value.y * 127));\n },\n snorm8x4(output, _, value: wgsl.v4f) {\n output.writeInt8(Math.round(value.x * 127));\n output.writeInt8(Math.round(value.y * 127));\n output.writeInt8(Math.round(value.z * 127));\n output.writeInt8(Math.round(value.w * 127));\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.writeInt16(Math.round(value * 32767));\n },\n snorm16x2(output, _, value: wgsl.v2f) {\n output.writeInt16(Math.round(value.x * 32767));\n output.writeInt16(Math.round(value.y * 32767));\n },\n snorm16x4(output, _, value: wgsl.v4f) {\n output.writeInt16(Math.round(value.x * 32767));\n output.writeInt16(Math.round(value.y * 32767));\n output.writeInt16(Math.round(value.z * 32767));\n output.writeInt16(Math.round(value.w * 32767));\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.y * 1023) & 1023) << 12; // g (10 bits)\n packed |= ((value.z * 1023) & 1023) << 2; // b (10 bits)\n packed |= (value.w * 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 const propTypes = schema.propTypes as Record<string, wgsl.BaseData>;\n for (const [key, property] of Object.entries(propTypes)) {\n dataWriters[property.type]?.(output, property, value[key]);\n }\n },\n\n 'loose-decorated'(output, schema: LooseDecorated, value: unknown) {\n const alignment = customAlignmentOf(schema);\n alignIO(output, alignment);\n\n const beginning = output.currentByteOffset;\n const writer = dataWriters[(schema.inner as AnyData)?.type];\n writer?.(output, schema.inner, value);\n output.seekTo(beginning + sizeOf(schema));\n return value;\n },\n} satisfies CompleteDataWriters as Record<\n string,\n (output: ISerialOutput, schema: unknown, value: unknown) => void\n>;\n\nexport function writeData<TData extends wgsl.BaseData>(\n output: ISerialOutput,\n schema: TData,\n value: Infer<TData>,\n): void {\n const writer = dataWriters[schema.type];\n if (!writer) {\n throw new Error(`Cannot write data of type '${schema.type}'.`);\n }\n\n writer(output, schema, value);\n}\n\nconst dataReaders = {\n bool(): boolean {\n throw new Error('Booleans are not host-shareable');\n },\n\n f32(input: ISerialInput): number {\n return input.readFloat32();\n },\n\n f16(input: ISerialInput): number {\n return input.readFloat16();\n },\n\n i32(input: ISerialInput): number {\n return input.readInt32();\n },\n\n u32(input: ISerialInput): number {\n return input.readUint32();\n },\n\n u16(input: ISerialInput): number {\n return input.readUint16();\n },\n\n vec2f(input: ISerialInput): wgsl.v2f {\n return vec2f(input.readFloat32(), input.readFloat32());\n },\n\n vec3f(input: ISerialInput): wgsl.v3f {\n return vec3f(input.readFloat32(), input.readFloat32(), input.readFloat32());\n },\n\n vec4f(input: ISerialInput): wgsl.v4f {\n return vec4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n vec2h(input): wgsl.v2h {\n return vec2h(input.readFloat16(), input.readFloat16());\n },\n\n vec3h(input: ISerialInput): wgsl.v3h {\n return vec3h(input.readFloat16(), input.readFloat16(), input.readFloat16());\n },\n\n vec4h(input: ISerialInput): wgsl.v4h {\n return vec4h(\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n input.readFloat16(),\n );\n },\n\n vec2i(input): wgsl.v2i {\n return vec2i(input.readInt32(), input.readInt32());\n },\n\n vec3i(input: ISerialInput): wgsl.v3i {\n return vec3i(input.readInt32(), input.readInt32(), input.readInt32());\n },\n\n vec4i(input: ISerialInput): wgsl.v4i {\n return vec4i(\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n input.readInt32(),\n );\n },\n\n vec2u(input): wgsl.v2u {\n return vec2u(input.readUint32(), input.readUint32());\n },\n\n vec3u(input: ISerialInput): wgsl.v3u {\n return vec3u(input.readUint32(), input.readUint32(), input.readUint32());\n },\n\n vec4u(input: ISerialInput): wgsl.v4u {\n return vec4u(\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n input.readUint32(),\n );\n },\n\n 'vec2<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n 'vec3<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n 'vec4<bool>'() {\n throw new Error('Booleans are not host-shareable');\n },\n\n mat2x2f(input: ISerialInput): wgsl.m2x2f {\n return mat2x2f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n mat3x3f(input: ISerialInput): wgsl.m3x3f {\n const skipOneAfter = () => {\n const value = input.readFloat32();\n input.readFloat32(); // skipping;\n return value;\n };\n\n return mat3x3f(\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n //\n input.readFloat32(),\n input.readFloat32(),\n skipOneAfter(),\n );\n },\n\n mat4x4f(input: ISerialInput): wgsl.m4x4f {\n return mat4x4f(\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n //\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n input.readFloat32(),\n );\n },\n\n struct(input: ISerialInput, schema: wgsl.WgslStruct) {\n const alignment = alignmentOf(schema);\n alignIO(input, alignment);\n const result = {} as Record<string, unknown>;\n\n const propTypes = schema.propTypes as Record<string, wgsl.BaseData>;\n for (const [key, property] of Object.entries(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.readInt8() / 127,\n snorm8x2: (i) => vec2f(i.readInt8() / 127, i.readInt8() / 127),\n snorm8x4: (i) =>\n vec4f(\n i.readInt8() / 127,\n i.readInt8() / 127,\n i.readInt8() / 127,\n i.readInt8() / 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.readInt16() / 32767,\n snorm16x2: (i): wgsl.v2f =>\n vec2f(i.readInt16() / 32767, i.readInt16() / 32767),\n snorm16x4: (i): wgsl.v4f =>\n vec4f(\n i.readInt16() / 32767,\n i.readInt16() / 32767,\n i.readInt16() / 32767,\n i.readInt16() / 32767,\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 const propTypes = schema.propTypes as Record<string, wgsl.BaseData>;\n for (const [key, property] of Object.entries(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 { BufferReader, BufferWriter, getSystemEndianness } from 'typed-binary';\nimport { getCompiledWriterForSchema } from '../../data/compiledIO.ts';\nimport { readData, writeData } from '../../data/dataIO.ts';\nimport { getWriteInstructions } from '../../data/partialIO.ts';\nimport { sizeOf } from '../../data/sizeOf.ts';\nimport type { BaseData } from '../../data/wgslTypes.ts';\nimport { isWgslData } from '../../data/wgslTypes.ts';\nimport type { StorageFlag } from '../../extension.ts';\nimport type { TgpuNamable } from '../../shared/meta.ts';\nimport { getName, setName } from '../../shared/meta.ts';\nimport type {\n Infer,\n InferPartial,\n IsValidIndexSchema,\n IsValidStorageSchema,\n IsValidUniformSchema,\n IsValidVertexSchema,\n MemIdentity,\n} from '../../shared/repr.ts';\nimport { $internal } from '../../shared/symbols.ts';\nimport type {\n Prettify,\n UnionToIntersection,\n} from '../../shared/utilityTypes.ts';\nimport { isGPUBuffer } from '../../types.ts';\nimport type { ExperimentalTgpuRoot } from '../root/rootTypes.ts';\nimport {\n asMutable,\n asReadonly,\n asUniform,\n type TgpuBufferMutable,\n type TgpuBufferReadonly,\n type TgpuBufferUniform,\n type TgpuFixedBufferUsage,\n} from './bufferUsage.ts';\nimport type { AnyData } from '../../data/dataTypes.ts';\n\n// ----------\n// Public API\n// ----------\n\nexport interface UniformFlag {\n usableAsUniform: true;\n}\n\n/**\n * @deprecated Use UniformFlag instead.\n */\nexport type Uniform = UniformFlag;\n\nexport interface VertexFlag {\n usableAsVertex: true;\n}\n\nexport interface IndexFlag {\n usableAsIndex: true;\n}\n\n/**\n * @deprecated Use VertexFlag instead.\n */\nexport type Vertex = VertexFlag;\n\ntype LiteralToUsageType<T extends 'uniform' | 'storage' | 'vertex' | 'index'> =\n T extends 'uniform' ? UniformFlag\n : T extends 'storage' ? StorageFlag\n : T extends 'vertex' ? VertexFlag\n : T extends 'index' ? IndexFlag\n : never;\n\ntype ViewUsages<TBuffer extends TgpuBuffer<BaseData>> =\n | (boolean extends TBuffer['usableAsUniform'] ? never : 'uniform')\n | (boolean extends TBuffer['usableAsStorage'] ? never\n : 'readonly' | 'mutable');\n\ntype UsageTypeToBufferUsage<TData extends BaseData> = {\n uniform: TgpuBufferUniform<TData> & TgpuFixedBufferUsage<TData>;\n mutable: TgpuBufferMutable<TData> & TgpuFixedBufferUsage<TData>;\n readonly: TgpuBufferReadonly<TData> & TgpuFixedBufferUsage<TData>;\n};\n\nconst usageToUsageConstructor = {\n uniform: asUniform,\n mutable: asMutable,\n readonly: asReadonly,\n};\n\n/**\n * Done as an object to later Prettify it\n */\ntype InnerValidUsagesFor<T> = {\n usage:\n | (IsValidStorageSchema<T> extends true ? 'storage' : never)\n | (IsValidUniformSchema<T> extends true ? 'uniform' : never)\n | (IsValidVertexSchema<T> extends true ? 'vertex' : never)\n | (IsValidIndexSchema<T> extends true ? 'index' : never);\n};\n\nexport type ValidUsagesFor<T> = InnerValidUsagesFor<T>['usage'];\n\nexport interface TgpuBuffer<TData extends BaseData> extends TgpuNamable {\n readonly [$internal]: true;\n readonly resourceType: 'buffer';\n readonly dataType: TData;\n readonly initial?: Infer<TData> | undefined;\n\n readonly buffer: GPUBuffer;\n readonly destroyed: boolean;\n\n usableAsUniform: boolean;\n usableAsStorage: boolean;\n usableAsVertex: boolean;\n usableAsIndex: boolean;\n\n $usage<\n T extends [\n Prettify<InnerValidUsagesFor<TData>>['usage'],\n ...Prettify<InnerValidUsagesFor<TData>>['usage'][],\n ],\n >(\n ...usages: T\n ): this & UnionToIntersection<LiteralToUsageType<T[number]>>;\n $addFlags(flags: GPUBufferUsageFlags): this;\n\n as<T extends ViewUsages<this>>(usage: T): UsageTypeToBufferUsage<TData>[T];\n\n compileWriter(): void;\n write(data: Infer<TData>): void;\n writePartial(data: InferPartial<TData>): void;\n copyFrom(srcBuffer: TgpuBuffer<MemIdentity<TData>>): void;\n read(): Promise<Infer<TData>>;\n destroy(): void;\n}\n\nexport function INTERNAL_createBuffer<TData extends AnyData>(\n group: ExperimentalTgpuRoot,\n typeSchema: TData,\n initialOrBuffer?: Infer<TData> | GPUBuffer,\n): TgpuBuffer<TData> {\n if (!isWgslData(typeSchema)) {\n return new TgpuBufferImpl(group, typeSchema, initialOrBuffer, [\n 'storage',\n 'uniform',\n ]);\n }\n\n return new TgpuBufferImpl(group, typeSchema, initialOrBuffer);\n}\n\nexport function isBuffer<T extends TgpuBuffer<AnyData>>(\n value: T | unknown,\n): value is T {\n return (value as TgpuBuffer<AnyData>).resourceType === 'buffer';\n}\n\nexport function isUsableAsVertex<T extends TgpuBuffer<AnyData>>(\n buffer: T,\n): buffer is T & VertexFlag {\n return !!(buffer as unknown as VertexFlag).usableAsVertex;\n}\n\nexport function isUsableAsIndex<T extends TgpuBuffer<AnyData>>(\n buffer: T,\n): buffer is T & IndexFlag {\n return !!(buffer as unknown as IndexFlag).usableAsIndex;\n}\n\n// --------------\n// Implementation\n// --------------\nconst endianness = getSystemEndianness();\n\nclass TgpuBufferImpl<TData extends AnyData> implements TgpuBuffer<TData> {\n public readonly [$internal] = true;\n public readonly resourceType = 'buffer';\n public flags: GPUBufferUsageFlags = GPUBufferUsage.COPY_DST |\n GPUBufferUsage.COPY_SRC;\n private _buffer: GPUBuffer | null = null;\n private _ownBuffer: boolean;\n private _destroyed = false;\n private _hostBuffer: ArrayBuffer | undefined;\n\n readonly initial: Infer<TData> | undefined;\n\n usableAsUniform = false;\n usableAsStorage = false;\n usableAsVertex = false;\n usableAsIndex = false;\n\n constructor(\n private readonly _group: ExperimentalTgpuRoot,\n public readonly dataType: TData,\n public readonly initialOrBuffer?: Infer<TData> | GPUBuffer | undefined,\n private readonly _disallowedUsages?:\n ('uniform' | 'storage' | 'vertex' | 'index')[],\n ) {\n if (isGPUBuffer(initialOrBuffer)) {\n this._ownBuffer = false;\n this._buffer = initialOrBuffer;\n } else {\n this._ownBuffer = true;\n this.initial = initialOrBuffer;\n }\n }\n\n get buffer() {\n const device = this._group.device;\n\n if (this._destroyed) {\n throw new Error('This buffer has been destroyed');\n }\n\n if (!this._buffer) {\n this._buffer = device.createBuffer({\n size: sizeOf(this.dataType),\n usage: this.flags,\n mappedAtCreation: !!this.initial,\n label: getName(this) ?? '<unnamed>',\n });\n\n if (this.initial) {\n const writer = new BufferWriter(this._buffer.getMappedRange());\n writeData(writer, this.dataType, this.initial);\n this._buffer.unmap();\n }\n }\n\n return this._buffer;\n }\n\n get destroyed() {\n return this._destroyed;\n }\n\n $name(label: string) {\n setName(this, label);\n if (this._buffer) {\n this._buffer.label = label;\n }\n return this;\n }\n\n $usage<T extends ('uniform' | 'storage' | 'vertex' | 'index')[]>(\n ...usages: T\n ): this & UnionToIntersection<LiteralToUsageType<T[number]>> {\n for (const usage of usages) {\n if (this._disallowedUsages?.includes(usage)) {\n throw new Error(\n `Buffer of type ${this.dataType.type} cannot be used as ${usage}`,\n );\n }\n\n this.flags |= usage === 'uniform' ? GPUBufferUsage.UNIFORM : 0;\n this.flags |= usage === 'storage' ? GPUBufferUsage.STORAGE : 0;\n this.flags |= usage === 'vertex' ? GPUBufferUsage.VERTEX : 0;\n this.flags |= usage === 'index' ? GPUBufferUsage.INDEX : 0;\n this.usableAsUniform = this.usableAsUniform || usage === 'uniform';\n this.usableAsStorage = this.usableAsStorage || usage === 'storage';\n this.usableAsVertex = this.usableAsVertex || usage === 'vertex';\n this.usableAsIndex = this.usableAsIndex || usage === 'index';\n }\n return this as this & UnionToIntersection<LiteralToUsageType<T[number]>>;\n }\n\n $addFlags(flags: GPUBufferUsageFlags) {\n if (!this._ownBuffer) {\n throw new Error(\n 'Cannot add flags to a buffer that is not managed by TypeGPU.',\n );\n }\n\n if (flags & GPUBufferUsage.MAP_READ) {\n this.flags = GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ;\n return this;\n }\n\n if (flags & GPUBufferUsage.MAP_WRITE) {\n this.flags = GPUBufferUsage.COPY_SRC | GPUBufferUsage.MAP_WRITE;\n return this;\n }\n\n this.flags |= flags;\n return this;\n }\n\n compileWriter(): void {\n getCompiledWriterForSchema(this.dataType);\n }\n\n private _writeToTarget(\n target: ArrayBuffer,\n data: Infer<TData>,\n ): void {\n const compiledWriter = getCompiledWriterForSchema(this.dataType);\n\n if (compiledWriter) {\n try {\n compiledWriter(\n new DataView(target),\n 0,\n data,\n endianness === 'little',\n );\n return;\n } catch (error) {\n console.error(\n `Error when using compiled writer for buffer ${\n getName(this) ?? '<unnamed>'\n } - this is likely a bug, please submit an issue at https://github.com/software-mansion/TypeGPU/issues\\nUsing fallback writer instead.`,\n error,\n );\n }\n }\n\n writeData(new BufferWriter(target), this.dataType, data);\n }\n\n write(data: Infer<TData>): void {\n const gpuBuffer = this.buffer;\n const device = this._group.device;\n\n if (gpuBuffer.mapState === 'mapped') {\n const mapped = gpuBuffer.getMappedRange();\n this._writeToTarget(mapped, data);\n return;\n }\n\n const size = sizeOf(this.dataType);\n if (!this._hostBuffer) {\n this._hostBuffer = new ArrayBuffer(size);\n }\n\n // Flushing any commands yet to be encoded.\n this._group.flush();\n\n this._writeToTarget(this._hostBuffer, data);\n device.queue.writeBuffer(gpuBuffer, 0, this._hostBuffer, 0, size);\n }\n\n public writePartial(data: InferPartial<TData>): void {\n const gpuBuffer = this.buffer;\n const device = this._group.device;\n\n const instructions = getWriteInstructions(this.dataType, data);\n\n if (gpuBuffer.mapState === 'mapped') {\n const mappedRange = gpuBuffer.getMappedRange();\n const mappedView = new Uint8Array(mappedRange);\n\n for (const instruction of instructions) {\n mappedView.set(instruction.data, instruction.data.byteOffset);\n }\n } else {\n for (const instruction of instructions) {\n device.queue.writeBuffer(\n gpuBuffer,\n instruction.data.byteOffset,\n instruction.data,\n 0,\n instruction.data.byteLength,\n );\n }\n }\n }\n\n copyFrom(srcBuffer: TgpuBuffer<MemIdentity<TData>>): void {\n if (this.buffer.mapState === 'mapped') {\n throw new Error('Cannot copy to a mapped buffer.');\n }\n\n const size = sizeOf(this.dataType);\n const encoder = this._group.commandEncoder;\n encoder.copyBufferToBuffer(srcBuffer.buffer, 0, this.buffer, 0, size);\n }\n\n async read(): Promise<Infer<TData>> {\n // Flushing any commands yet to be encoded.\n this._group.flush();\n\n const gpuBuffer = this.buffer;\n const device = this._group.device;\n\n if (gpuBuffer.mapState === 'mapped') {\n const mapped = gpuBuffer.getMappedRange();\n return readData(new BufferReader(mapped), this.dataType);\n }\n\n if (gpuBuffer.usage & GPUBufferUsage.MAP_READ) {\n await gpuBuffer.mapAsync(GPUMapMode.READ);\n const mapped = gpuBuffer.getMappedRange();\n const res = readData(new BufferReader(mapped), this.dataType);\n gpuBuffer.unmap();\n return res;\n }\n\n const stagingBuffer = device.createBuffer({\n size: sizeOf(this.dataType),\n usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,\n });\n\n const commandEncoder = device.createCommandEncoder();\n commandEncoder.copyBufferToBuffer(\n gpuBuffer,\n 0,\n stagingBuffer,\n 0,\n sizeOf(this.dataType),\n );\n\n device.queue.submit([commandEncoder.finish()]);\n await device.queue.onSubmittedWorkDone();\n await stagingBuffer.mapAsync(GPUMapMode.READ, 0, sizeOf(this.dataType));\n\n const res = readData(\n new BufferReader(stagingBuffer.getMappedRange()),\n this.dataType,\n );\n\n stagingBuffer.unmap();\n stagingBuffer.destroy();\n\n return res;\n }\n\n as<T extends ViewUsages<this>>(usage: T): UsageTypeToBufferUsage<TData>[T] {\n return usageToUsageConstructor[usage]?.(\n this as never,\n ) as UsageTypeToBufferUsage<TData>[T];\n }\n\n destroy() {\n if (this._destroyed) {\n return;\n }\n this._destroyed = true;\n if (this._ownBuffer) {\n this._buffer?.destroy();\n }\n }\n\n toString(): string {\n return `buffer:${getName(this) ?? '<unnamed>'}`;\n }\n}\n","import { resolveData } from './core/resolve/resolveData.ts';\nimport { ConfigurableImpl } from './core/root/configurableImpl.ts';\nimport type { Configurable } from './core/root/rootTypes.ts';\nimport {\n type Eventual,\n isDerived,\n isProviding,\n isSlot,\n type SlotValuePair,\n type TgpuDerived,\n type TgpuSlot,\n} from './core/slot/slotTypes.ts';\nimport { getAttributesString } from './data/attributes.ts';\nimport { type AnyData, isData, type UnknownData } from './data/dataTypes.ts';\nimport { snip, type Snippet } from './data/snippet.ts';\nimport { isWgslArray, isWgslStruct } from './data/wgslTypes.ts';\nimport {\n invariant,\n MissingSlotValueError,\n ResolutionError,\n WgslTypeError,\n} from './errors.ts';\nimport { provideCtx, topLevelState } from './execMode.ts';\nimport type { NameRegistry } from './nameRegistry.ts';\nimport { naturalsExcept } from './shared/generators.ts';\nimport type { Infer } from './shared/repr.ts';\nimport { $internal, $providing } from './shared/symbols.ts';\nimport {\n bindGroupLayout,\n type TgpuBindGroup,\n TgpuBindGroupImpl,\n type TgpuBindGroupLayout,\n type TgpuLayoutEntry,\n} from './tgpuBindGroupLayout.ts';\nimport { coerceToSnippet } from './tgsl/generationHelpers.ts';\nimport { generateFunction } from './tgsl/wgslGenerator.ts';\nimport type {\n ExecMode,\n ExecState,\n FnToWgslOptions,\n ItemLayer,\n ItemStateStack,\n ResolutionCtx,\n Wgsl,\n} from './types.ts';\nimport {\n CodegenState,\n isMarkedInternal,\n isSelfResolvable,\n NormalState,\n} from './types.ts';\n\n/**\n * Inserted into bind group entry definitions that belong\n * to the automatically generated catch-all bind group.\n *\n * A non-occupied group index can only be determined after\n * every resource has been resolved, so this acts as a placeholder\n * to be replaced with an actual numeric index at the very end\n * of the resolution process.\n */\nconst CATCHALL_BIND_GROUP_IDX_MARKER = '#CATCHALL#';\n\nexport type ResolutionCtxImplOptions = {\n readonly names: NameRegistry;\n};\n\ntype SlotToValueMap = Map<TgpuSlot<unknown>, unknown>;\n\ntype SlotBindingLayer = {\n type: 'slotBinding';\n bindingMap: WeakMap<TgpuSlot<unknown>, unknown>;\n};\n\ntype FunctionScopeLayer = {\n type: 'functionScope';\n args: Snippet[];\n argAliases: Record<string, Snippet>;\n externalMap: Record<string, unknown>;\n returnType: AnyData;\n};\n\ntype BlockScopeLayer = {\n type: 'blockScope';\n declarations: Map<string, AnyData | UnknownData>;\n};\n\nclass ItemStateStackImpl implements ItemStateStack {\n private _stack: (\n | ItemLayer\n | SlotBindingLayer\n | FunctionScopeLayer\n | BlockScopeLayer\n )[] = [];\n private _itemDepth = 0;\n\n get itemDepth(): number {\n return this._itemDepth;\n }\n\n get topItem(): ItemLayer {\n const state = this._stack[this._stack.length - 1];\n if (!state || state.type !== 'item') {\n throw new Error('Internal error, expected item layer to be on top.');\n }\n return state;\n }\n\n get topFunctionReturnType(): AnyData {\n const scope = this._stack.findLast((e) => e.type === 'functionScope');\n if (!scope) {\n throw new Error('Internal error, expected function scope to be present.');\n }\n return scope.returnType;\n }\n\n pushItem() {\n this._itemDepth++;\n this._stack.push({\n type: 'item',\n usedSlots: new Set(),\n });\n }\n\n popItem() {\n this.pop('item');\n }\n\n pushSlotBindings(pairs: SlotValuePair<unknown>[]) {\n this._stack.push({\n type: 'slotBinding',\n bindingMap: new WeakMap(pairs),\n });\n }\n\n popSlotBindings() {\n this.pop('slotBinding');\n }\n\n pushFunctionScope(\n args: Snippet[],\n argAliases: Record<string, Snippet>,\n returnType: AnyData,\n externalMap: Record<string, unknown>,\n ) {\n this._stack.push({\n type: 'functionScope',\n args,\n argAliases,\n returnType,\n externalMap,\n });\n }\n\n popFunctionScope() {\n this.pop('functionScope');\n }\n\n pushBlockScope() {\n this._stack.push({\n type: 'blockScope',\n declarations: new Map<string, AnyData | UnknownData>(),\n });\n }\n\n popBlockScope() {\n this.pop('blockScope');\n }\n\n pop(type?: (typeof this._stack)[number]['type']) {\n const layer = this._stack[this._stack.length - 1];\n if (!layer || (type && layer.type !== type)) {\n throw new Error(`Internal error, expected a ${type} layer to be on top.`);\n }\n\n this._stack.pop();\n if (type === 'item') {\n this._itemDepth--;\n }\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n if (layer?.type === 'item') {\n // Binding not available yet, so this layer is dependent on the slot's value.\n layer.usedSlots.add(slot);\n } else if (layer?.type === 'slotBinding') {\n const boundValue = layer.bindingMap.get(slot);\n\n if (boundValue !== undefined) {\n return boundValue as T;\n }\n } else if (\n layer?.type === 'functionScope' ||\n layer?.type === 'blockScope'\n ) {\n // Skip\n } else {\n throw new Error('Unknown layer type.');\n }\n }\n\n return slot.defaultValue;\n }\n\n getSnippetById(id: string): Snippet | undefined {\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'functionScope') {\n const arg = layer.args.find((a) => a.value === id);\n if (arg !== undefined) {\n return arg;\n }\n\n if (layer.argAliases[id]) {\n return layer.argAliases[id];\n }\n\n const external = layer.externalMap[id];\n\n if (external !== undefined && external !== null) {\n return coerceToSnippet(external);\n }\n\n // Since functions cannot access resources from the calling scope, we\n // return early here.\n return undefined;\n }\n\n if (layer?.type === 'blockScope') {\n const declarationType = layer.declarations.get(id);\n if (declarationType !== undefined) {\n return snip(id, declarationType);\n }\n } else {\n // Skip\n }\n }\n\n return undefined;\n }\n\n defineBlockVariable(id: string, type: AnyData | UnknownData): Snippet {\n if (type.type === 'unknown') {\n throw Error(`Tried to define variable '${id}' of unknown type`);\n }\n\n for (let i = this._stack.length - 1; i >= 0; --i) {\n const layer = this._stack[i];\n\n if (layer?.type === 'blockScope') {\n layer.declarations.set(id, type);\n\n return snip(id, type);\n }\n }\n\n throw new Error('No block scope found to define a variable in.');\n }\n}\n\nconst INDENT = [\n '', // 0\n ' ', // 1\n ' ', // 2\n ' ', // 3\n ' ', // 4\n ' ', // 5\n ' ', // 6\n ' ', // 7\n ' ', // 8\n];\n\nconst N = INDENT.length - 1;\n\nexport class IndentController {\n private identLevel = 0;\n\n get pre(): string {\n return (\n INDENT[this.identLevel] ??\n (INDENT[N] as string).repeat(this.identLevel / N) +\n INDENT[this.identLevel % N]\n );\n }\n\n indent(): string {\n const str = this.pre;\n this.identLevel++;\n return str;\n }\n\n dedent(): string {\n this.identLevel--;\n return this.pre;\n }\n\n withResetLevel<T>(callback: () => T): T {\n const savedLevel = this.identLevel;\n this.identLevel = 0;\n try {\n return callback();\n } finally {\n this.identLevel = savedLevel;\n }\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 _itemStateStack = new ItemStateStackImpl();\n readonly #modeStack: ExecState[] = [];\n private readonly _declarations: string[] = [];\n private _varyingLocations: Record<string, number> | undefined;\n\n get varyingLocations() {\n return this._varyingLocations;\n }\n\n readonly [$internal] = {\n itemStateStack: this._itemStateStack,\n };\n\n // -- Bindings\n /**\n * A map from registered bind group layouts to random strings put in\n * place of their group index. The whole tree has to be traversed to\n * collect every use of a typed bind group layout, since they can be\n * explicitly imposed group indices, and they cannot collide.\n */\n public readonly bindGroupLayoutsToPlaceholderMap = new Map<\n TgpuBindGroupLayout,\n string\n >();\n private _nextFreeLayoutPlaceholderIdx = 0;\n public readonly fixedBindings: FixedBindingConfig[] = [];\n // --\n\n public readonly names: NameRegistry;\n public expectedType: AnyData | undefined;\n\n constructor(opts: ResolutionCtxImplOptions) {\n this.names = opts.names;\n }\n\n get pre(): string {\n return this._indentController.pre;\n }\n\n get topFunctionReturnType() {\n return this._itemStateStack.topFunctionReturnType;\n }\n\n indent(): string {\n return this._indentController.indent();\n }\n\n dedent(): string {\n return this._indentController.dedent();\n }\n\n withResetIndentLevel<T>(callback: () => T): T {\n return this._indentController.withResetLevel(callback);\n }\n\n getById(id: string): Snippet | null {\n const item = this._itemStateStack.getSnippetById(id);\n\n if (item === undefined) {\n return null;\n }\n\n return item;\n }\n\n defineVariable(id: string, dataType: AnyData | UnknownData): Snippet {\n return this._itemStateStack.defineBlockVariable(id, dataType);\n }\n\n pushBlockScope() {\n this._itemStateStack.pushBlockScope();\n }\n\n popBlockScope() {\n this._itemStateStack.popBlockScope();\n }\n\n fnToWgsl(options: FnToWgslOptions): { head: Wgsl; body: Wgsl } {\n this._itemStateStack.pushFunctionScope(\n options.args,\n options.argAliases,\n options.returnType,\n options.externalMap,\n );\n\n try {\n return {\n head: resolveFunctionHeader(this, options.args, options.returnType),\n body: generateFunction(this, options.body),\n };\n } finally {\n this._itemStateStack.popFunctionScope();\n }\n }\n\n addDeclaration(declaration: string): void {\n this._declarations.push(declaration);\n }\n\n allocateLayoutEntry(layout: TgpuBindGroupLayout): string {\n const memoMap = this.bindGroupLayoutsToPlaceholderMap;\n let placeholderKey = memoMap.get(layout);\n\n if (!placeholderKey) {\n placeholderKey = `#BIND_GROUP_LAYOUT_${this\n ._nextFreeLayoutPlaceholderIdx++}#`;\n memoMap.set(layout, placeholderKey);\n }\n\n return placeholderKey;\n }\n\n allocateFixedEntry(\n layoutEntry: TgpuLayoutEntry,\n resource: object,\n ): { group: string; binding: number } {\n const binding = this.fixedBindings.length;\n this.fixedBindings.push({ layoutEntry, resource });\n\n return {\n group: CATCHALL_BIND_GROUP_IDX_MARKER,\n binding,\n };\n }\n\n readSlot<T>(slot: TgpuSlot<T>): T {\n const value = this._itemStateStack.readSlot(slot);\n\n if (value === undefined) {\n throw new MissingSlotValueError(slot);\n }\n\n return value;\n }\n\n withSlots<T>(pairs: SlotValuePair<unknown>[], callback: () => T): T {\n this._itemStateStack.pushSlotBindings(pairs);\n\n try {\n return callback();\n } finally {\n this._itemStateStack.popSlotBindings();\n }\n }\n\n withVaryingLocations<T>(\n locations: Record<string, number>,\n callback: () => T,\n ): T {\n this._varyingLocations = locations;\n\n try {\n return callback();\n } finally {\n this._varyingLocations = undefined;\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 // Getting out of codegen or simulation mode so we can execute JS normally.\n this.pushMode(new NormalState());\n\n let result: T;\n try {\n result = derived['~compute']();\n } finally {\n this.popMode('normal');\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedDerived.set(derived, instances);\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(derived);\n }\n\n throw new ResolutionError(err, [derived]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n /**\n * @param item The item whose resolution should be either retrieved from the cache (if there is a cache hit), or resolved.\n */\n _getOrInstantiate(item: object): string {\n // All memoized versions of `item`\n const instances = this._memoizedResolves.get(item) ?? [];\n\n this._itemStateStack.pushItem();\n\n try {\n for (const instance of instances) {\n const slotValuePairs = [...instance.slotToValueMap.entries()];\n\n if (\n slotValuePairs.every(([slot, expectedValue]) =>\n slot.areEqual(this._itemStateStack.readSlot(slot), expectedValue)\n )\n ) {\n return instance.result;\n }\n }\n\n // If we got here, no item with the given slot-to-value combo exists in cache yet\n let result: string;\n if (isData(item)) {\n result = resolveData(this, item);\n } else if (isDerived(item) || isSlot(item)) {\n result = this.resolve(this.unwrap(item));\n } else if (isSelfResolvable(item)) {\n result = item['~resolve'](this);\n } else {\n throw new TypeError(\n `Unresolvable internal value: ${item} (as json: ${\n JSON.stringify(item)\n })`,\n );\n }\n\n // We know which slots the item used while resolving\n const slotToValueMap = new Map<TgpuSlot<unknown>, unknown>();\n for (const usedSlot of this._itemStateStack.topItem.usedSlots) {\n slotToValueMap.set(usedSlot, this._itemStateStack.readSlot(usedSlot));\n }\n\n instances.push({ slotToValueMap, result });\n this._memoizedResolves.set(item, instances);\n\n return result;\n } catch (err) {\n if (err instanceof ResolutionError) {\n throw err.appendToTrace(item);\n }\n\n throw new ResolutionError(err, [item]);\n } finally {\n this._itemStateStack.popItem();\n }\n }\n\n resolve(item: unknown, schema?: AnyData | undefined): string {\n if (isProviding(item)) {\n return this.withSlots(\n item[$providing].pairs,\n () => this.resolve(item[$providing].inner, schema),\n );\n }\n\n if (isMarkedInternal(item)) {\n // Top-level resolve\n if (this._itemStateStack.itemDepth === 0) {\n try {\n this.pushMode(new CodegenState());\n const result = provideCtx(this, () => this._getOrInstantiate(item));\n\n return `${[...this._declarations].join('\\n\\n')}${result}`;\n } finally {\n this.popMode('codegen');\n }\n }\n\n return this._getOrInstantiate(item);\n }\n\n // This is a value that comes from the outside, maybe we can coerce it\n if (typeof item === 'number') {\n if (\n schema?.type === 'abstractInt' ||\n schema?.type === 'u32' ||\n schema?.type === 'i32'\n ) {\n return String(item);\n }\n\n // Just picking the shorter one\n const exp = item.toExponential();\n const decimal = String(item);\n return exp.length < decimal.length ? exp : decimal;\n }\n\n if (typeof item !== 'object' && typeof item !== 'function') {\n return String(item);\n }\n\n if (schema && isWgslArray(schema)) {\n if (!Array.isArray(item)) {\n throw new WgslTypeError(\n `Cannot coerce ${item} into value of type '${schema}'`,\n );\n }\n\n if (schema.elementCount !== item.length) {\n throw new WgslTypeError(\n `Cannot create value of type '${schema}' from an array of length: ${item.length}`,\n );\n }\n\n const elementTypeString = this.resolve(schema.elementType);\n return `array<${elementTypeString}, ${schema.elementCount}>(${\n item.map((element) =>\n this.resolve(element, schema.elementType as AnyData)\n )\n })`;\n }\n\n if (Array.isArray(item)) {\n return `array(${item.map((element) => this.resolve(element))})`;\n }\n\n if (schema && isWgslStruct(schema)) {\n return `${this.resolve(schema)}(${\n Object.entries(schema.propTypes).map(([key, type_]) =>\n this.resolve((item as Infer<typeof schema>)[key], type_ as AnyData)\n )\n })`;\n }\n\n throw new WgslTypeError(\n `Value ${item} (as json: ${JSON.stringify(item)}) is not resolvable${\n schema ? ` to type ${schema}` : ''\n }`,\n );\n }\n\n pushMode(mode: ExecState) {\n this.#modeStack.push(mode);\n }\n\n popMode(expected?: ExecMode) {\n const mode = this.#modeStack.pop();\n if (expected !== undefined) {\n invariant(mode?.type === expected, 'Unexpected mode');\n }\n }\n\n get mode(): ExecState {\n return this.#modeStack[this.#modeStack.length - 1] ?? topLevelState;\n }\n}\n\n/**\n * The results of a WGSL resolution.\n *\n * @param code - The resolved code.\n * @param usedBindGroupLayouts - List of used `tgpu.bindGroupLayout`s.\n * @param catchall - Automatically constructed bind group for buffer usages and buffer shorthands, preceded by its index.\n */\nexport interface ResolutionResult {\n code: string;\n usedBindGroupLayouts: TgpuBindGroupLayout[];\n catchall: [number, TgpuBindGroup] | undefined;\n}\n\nexport function resolve(\n item: Wgsl,\n options: ResolutionCtxImplOptions,\n config?: (cfg: Configurable) => Configurable,\n): ResolutionResult {\n const ctx = new ResolutionCtxImpl(options);\n let code = config\n ? ctx.withSlots(\n config(new ConfigurableImpl([])).bindings,\n () => ctx.resolve(item),\n )\n : ctx.resolve(item);\n\n const memoMap = ctx.bindGroupLayoutsToPlaceholderMap;\n const usedBindGroupLayouts: 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 usedBindGroupLayouts[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() : undefined;\n\n for (const [layout, placeholder] of memoMap.entries()) {\n const idx = layout.index ?? automaticIds.next().value;\n usedBindGroupLayouts[idx] = layout;\n code = code.replaceAll(placeholder, String(idx));\n }\n\n return {\n code,\n usedBindGroupLayouts,\n catchall,\n };\n}\n\nexport function resolveFunctionHeader(\n ctx: ResolutionCtx,\n args: Snippet[],\n returnType: AnyData,\n) {\n const argList = args\n .map((arg) => `${arg.value}: ${ctx.resolve(arg.dataType as AnyData)}`)\n .join(', ');\n\n return returnType.type !== 'void'\n ? `(${argList}) -> ${getAttributesString(returnType)}${\n ctx.resolve(returnType)\n } `\n : `(${argList}) `;\n}\n"]}
|
package/index.d.cts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { T as TgpuBindGroupLayout, a as TgpuBindGroup, W as Wgsl, C as Configurable, b as TgpuBuffer, A as AnyData, c as TgpuVar, d as TgpuRoot, e as AnyWgslData, f as TgpuFn, g as TgpuBufferUsage, I as Infer, h as TgpuAccessor, i as TgpuDerived, j as TgpuSlot, k as TgpuVertexLayout, l as WgslArray, D as Disarray, m as fn, n as bindGroupLayout, v as vertexLayout, o as fragmentFn, p as vertexFn, q as computeFn, r as privateVar, w as workgroupVar, s as constant, t as declare, u as sampler, x as comparisonSampler } from './tgpuConstant-
|
2
|
-
export { aq as BindLayoutEntry, ac as Eventual, ar as ExtractBindGroupInputFromLayout, aJ as INTERNAL_GlobalExt, a0 as IndexFlag, as as LayoutEntryToInput, ak as Render, al as Sampled, Y as Storage, Z as StorageFlag, aj as TextureProps, ad as TgpuAnyTextureView, a6 as TgpuBufferMutable, a7 as TgpuBufferReadonly, a8 as TgpuBufferUniform, aG as TgpuComputeFn, aH as TgpuComputeFnShell, $ as TgpuComputePipeline, am as TgpuConst, aI as TgpuDeclare, aB as TgpuFnShell, aE as TgpuFragmentFn, aF as TgpuFragmentFnShell, at as TgpuLayoutComparisonSampler, au as TgpuLayoutEntry, av as TgpuLayoutExternalTexture, aw as TgpuLayoutSampler, ax as TgpuLayoutStorage, ay as TgpuLayoutStorageTexture, az as TgpuLayoutTexture, aA as TgpuLayoutUniform, a9 as TgpuMutable, ae as TgpuMutableTexture, ap as TgpuQuerySet, aa as TgpuReadonly, af as TgpuReadonlyTexture, _ as TgpuRenderPipeline, ag as TgpuSampledTexture, ao as TgpuSampler, ah as TgpuTexture, ab as TgpuUniform, aC as TgpuVertexFn, aD as TgpuVertexFnShell, ai as TgpuWriteonlyTexture, a1 as Uniform, a2 as UniformFlag, a3 as ValidUsagesFor, V as ValidateBufferSchema, an as VariableScope, a4 as Vertex, a5 as VertexFlag, R as WithBinding, S as WithCompute, U as WithFragment, X as WithVertex, y as isBuffer, P as isBufferShorthand, F as isComparisonSampler, B as isDerived, H as isSampledTextureView, G as isSampler, E as isSlot, J as isStorageTextureView, K as isTexture, Q as isTgpuFn, L as isUsableAsRender, M as isUsableAsSampled, N as isUsableAsStorage, O as isUsableAsUniform, z as isUsableAsVertex } from './tgpuConstant-
|
1
|
+
import { T as TgpuBindGroupLayout, a as TgpuBindGroup, W as Wgsl, C as Configurable, b as TgpuBuffer, A as AnyData, c as TgpuVar, d as TgpuRoot, e as AnyWgslData, f as TgpuFn, g as TgpuBufferUsage, I as Infer, h as TgpuAccessor, i as TgpuDerived, j as TgpuSlot, k as TgpuVertexLayout, l as WgslArray, D as Disarray, m as fn, n as bindGroupLayout, v as vertexLayout, o as fragmentFn, p as vertexFn, q as computeFn, r as privateVar, w as workgroupVar, s as constant, t as declare, u as sampler, x as comparisonSampler } from './tgpuConstant-BSgcF4zi.cjs';
|
2
|
+
export { aq as BindLayoutEntry, ac as Eventual, ar as ExtractBindGroupInputFromLayout, aJ as INTERNAL_GlobalExt, a0 as IndexFlag, as as LayoutEntryToInput, ak as Render, al as Sampled, Y as Storage, Z as StorageFlag, aj as TextureProps, ad as TgpuAnyTextureView, a6 as TgpuBufferMutable, a7 as TgpuBufferReadonly, a8 as TgpuBufferUniform, aG as TgpuComputeFn, aH as TgpuComputeFnShell, $ as TgpuComputePipeline, am as TgpuConst, aI as TgpuDeclare, aB as TgpuFnShell, aE as TgpuFragmentFn, aF as TgpuFragmentFnShell, at as TgpuLayoutComparisonSampler, au as TgpuLayoutEntry, av as TgpuLayoutExternalTexture, aw as TgpuLayoutSampler, ax as TgpuLayoutStorage, ay as TgpuLayoutStorageTexture, az as TgpuLayoutTexture, aA as TgpuLayoutUniform, a9 as TgpuMutable, ae as TgpuMutableTexture, ap as TgpuQuerySet, aa as TgpuReadonly, af as TgpuReadonlyTexture, _ as TgpuRenderPipeline, ag as TgpuSampledTexture, ao as TgpuSampler, ah as TgpuTexture, ab as TgpuUniform, aC as TgpuVertexFn, aD as TgpuVertexFnShell, ai as TgpuWriteonlyTexture, a1 as Uniform, a2 as UniformFlag, a3 as ValidUsagesFor, V as ValidateBufferSchema, an as VariableScope, a4 as Vertex, a5 as VertexFlag, R as WithBinding, S as WithCompute, U as WithFragment, X as WithVertex, y as isBuffer, P as isBufferShorthand, F as isComparisonSampler, B as isDerived, H as isSampledTextureView, G as isSampler, E as isSlot, J as isStorageTextureView, K as isTexture, Q as isTgpuFn, L as isUsableAsRender, M as isUsableAsSampled, N as isUsableAsStorage, O as isUsableAsUniform, z as isUsableAsVertex } from './tgpuConstant-BSgcF4zi.cjs';
|
3
3
|
import 'tinyest';
|
4
4
|
|
5
5
|
/**
|
package/index.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { T as TgpuBindGroupLayout, a as TgpuBindGroup, W as Wgsl, C as Configurable, b as TgpuBuffer, A as AnyData, c as TgpuVar, d as TgpuRoot, e as AnyWgslData, f as TgpuFn, g as TgpuBufferUsage, I as Infer, h as TgpuAccessor, i as TgpuDerived, j as TgpuSlot, k as TgpuVertexLayout, l as WgslArray, D as Disarray, m as fn, n as bindGroupLayout, v as vertexLayout, o as fragmentFn, p as vertexFn, q as computeFn, r as privateVar, w as workgroupVar, s as constant, t as declare, u as sampler, x as comparisonSampler } from './tgpuConstant-
|
2
|
-
export { aq as BindLayoutEntry, ac as Eventual, ar as ExtractBindGroupInputFromLayout, aJ as INTERNAL_GlobalExt, a0 as IndexFlag, as as LayoutEntryToInput, ak as Render, al as Sampled, Y as Storage, Z as StorageFlag, aj as TextureProps, ad as TgpuAnyTextureView, a6 as TgpuBufferMutable, a7 as TgpuBufferReadonly, a8 as TgpuBufferUniform, aG as TgpuComputeFn, aH as TgpuComputeFnShell, $ as TgpuComputePipeline, am as TgpuConst, aI as TgpuDeclare, aB as TgpuFnShell, aE as TgpuFragmentFn, aF as TgpuFragmentFnShell, at as TgpuLayoutComparisonSampler, au as TgpuLayoutEntry, av as TgpuLayoutExternalTexture, aw as TgpuLayoutSampler, ax as TgpuLayoutStorage, ay as TgpuLayoutStorageTexture, az as TgpuLayoutTexture, aA as TgpuLayoutUniform, a9 as TgpuMutable, ae as TgpuMutableTexture, ap as TgpuQuerySet, aa as TgpuReadonly, af as TgpuReadonlyTexture, _ as TgpuRenderPipeline, ag as TgpuSampledTexture, ao as TgpuSampler, ah as TgpuTexture, ab as TgpuUniform, aC as TgpuVertexFn, aD as TgpuVertexFnShell, ai as TgpuWriteonlyTexture, a1 as Uniform, a2 as UniformFlag, a3 as ValidUsagesFor, V as ValidateBufferSchema, an as VariableScope, a4 as Vertex, a5 as VertexFlag, R as WithBinding, S as WithCompute, U as WithFragment, X as WithVertex, y as isBuffer, P as isBufferShorthand, F as isComparisonSampler, B as isDerived, H as isSampledTextureView, G as isSampler, E as isSlot, J as isStorageTextureView, K as isTexture, Q as isTgpuFn, L as isUsableAsRender, M as isUsableAsSampled, N as isUsableAsStorage, O as isUsableAsUniform, z as isUsableAsVertex } from './tgpuConstant-
|
1
|
+
import { T as TgpuBindGroupLayout, a as TgpuBindGroup, W as Wgsl, C as Configurable, b as TgpuBuffer, A as AnyData, c as TgpuVar, d as TgpuRoot, e as AnyWgslData, f as TgpuFn, g as TgpuBufferUsage, I as Infer, h as TgpuAccessor, i as TgpuDerived, j as TgpuSlot, k as TgpuVertexLayout, l as WgslArray, D as Disarray, m as fn, n as bindGroupLayout, v as vertexLayout, o as fragmentFn, p as vertexFn, q as computeFn, r as privateVar, w as workgroupVar, s as constant, t as declare, u as sampler, x as comparisonSampler } from './tgpuConstant-BSgcF4zi.js';
|
2
|
+
export { aq as BindLayoutEntry, ac as Eventual, ar as ExtractBindGroupInputFromLayout, aJ as INTERNAL_GlobalExt, a0 as IndexFlag, as as LayoutEntryToInput, ak as Render, al as Sampled, Y as Storage, Z as StorageFlag, aj as TextureProps, ad as TgpuAnyTextureView, a6 as TgpuBufferMutable, a7 as TgpuBufferReadonly, a8 as TgpuBufferUniform, aG as TgpuComputeFn, aH as TgpuComputeFnShell, $ as TgpuComputePipeline, am as TgpuConst, aI as TgpuDeclare, aB as TgpuFnShell, aE as TgpuFragmentFn, aF as TgpuFragmentFnShell, at as TgpuLayoutComparisonSampler, au as TgpuLayoutEntry, av as TgpuLayoutExternalTexture, aw as TgpuLayoutSampler, ax as TgpuLayoutStorage, ay as TgpuLayoutStorageTexture, az as TgpuLayoutTexture, aA as TgpuLayoutUniform, a9 as TgpuMutable, ae as TgpuMutableTexture, ap as TgpuQuerySet, aa as TgpuReadonly, af as TgpuReadonlyTexture, _ as TgpuRenderPipeline, ag as TgpuSampledTexture, ao as TgpuSampler, ah as TgpuTexture, ab as TgpuUniform, aC as TgpuVertexFn, aD as TgpuVertexFnShell, ai as TgpuWriteonlyTexture, a1 as Uniform, a2 as UniformFlag, a3 as ValidUsagesFor, V as ValidateBufferSchema, an as VariableScope, a4 as Vertex, a5 as VertexFlag, R as WithBinding, S as WithCompute, U as WithFragment, X as WithVertex, y as isBuffer, P as isBufferShorthand, F as isComparisonSampler, B as isDerived, H as isSampledTextureView, G as isSampler, E as isSlot, J as isStorageTextureView, K as isTexture, Q as isTgpuFn, L as isUsableAsRender, M as isUsableAsSampled, N as isUsableAsStorage, O as isUsableAsUniform, z as isUsableAsVertex } from './tgpuConstant-BSgcF4zi.js';
|
3
3
|
import 'tinyest';
|
4
4
|
|
5
5
|
/**
|