typegpu
Version:
A thin layer between JS and WebGPU/WGSL that improves development experience and allows for faster iteration.
126 lines (125 loc) • 4.92 kB
JavaScript
import { snip } from "../../data/snippet.js";
import { getName, setName } from "../../shared/meta.js";
import { $gpuValueOf, $internal, $repr, $resolve, $soul } from "../../shared/symbols.js";
import { comparisonSampler as wgslComparisonSampler, sampler as wgslSampler, } from "../../data/sampler.js";
import { makeDereferenceable } from "../../tgsl/makeDereferenceable.js";
import { makeResolvable } from "../../tgsl/makeResolvable.js";
export function INTERNAL_createSampler(props, root) {
return new TgpuFixedSamplerImpl(wgslSampler(), props, root);
}
export function INTERNAL_createComparisonSampler(props, root) {
return new TgpuFixedSamplerImpl(wgslComparisonSampler(), props, root);
}
export function isSampler(resource) {
const maybe = resource;
return maybe?.resourceType === 'sampler' && !!maybe[$internal];
}
export function isComparisonSampler(resource) {
const maybe = resource;
return maybe?.resourceType === 'sampler-comparison' && !!maybe[$internal];
}
// --------------
// Implementation
// --------------
export class TgpuLaidOutSamplerImpl {
[$internal] = { materialize: undefined };
resourceType;
schema;
#membership;
static {
makeDereferenceable(makeResolvable(TgpuLaidOutSamplerImpl.prototype, {
asString() {
return `${this.resourceType}:${getName(this) ?? '<unnamed>'}`;
},
resolve(ctx) {
const id = ctx.makeUniqueIdentifier(getName(this), 'global');
const group = ctx.allocateLayoutEntry(this.#membership.layout);
ctx.addDeclaration(` var ${id}: ${ctx.resolve(this.schema).value};`, id);
return snip(id, this.schema, /* origin */ 'handle');
},
}), {
codegenMode: {
getBaseSnippet(trackingProxy) {
return snip(trackingProxy, this.schema, /* origin */ 'handle', false);
},
},
normalMode: {
get() {
throw new Error('Direct access to sampler values is possible only as part of a compute dispatch or draw call.');
},
},
});
}
constructor(schema, membership) {
this.schema = schema;
this.#membership = membership;
this.resourceType = (schema.type === 'sampler_comparison' ? 'sampler-comparison' : 'sampler');
setName(this, membership.key);
}
}
class TgpuFixedSamplerImpl {
[$internal];
[$soul];
resourceType;
schema;
#filtering;
static {
makeDereferenceable(makeResolvable(TgpuFixedSamplerImpl.prototype, {
asString() {
return `${this.resourceType}:${getName(this) ?? '<unnamed>'}`;
},
resolve(ctx) {
const id = ctx.makeUniqueIdentifier(getName(this), 'global');
const { group, binding } = ctx.allocateFixedEntry(this.schema.type === 'sampler_comparison'
? { sampler: 'comparison' }
: { sampler: this.#filtering ? 'filtering' : 'non-filtering' }, this);
ctx.addDeclaration(` var ${id}: ${ctx.resolve(this.schema).value};`, id);
return snip(id, this.schema, /* origin */ 'handle');
},
}), {
codegenMode: {
getBaseSnippet(trackingProxy) {
return snip(trackingProxy, this.schema, /* origin */ 'handle', false);
},
},
normalMode: {
get() {
throw new Error('Direct access to sampler values is possible only as part of a compute dispatch or draw call.');
},
},
});
}
constructor(schema, props, root) {
this.schema = schema;
this.resourceType = (schema.type === 'sampler_comparison' ? 'sampler-comparison' : 'sampler');
this[$soul] = {
type: this.resourceType,
device: root.device,
props,
raw: undefined,
label: undefined,
};
this[$internal] = {
root,
materialize: () => {
const soul = this[$soul];
if (!soul.raw) {
soul.raw = soul.device.createSampler({
...soul.props,
label: getName(this) ?? '<unnamed>',
});
}
return soul.raw;
},
};
// Based on https://www.w3.org/TR/webgpu/#sampler-creation
this.#filtering =
props.minFilter === 'linear' ||
props.magFilter === 'linear' ||
props.mipmapFilter === 'linear';
}
$name(label) {
setName(this, label);
return this;
}
}