Unifying the interfaces of regl and webgpu makePass and makePipeline.

This commit is contained in:
Rezmason
2021-11-09 09:17:01 -08:00
parent 6586badf42
commit 87c2093281
14 changed files with 55 additions and 68 deletions

View File

@@ -9,7 +9,7 @@ const levelStrengths = Array(pyramidHeight)
.map((_, index) => Math.pow(index / (pyramidHeight * 2) + 0.5, 1 / 3).toPrecision(5)) .map((_, index) => Math.pow(index / (pyramidHeight * 2) + 0.5, 1 / 3).toPrecision(5))
.reverse(); .reverse();
export default (regl, config, inputs) => { export default ({ regl, config }, inputs) => {
const { bloomStrength, bloomSize, highPassThreshold } = config; const { bloomStrength, bloomSize, highPassThreshold } = config;
const enabled = bloomSize > 0 && bloomStrength > 0; const enabled = bloomSize > 0 && bloomStrength > 0;
@@ -80,6 +80,14 @@ export default (regl, config, inputs) => {
primary: inputs.primary, primary: inputs.primary,
bloom: output, bloom: output,
}, },
Promise.all([highPassFrag.loaded, blurFrag.loaded]),
(w, h) => {
// The blur pyramids can be lower resolution than the screen.
resizePyramid(highPassPyramid, w, h, bloomSize);
resizePyramid(hBlurPyramid, w, h, bloomSize);
resizePyramid(vBlurPyramid, w, h, bloomSize);
output.resize(w, h);
},
() => { () => {
for (let i = 0; i < pyramidHeight; i++) { for (let i = 0; i < pyramidHeight; i++) {
const highPassFBO = highPassPyramid[i]; const highPassFBO = highPassPyramid[i];
@@ -91,14 +99,6 @@ export default (regl, config, inputs) => {
} }
sumPyramid(); sumPyramid();
}, }
(w, h) => {
// The blur pyramids can be lower resolution than the screen.
resizePyramid(highPassPyramid, w, h, bloomSize);
resizePyramid(hBlurPyramid, w, h, bloomSize);
resizePyramid(vBlurPyramid, w, h, bloomSize);
output.resize(w, h);
},
[highPassFrag.loaded, blurFrag.loaded]
); );
}; };

View File

@@ -4,7 +4,7 @@ import { loadImage, loadText, makePassFBO, makePass } from "./utils.js";
const defaultBGURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flammarion_Colored.jpg/917px-Flammarion_Colored.jpg"; const defaultBGURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Flammarion_Colored.jpg/917px-Flammarion_Colored.jpg";
export default (regl, config, inputs) => { export default ({ regl, config }, inputs) => {
const output = makePassFBO(regl, config.useHalfFloat); const output = makePassFBO(regl, config.useHalfFloat);
const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL; const bgURL = "bgURL" in config ? config.bgURL : defaultBGURL;
const background = loadImage(regl, bgURL); const background = loadImage(regl, bgURL);
@@ -22,8 +22,8 @@ export default (regl, config, inputs) => {
{ {
primary: output, primary: output,
}, },
() => render({ frag: imagePassFrag.text() }), Promise.all([background.loaded, imagePassFrag.loaded]),
null, (w, h) => output.resize(w, h),
[background.loaded, imagePassFrag.loaded] () => render({ frag: imagePassFrag.text() })
); );
}; };

View File

@@ -40,7 +40,7 @@ export default async (canvas, config) => {
// All this takes place in a full screen quad. // All this takes place in a full screen quad.
const fullScreenQuad = makeFullScreenQuad(regl); const fullScreenQuad = makeFullScreenQuad(regl);
const effectName = config.effect in effects ? config.effect : "plain"; const effectName = config.effect in effects ? config.effect : "plain";
const pipeline = makePipeline([makeRain, makeBloomPass, effects[effectName]], (p) => p.outputs, regl, config); const pipeline = makePipeline({ regl, config }, [makeRain, makeBloomPass, effects[effectName]]);
const screenUniforms = { tex: pipeline[pipeline.length - 1].outputs.primary }; const screenUniforms = { tex: pipeline[pipeline.length - 1].outputs.primary };
const drawToScreen = regl({ uniforms: screenUniforms }); const drawToScreen = regl({ uniforms: screenUniforms });
await Promise.all(pipeline.map((step) => step.ready)); await Promise.all(pipeline.map((step) => step.ready));
@@ -50,12 +50,12 @@ export default async (canvas, config) => {
dimensions.width = viewportWidth; dimensions.width = viewportWidth;
dimensions.height = viewportHeight; dimensions.height = viewportHeight;
for (const step of pipeline) { for (const step of pipeline) {
step.resize(viewportWidth, viewportHeight); step.setSize(viewportWidth, viewportHeight);
} }
} }
fullScreenQuad(() => { fullScreenQuad(() => {
for (const step of pipeline) { for (const step of pipeline) {
step.render(); step.execute();
} }
drawToScreen(); drawToScreen();
}); });

