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
JavaScript
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;
}