Fixed some major bugs: the WebGPU cache should store loaded images and text, never GPU resource handles; renamed renderer "formulate" to "configure"; WebGPU renderer's configure function needs early returns after each major await, in case there's a new config; the render loops are now locally stored closures; renderers now have start and stop functions; fixed bugs in the REGL and WebGPU mirror passes; WebGPU bloom pass now enforces texture dimensions are greater than zero; the react component now stores the renderer type in a useRef and returns early from renderer init awaits to prevent multiple renderers from instantiating.

This commit is contained in:
Rezmason
2025-05-25 03:30:26 -07:00
parent 1da1feb356
commit b6570de106
15 changed files with 405 additions and 351 deletions

View File

@@ -24,7 +24,7 @@ const effects = {
export default class REGLRenderer extends Renderer {
#tick;
#renderFunc;
#regl;
#glMatrix;
@@ -43,26 +43,24 @@ export default class REGLRenderer extends Renderer {
});
}
async formulate(config) {
await super.formulate(config);
const canvas = this.canvas;
const cache = this.cache;
const regl = this.#regl;
const glMatrix = this.#glMatrix;
const dimensions = { width: 1, height: 1 };
async configure(config) {
await super.configure(config);
if (config.useCamera) {
await setupCamera();
}
const canvas = this.canvas;
const cache = this.cache;
const regl = this.#regl;
const glMatrix = this.#glMatrix;
const dimensions = { width: 1, height: 1 };
const cameraTex = regl.texture(cameraCanvas);
// All this takes place in a full screen quad.
const fullScreenQuad = makeFullScreenQuad(regl);
const effectName = config.effect in effects ? config.effect : "palette";
const context = { regl, cache, config, cameraTex, cameraAspectRatio, glMatrix };
const context = { regl, canvas, cache, config, cameraTex, cameraAspectRatio, glMatrix };
const pipeline = makePipeline(context, [makeRain, makeBloomPass, effects[effectName]]);
const screenUniforms = { tex: pipeline[pipeline.length - 1].outputs.primary };
@@ -75,17 +73,15 @@ export default class REGLRenderer extends Renderer {
const targetFrameTimeMilliseconds = 1000 / config.fps;
let last = NaN;
resetREGLTime: {
const reset = regl.frame((o) => {
o.time = 0;
o.tick = 0;
reset.cancel();
});
}
const reset = regl.frame((reglContext) => {
reglContext.tick = 0;
reset.cancel();
});
this.#renderFunc = (reglContext) => {
const tick = regl.frame(({ viewportWidth, viewportHeight }) => {
if (config.once) {
tick.cancel();
this.stop();
}
const now = regl.now() * 1000;
@@ -106,6 +102,7 @@ export default class REGLRenderer extends Renderer {
if (config.useCamera) {
cameraTex(cameraCanvas);
}
const {viewportWidth, viewportHeight} = reglContext;
if (dimensions.width !== viewportWidth || dimensions.height !== viewportHeight) {
dimensions.width = viewportWidth;
dimensions.height = viewportHeight;
@@ -119,20 +116,31 @@ export default class REGLRenderer extends Renderer {
}
drawToScreen();
});
};
const frame = this.#regl.frame(o => {
this.#renderFunc(o);
frame.cancel();
});
}
if (this.#tick != null) {
this.#tick.cancel();
stop() {
super.stop();
this.#renderFunc = null;
}
update(now) {
if (this.#renderFunc != null) {
const frame = this.#regl.frame(o => {
this.#renderFunc(o);
frame.cancel();
})
}
this.#tick = tick;
super.update(now);
}
destroy() {
if (this.destroyed) {
return;
}
this.#tick.cancel(); // stop RAF
if (this.destroyed) return;
this.#regl.destroy(); // releases all GPU resources & event listeners
super.destroy();
}