UNPKG

@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
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