Replacing the final copyToTexture call with a tiny render pass, so that the postprocessing passes can be compute shaders working off of storage textures instead.

This commit is contained in:
Rezmason
2021-11-11 08:24:30 -08:00
parent 9813e76ffb
commit 9c861fd50b
5 changed files with 85 additions and 5 deletions

60
js/webgpu/endPass.js Normal file
View File

@@ -0,0 +1,60 @@
import { loadShader, makeBindGroup, makePassFBO, makePass } from "./utils.js";
const numVerticesPerQuad = 2 * 3;
export default (context, getInputs) => {
const { config, device, canvasFormat, canvasContext } = context;
const linearSampler = device.createSampler({
magFilter: "linear",
minFilter: "linear",
});
const renderPassConfig = {
colorAttachments: [
{
view: null,
loadValue: { r: 0, g: 0, b: 0, a: 1 },
storeOp: "store",
},
],
};
let renderPipeline;
const assets = [loadShader(device, "shaders/wgsl/endPass.wgsl")];
const ready = (async () => {
const [imageShader] = await Promise.all(assets);
renderPipeline = device.createRenderPipeline({
vertex: {
module: imageShader.module,
entryPoint: "vertMain",
},
fragment: {
module: imageShader.module,
entryPoint: "fragMain",
targets: [
{
format: canvasFormat,
},
],
},
});
})();
const execute = (encoder) => {
const inputs = getInputs();
const tex = inputs.primary;
const renderBindGroup = makeBindGroup(device, renderPipeline, 0, [linearSampler, tex.createView()]);
renderPassConfig.colorAttachments[0].view = canvasContext.getCurrentTexture().createView();
const renderPass = encoder.beginRenderPass(renderPassConfig);
renderPass.setPipeline(renderPipeline);
renderPass.setBindGroup(0, renderBindGroup);
renderPass.draw(numVerticesPerQuad, 1, 0, 0);
renderPass.endPass();
};
return makePass(null, ready, null, execute);
};

View File

@@ -7,6 +7,7 @@ import makePalettePass from "./palettePass.js";
import makeStripePass from "./stripePass.js";
import makeImagePass from "./imagePass.js";
import makeResurrectionPass from "./resurrectionPass.js";
import makeEndPass from "./endPass.js";
const effects = {
none: null,
@@ -51,7 +52,7 @@ export default async (canvas, config) => {
};
const effectName = config.effect in effects ? config.effect : "plain";
const pipeline = makePipeline(context, [makeRain, makeBloomPass, effects[effectName]]);
const pipeline = makePipeline(context, [makeRain, makeBloomPass, effects[effectName], makeEndPass]);
await Promise.all(pipeline.map((step) => step.ready));
@@ -74,7 +75,8 @@ export default async (canvas, config) => {
const encoder = device.createCommandEncoder();
pipeline.forEach((step) => step.execute(encoder));
encoder.copyTextureToTexture({ texture: pipeline[pipeline.length - 1].getOutputs().primary }, { texture: canvasContext.getCurrentTexture() }, canvasSize);
// Eventually, when WebGPU allows it, we'll remove the endPass and just copy from our pipeline's output to the canvas texture.
// encoder.copyTextureToTexture({ texture: pipeline[pipeline.length - 1].getOutputs().primary }, { texture: canvasContext.getCurrentTexture() }, canvasSize);
device.queue.submit([encoder.finish()]);
requestAnimationFrame(renderLoop);
};

View File

@@ -27,7 +27,7 @@ const loadTexture = async (device, url) => {
return texture;
};
const makePassFBO = (device, width, height, format = "rgba8unorm") =>
const makePassFBO = (device, width, height, format = "bgra8unorm") =>
device.createTexture({
size: [width, height, 1],
format,