View File

@@ -62,7 +62,7 @@ const makePalette = (regl, entries) => {
// won't persist across subsequent frames. This is a safe trick // won't persist across subsequent frames. This is a safe trick
// in screen space. // in screen space.
export default (regl, config, inputs) => { export default ({ regl, config }, inputs) => {
const output = makePassFBO(regl, config.useHalfFloat); const output = makePassFBO(regl, config.useHalfFloat);
const palette = makePalette(regl, config.paletteEntries); const palette = makePalette(regl, config.paletteEntries);
const { backgroundColor } = config; const { backgroundColor } = config;
@@ -86,8 +86,8 @@ export default (regl, config, inputs) => {
{ {
primary: output, primary: output,
}, },
() => render({ frag: palettePassFrag.text() }), palettePassFrag.loaded,
null, (w, h) => output.resize(w, h),
palettePassFrag.loaded () => render({ frag: palettePassFrag.text() })
); );
}; };

View File

@@ -19,7 +19,7 @@ const blVert = [1, 0];
const brVert = [1, 1]; const brVert = [1, 1];
const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert]; const quadVertices = [tlVert, trVert, brVert, tlVert, brVert, blVert];
export default (regl, config) => { export default ({ regl, config }) => {
// The volumetric mode multiplies the number of columns // The volumetric mode multiplies the number of columns
// to reach the desired density, and then overlaps them // to reach the desired density, and then overlaps them
const volumetric = config.volumetric; const volumetric = config.volumetric;
@@ -170,15 +170,7 @@ export default (regl, config) => {
{ {
primary: output, primary: output,
}, },
() => { Promise.all([msdf.loaded, rainPassCompute.loaded, rainPassVert.loaded, rainPassFrag.loaded]),
compute({ frag: rainPassCompute.text() });
regl.clear({
depth: 1,
color: [0, 0, 0, 1],
framebuffer: output,
});
render({ camera, transform, screenSize, vert: rainPassVert.text(), frag: rainPassFrag.text() });
},
(w, h) => { (w, h) => {
output.resize(w, h); output.resize(w, h);
const aspectRatio = w / h; const aspectRatio = w / h;
@@ -193,6 +185,14 @@ export default (regl, config) => {
} }
[screenSize[0], screenSize[1]] = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1]; [screenSize[0], screenSize[1]] = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
}, },
[msdf.loaded, rainPassCompute.loaded, rainPassVert.loaded, rainPassFrag.loaded] () => {
compute({ frag: rainPassCompute.text() });
regl.clear({
depth: 1,
color: [0, 0, 0, 1],
framebuffer: output,
});
render({ camera, transform, screenSize, vert: rainPassVert.text(), frag: rainPassFrag.text() });
}
); );
}; };

View File

@@ -8,7 +8,7 @@ import { loadText, make1DTexture, makePassFBO, makePass } from "./utils.js";
// Downward-flowing glyphs should be tinted slightly blue on top and golden on the bottom // Downward-flowing glyphs should be tinted slightly blue on top and golden on the bottom
// Cheat a lens blur, interpolating between the texture and bloom at the edges // Cheat a lens blur, interpolating between the texture and bloom at the edges
export default (regl, config, inputs) => { export default ({ regl, config }, inputs) => {
const output = makePassFBO(regl, config.useHalfFloat); const output = makePassFBO(regl, config.useHalfFloat);
const { backgroundColor } = config; const { backgroundColor } = config;
const resurrectionPassFrag = loadText("shaders/glsl/resurrectionPass.frag.glsl"); const resurrectionPassFrag = loadText("shaders/glsl/resurrectionPass.frag.glsl");
@@ -29,8 +29,8 @@ export default (regl, config, inputs) => {
{ {
primary: output, primary: output,
}, },
() => render({ frag: resurrectionPassFrag.text() }), resurrectionPassFrag.loaded,
null, (w, h) => output.resize(w, h),
resurrectionPassFrag.loaded () => render({ frag: resurrectionPassFrag.text() })
); );
}; };

