UNPKG

typegpu

Version:

A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.

120 lines (119 loc) 4.57 kB
import { snip } from "../../data/snippet.js"; import { Void } from "../../data/wgslTypes.js"; import { getName } from "../../internal.js"; import { resolve as resolveImpl } from "../../resolutionCtx.js"; import { $internal, $resolve, $soul } from "../../shared/symbols.js"; import { isBindGroupLayout } from "../../tgpuBindGroupLayout.js"; import { logger } from "../../tgpuLogger.js"; import { isBuffer } from "../../types.js"; import { isBufferBinding } from "../buffer/bufferBinding.js"; import { isPipeline } from "../pipeline/typeGuards.js"; import { isComparisonSampler, isSampler } from "../sampler/sampler.js"; import { isTexture, isTextureView } from "../texture/texture.js"; import { replaceExternalsInWgsl } from "./externals.js"; import { namespace } from "./namespace.js"; export function resolveWithContext(arg0, options) { if (Array.isArray(arg0)) { return resolveFromArray(arg0, options); } return resolveFromTemplate(arg0); } export function resolve(arg, options) { if (Array.isArray(arg)) { return resolveWithContext(arg, options).code; } return resolveWithContext(arg).code; } function resolveFromTemplate(options) { const { template, externals, unstable_shaderGenerator: shaderGenerator, names = 'strict', unstable_minify, config, enableExtensions, } = options; if (!template) { logger.warn('deprecated', "Calling resolve with an empty template is deprecated and will soon return an empty string. Consider using the 'tgpu.resolve(resolvableArray, options)' API instead."); } const resolutionObj = { [$internal]: true, [$resolve](ctx) { return snip(replaceExternalsInWgsl(ctx, externals, template ?? ''), Void, /* origin */ 'runtime'); }, toString: () => '<root>', }; const maybeRoot = tryFindRoot(Object.values(externals)); return resolveImpl(resolutionObj, { namespace: typeof names === 'string' ? namespace({ names }) : names, minify: unstable_minify ?? maybeRoot?.minify ?? false, enableExtensions, shaderGenerator, config, root: maybeRoot, }); } function resolveFromArray(items, options) { const { unstable_shaderGenerator: shaderGenerator, names = 'strict', unstable_minify, config, enableExtensions, } = options ?? {}; const resolutionObj = { [$internal]: true, [$resolve](ctx) { for (const item of items) { // Support for: tgpu.resolve([layout]) if (isBindGroupLayout(item)) { for (const binding of Object.values(item[$internal])) { ctx.resolve(binding); } } else { ctx.resolve(item); } } return snip('', Void, 'runtime'); }, toString: () => '<root>', }; const maybeRoot = tryFindRoot(items); return resolveImpl(resolutionObj, { namespace: typeof names === 'string' ? namespace({ names }) : names, minify: unstable_minify ?? maybeRoot?.minify ?? false, enableExtensions, shaderGenerator, config, root: maybeRoot, }); } /** * Attempts to locate a root in a list of items that may hold it. * Does not check recursively. * Throws an error if multiple roots are found. */ function tryFindRoot(items) { const buckets = new Map(); for (const item of items) { const root = extractRoot(item); if (root) { const bucket = buckets.get(root) ?? new Set(); buckets.set(root, bucket); bucket.add(item); } } if (buckets.size > 1) { const bucketsString = [...buckets.values()] .map((bucket, i) => `root ${i + 1}: ${[...bucket].map((item) => getName(item) ?? '<unnamed>').join(', ')}`) .join('; '); throw new Error(`Found resources originating from different roots in a single resolve (${bucketsString}).`); } return [...buckets.keys()][0]; } function extractRoot(item) { if (isPipeline(item) || isBuffer(item) || isTexture(item) || isSampler(item) || isComparisonSampler(item)) { return item[$internal].root; } if (isBufferBinding(item)) { return extractRoot(item.buffer); } if (isTextureView(item)) { // laid out texture view should never appear here, but still passes this type guard return extractRoot(item?.[$soul]?.texture); } return undefined; }