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