UNPKG

typegpu

Version:

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

155 lines (154 loc) 6.09 kB
import { schemaCallWrapper } from "../../data/schemaCallWrapper.js"; import { snip } from "../../data/snippet.js"; import { IllegalBufferAccessError } from "../../errors.js"; import { isInsideTgpuFn } from "../../execMode.js"; import {} from "../../extension.js"; import { getName, setName } from "../../shared/meta.js"; import { $getNameForward, $gpuValueOf, $internal, $repr, $soul } from "../../shared/symbols.js"; import { isUsableAsStorage, isUsableAsUniform } from "../../types.js"; import { makeDereferenceable } from "../../tgsl/makeDereferenceable.js"; import { makeResolvable } from "../../tgsl/makeResolvable.js"; import {} from "./buffer.js"; export function isBufferBinding(value) { return value instanceof TgpuBufferBindingImpl; } export function isUniformBinding(value) { return isBufferBinding(value) && value.resourceType === 'uniform'; } export function isMutableBinding(value) { return isBufferBinding(value) && value.resourceType === 'mutable'; } export function isReadonlyBinding(value) { return isBufferBinding(value) && value.resourceType === 'readonly'; } // TODO(#2666) - remove this /** @deprecated Use 'isBufferBinding' instead. */ export const isBufferShorthand = isBufferBinding; export class TgpuBufferBindingImpl { [$soul]; [$getNameForward]; resourceType; static { TgpuBufferBindingImpl.prototype[$internal] = true; makeDereferenceable(makeResolvable(TgpuBufferBindingImpl.prototype, { asString() { return `${this.resourceType}BufferBinding:${getName(this) ?? '<unnamed>'}`; }, resolve(ctx) { const dataType = this.buffer.dataType; const id = ctx.makeUniqueIdentifier(getName(this), 'global'); const { group, binding } = ctx.allocateFixedEntry(this.resourceType === 'uniform' ? { uniform: dataType } : { storage: dataType, access: this.resourceType }, this.buffer); return ctx.gen.declareGlobalVar({ group, binding, scope: this.resourceType, id, dataType, init: undefined, }); }, }), { codegenMode: { getBaseSnippet(trackingProxy) { return snip(trackingProxy, this.buffer.dataType, /* origin */ this.resourceType, /* possibleSideEffects */ false); }, }, simulateMode: { get(state) { if (!state.buffers.has(this.buffer)) { // Not initialized yet state.buffers.set(this.buffer, schemaCallWrapper(this.buffer.dataType, this.buffer.initial)); } return state.buffers.get(this.buffer); }, set(state, value) { state.buffers.set(this.buffer, value); }, }, normalMode: { get() { throw new IllegalBufferAccessError(isInsideTgpuFn() ? `Cannot access ${String(this.buffer)}. TypeGPU functions that depends on GPU resources need to be part of a compute dispatch, draw call or simulation` : '.$ is inaccessible during normal JS execution. Try `.read()`'); }, set() { throw new IllegalBufferAccessError(isInsideTgpuFn() ? `Cannot access ${String(this.buffer)}. TypeGPU functions that depends on GPU resources need to be part of a compute dispatch, draw call or simulation` : '.$ is inaccessible during normal JS execution. Try `.write()`'); }, }, }); } constructor(usage, buffer) { this.resourceType = usage; this[$getNameForward] = buffer; this[$soul] = { type: usage, buffer, label: undefined, }; } get buffer() { return this[$soul].buffer; } $name(label) { setName(this, label); return this; } write(data, options) { this.buffer.write(data, options); } /** @deprecated Use {@link patch} instead. */ writePartial(data) { this.buffer.writePartial(data); } patch(data) { this.buffer.patch(data); } read() { return this.buffer.read(); } } // -------------- // Constructors // -------------- const mutableUsageMap = new WeakMap(); export function mutable(buffer) { if (!isUsableAsStorage(buffer)) { throw new Error(`Cannot call as('mutable') on ${buffer}, as it is not allowed to be used as storage. To allow it, call .$usage('storage') when creating the buffer.`); } let usage = mutableUsageMap.get(buffer); if (!usage) { usage = new TgpuBufferBindingImpl('mutable', buffer); mutableUsageMap.set(buffer, usage); } return usage; } const readonlyUsageMap = new WeakMap(); export function readonly(buffer) { if (!isUsableAsStorage(buffer)) { throw new Error(`Cannot call as('readonly') on ${buffer}, as it is not allowed to be used as storage. To allow it, call .$usage('storage') when creating the buffer.`); } let usage = readonlyUsageMap.get(buffer); if (!usage) { usage = new TgpuBufferBindingImpl('readonly', buffer); readonlyUsageMap.set(buffer, usage); } return usage; } const uniformUsageMap = new WeakMap(); export function uniform(buffer) { if (!isUsableAsUniform(buffer)) { throw new Error(`Cannot call as('uniform') on ${buffer}, as it is not allowed to be used as a uniform. To allow it, call .$usage('uniform') when creating the buffer.`); } let usage = uniformUsageMap.get(buffer); if (!usage) { usage = new TgpuBufferBindingImpl('uniform', buffer); uniformUsageMap.set(buffer, usage); } return usage; }