typegpu
Version:
A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.
94 lines (93 loc) • 3.88 kB
JavaScript
import { stitch } from "../../core/resolve/stitch.js";
import { shaderStageSlot } from "../../core/slot/internalSlots.js";
import { arrayOf } from "../../data/array.js";
import { atomic } from "../../data/atomic.js";
import { UnknownData, unptr } from "../../data/dataTypes.js";
import { u32 } from "../../data/numeric.js";
import { snip } from "../../data/snippet.js";
import { struct } from "../../data/struct.js";
import { Void, } from "../../data/wgslTypes.js";
import { invariant } from "../../errors.js";
import { $internal } from "../../shared/symbols.js";
import { logger } from "../../tgpuLogger.js";
import { convertToCommonType } from "../conversion.js";
import { concretizeSnippet } from "../generationHelpers.js";
import { createLoggingFunction } from "./serializers.js";
import {} from "./types.js";
const defaultOptions = {
logCountLimit: 64,
logSizeLimit: 252,
messagePrefix: ' GPU ',
};
const fallbackSnippet = snip('/* console.log() */', Void, /* origin */ 'runtime');
export class LogGeneratorNullImpl {
get logResources() {
return undefined;
}
generateLog() {
logger.warn('fallback', "'console.log' is only supported when resolving pipelines.");
return fallbackSnippet;
}
}
export class LogGeneratorImpl {
#options;
#logIdToMeta;
#firstUnusedId = 1;
#indexBuffer;
#dataBuffer;
constructor(root) {
this.#options = { ...defaultOptions, ...root[$internal].logOptions };
this.#logIdToMeta = new Map();
const SerializedLogData = struct({
id: u32,
serializedData: arrayOf(u32, Math.ceil(this.#options.logSizeLimit / 4)),
}).$name('SerializedLogData');
this.#dataBuffer = root
.createMutable(arrayOf(SerializedLogData, this.#options.logCountLimit))
.$name('dataBuffer');
this.#indexBuffer = root.createMutable(atomic(u32)).$name('indexBuffer');
}
/**
* Generates all necessary resources for serializing arguments for logging purposes.
*
* @param ctx Resolution context.
* @param args Argument snippets. Snippets of UnknownType will be treated as string literals.
* @returns A snippet containing the call to the logging function.
*/
generateLog(ctx, op, args) {
if (shaderStageSlot.$ === 'vertex') {
logger.warn('suspicious', `'console' operations are not supported in vertex shaders.`);
return fallbackSnippet;
}
const id = this.#firstUnusedId++;
const concreteArgsWithStrings = args
.map((arg) => {
if (arg.dataType === UnknownData) {
return arg;
}
const converted = convertToCommonType(ctx, [arg], [unptr(arg.dataType)])?.[0];
invariant(converted, `Internal error. Expected type ${arg.dataType} to be convertible to ${unptr(arg.dataType)}`);
return converted;
})
.map(concretizeSnippet);
const concreteArgs = concreteArgsWithStrings.filter((arg) => arg.dataType !== UnknownData);
const logFn = createLoggingFunction(id, concreteArgs.map((e) => e.dataType), this.#dataBuffer, this.#indexBuffer, this.#options);
const functionSnippet = snip(stitch `${ctx.resolve(logFn).value}(${concreteArgs})`, Void,
/* origin */ 'runtime');
this.#logIdToMeta.set(id, {
op,
argTypes: concreteArgsWithStrings.map((e) => e?.dataType === UnknownData ? e?.value : e?.dataType),
});
return functionSnippet;
}
get logResources() {
return this.#firstUnusedId === 1
? undefined
: {
dataBuffer: this.#dataBuffer,
indexBuffer: this.#indexBuffer,
options: this.#options,
logIdToMeta: this.#logIdToMeta,
};
}
}