@motion-core/motion-gpu
Version:
Framework-agnostic WebGPU runtime for fullscreen WGSL shaders with explicit Svelte, React, and Vue adapter entrypoints.
147 lines (146 loc) • 4.31 kB
JavaScript
import { resolveTextureSamplingLayout } from "../core/textures.js";
//#region src/lib/passes/FullscreenPass.ts
/**
* Shared base for fullscreen texture sampling passes.
*/
var FullscreenPass = class {
enabled;
needsSwap;
input;
output;
clear;
clearColor;
preserve;
filter;
device = null;
samplerBySamplingLayout = /* @__PURE__ */ new Map();
bindGroupLayoutBySamplingLayout = /* @__PURE__ */ new Map();
shaderModule = null;
pipelineByFormat = /* @__PURE__ */ new Map();
bindGroupByView = /* @__PURE__ */ new WeakMap();
constructor(options = {}) {
this.enabled = options.enabled ?? true;
this.needsSwap = options.needsSwap ?? true;
this.input = options.input ?? "source";
this.output = options.output ?? (this.needsSwap ? "target" : "source");
this.clear = options.clear ?? false;
this.clearColor = options.clearColor ?? [
0,
0,
0,
1
];
this.preserve = options.preserve ?? true;
this.filter = options.filter ?? "linear";
}
invalidateFullscreenCache() {
this.shaderModule = null;
this.pipelineByFormat.clear();
this.samplerBySamplingLayout.clear();
this.bindGroupLayoutBySamplingLayout.clear();
this.bindGroupByView = /* @__PURE__ */ new WeakMap();
}
ensureResources(device, inputFormat, outputFormat) {
if (this.device !== device) {
this.device = device;
this.invalidateFullscreenCache();
}
const samplingLayout = resolveTextureSamplingLayout({
format: inputFormat,
filter: this.filter,
deviceFeatures: device.features
});
const samplingLayoutKey = [
inputFormat,
samplingLayout.sampleType,
samplingLayout.samplerType,
samplingLayout.effectiveFilter
].join("|");
let sampler = this.samplerBySamplingLayout.get(samplingLayoutKey);
if (!sampler) {
sampler = device.createSampler({
magFilter: samplingLayout.effectiveFilter,
minFilter: samplingLayout.effectiveFilter,
addressModeU: "clamp-to-edge",
addressModeV: "clamp-to-edge"
});
this.samplerBySamplingLayout.set(samplingLayoutKey, sampler);
}
let bindGroupLayout = this.bindGroupLayoutBySamplingLayout.get(samplingLayoutKey);
if (!bindGroupLayout) {
bindGroupLayout = device.createBindGroupLayout({ entries: [{
binding: 0,
visibility: GPUShaderStage.FRAGMENT,
sampler: { type: samplingLayout.samplerType }
}, {
binding: 1,
visibility: GPUShaderStage.FRAGMENT,
texture: {
sampleType: samplingLayout.sampleType,
viewDimension: "2d",
multisampled: false
}
}] });
this.bindGroupLayoutBySamplingLayout.set(samplingLayoutKey, bindGroupLayout);
}
if (!this.shaderModule) this.shaderModule = device.createShaderModule({ code: this.getProgram() });
const pipelineKey = `${outputFormat}|${samplingLayoutKey}`;
let pipeline = this.pipelineByFormat.get(pipelineKey);
if (!pipeline) {
const pipelineLayout = device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout] });
pipeline = device.createRenderPipeline({
layout: pipelineLayout,
vertex: {
module: this.shaderModule,
entryPoint: this.getVertexEntryPoint()
},
fragment: {
module: this.shaderModule,
entryPoint: this.getFragmentEntryPoint(),
targets: [{ format: outputFormat }]
},
primitive: { topology: "triangle-list" }
});
this.pipelineByFormat.set(pipelineKey, pipeline);
}
return {
sampler,
bindGroupLayout,
pipeline
};
}
setSize(width, height) {}
renderFullscreen(context) {
const { sampler, bindGroupLayout, pipeline } = this.ensureResources(context.device, context.input.format, context.output.format);
const inputView = context.input.view;
let bindGroup = this.bindGroupByView.get(inputView);
if (!bindGroup) {
bindGroup = context.device.createBindGroup({
layout: bindGroupLayout,
entries: [{
binding: 0,
resource: sampler
}, {
binding: 1,
resource: inputView
}]
});
this.bindGroupByView.set(inputView, bindGroup);
}
const pass = context.beginRenderPass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.draw(3);
pass.end();
}
render(context) {
this.renderFullscreen(context);
}
dispose() {
this.device = null;
this.invalidateFullscreenCache();
}
};
//#endregion
export { FullscreenPass };
//# sourceMappingURL=FullscreenPass.js.map