View File

@@ -28,7 +28,7 @@ const prideStripeColors = [
[0.8, 0, 1], [0.8, 0, 1],
].flat(); ].flat();
export default (regl, config, inputs) => { export default ({ regl, config }, inputs) => {
const output = makePassFBO(regl, config.useHalfFloat); const output = makePassFBO(regl, config.useHalfFloat);
const { backgroundColor } = config; const { backgroundColor } = config;
@@ -61,8 +61,8 @@ export default (regl, config, inputs) => {
{ {
primary: output, primary: output,
}, },
() => render({ frag: stripePassFrag.text() }), stripePassFrag.loaded,
null, (w, h) => output.resize(w, h),
stripePassFrag.loaded () => render({ frag: stripePassFrag.text() })
); );
}; };

View File

@@ -129,28 +129,15 @@ const make1DTexture = (regl, data) =>
min: "linear", min: "linear",
}); });
const makePass = (outputs, render, resize, ready) => { const makePass = (outputs, ready, setSize, execute) => ({
if (render == null) { outputs: outputs ?? {},
render = () => {}; ready: ready ?? Promise.resolve(),
} setSize: setSize ?? (() => {}),
if (resize == null) { execute: execute ?? (() => {}),
resize = (w, h) => Object.values(outputs).forEach((output) => output.resize(w, h)); });
}
if (ready == null) {
ready = Promise.resolve();
} else if (ready instanceof Array) {
ready = Promise.all(ready);
}
return {
outputs,
render,
resize,
ready,
};
};
const makePipeline = (steps, getInputs, ...params) => const makePipeline = (context, steps) =>
steps.filter((f) => f != null).reduce((pipeline, f, i) => [...pipeline, f(...params, i == 0 ? null : getInputs(pipeline[i - 1]))], []); steps.filter((f) => f != null).reduce((pipeline, f, i) => [...pipeline, f(context, i == 0 ? null : pipeline[i - 1].outputs)], []);
export { export {
makePassTexture, makePassTexture,

View File

@@ -76,5 +76,5 @@ export default (context, getInputs) => {
renderPass.endPass(); renderPass.endPass();
}; };
return makePass(ready, setSize, getOutputs, execute); return makePass(getOutputs, ready, setSize, execute);
}; };

View File

@@ -161,5 +161,5 @@ export default (context, getInputs) => {
renderPass.endPass(); renderPass.endPass();
}; };
return makePass(ready, setSize, getOutputs, execute); return makePass(getOutputs, ready, setSize, execute);
}; };

View File

@@ -193,5 +193,5 @@ export default (context, getInputs) => {
renderPass.endPass(); renderPass.endPass();
}; };
return makePass(ready, setSize, getOutputs, execute); return makePass(getOutputs, ready, setSize, execute);
}; };

View File

@@ -83,5 +83,5 @@ export default (context, getInputs) => {
renderPass.endPass(); renderPass.endPass();
}; };
return makePass(ready, setSize, getOutputs, execute); return makePass(getOutputs, ready, setSize, execute);
}; };

View File

@@ -125,5 +125,5 @@ export default (context, getInputs) => {
renderPass.endPass(); renderPass.endPass();
}; };
return makePass(ready, setSize, getOutputs, execute); return makePass(getOutputs, ready, setSize, execute);
}; };

View File

@@ -80,10 +80,10 @@ const makeBindGroup = (device, pipeline, index, entries) =>
})), })),
}); });
const makePass = (ready, setSize, getOutputs, execute) => ({ const makePass = (getOutputs, ready, setSize, execute) => ({
getOutputs: getOutputs ?? (() => ({})),
ready: ready ?? Promise.resolve(), ready: ready ?? Promise.resolve(),
setSize: setSize ?? (() => {}), setSize: setSize ?? (() => {}),
getOutputs: getOutputs ?? (() => ({})),
execute: execute ?? (() => {}), execute: execute ?? (() => {}),
}); });