mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-14 12:29:30 -07:00
Testing hot-swapping renderers, which requires destroying and rebuilding the canvas after all. Fixed a few other related bugs and moved the imports into "bundle-contents.js".
This commit is contained in:
64
js/Matrix.js
64
js/Matrix.js
@@ -1,13 +1,6 @@
|
||||
import inclusions from "./inclusions";
|
||||
|
||||
import React, { useEffect, useState, useRef, memo } from "react";
|
||||
import * as reglRenderer from "./regl/main";
|
||||
import * as webgpuRenderer from "./webgpu/main";
|
||||
import makeConfig from "./utils/config";
|
||||
|
||||
globalThis.inclusions = inclusions;
|
||||
console.log(webgpuRenderer.init, webgpuRenderer.formulate, webgpuRenderer.destroy);
|
||||
|
||||
/**
|
||||
* @typedef {object} Colour
|
||||
* @property {"hsl"|"rgb"} space
|
||||
@@ -115,33 +108,64 @@ export const Matrix = memo((props) => {
|
||||
const { style, className, ...rest } = props;
|
||||
const elProps = { style, className };
|
||||
const matrix = useRef(null);
|
||||
const [rain, setRain] = useState(null);
|
||||
const [rCanvas, setCanvas] = useState(null);
|
||||
const [rRenderer, setRenderer] = useState(null);
|
||||
const [rRain, setRain] = useState(null);
|
||||
|
||||
const supportsWebGPU = () => {
|
||||
return (
|
||||
window.GPUQueue != null &&
|
||||
navigator.gpu != null &&
|
||||
navigator.gpu.getPreferredCanvasFormat != null
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const useWebGPU = supportsWebGPU() && ["webgpu"].includes(rest.renderer?.toLowerCase());
|
||||
const isWebGPU = rRenderer?.type === "webgpu";
|
||||
|
||||
if (rRenderer != null && useWebGPU === isWebGPU) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rCanvas != null) {
|
||||
matrix.current.removeChild(rCanvas);
|
||||
setCanvas(null);
|
||||
}
|
||||
|
||||
if (rRain != null) {
|
||||
rRenderer?.destroy(rRain);
|
||||
setRain(null);
|
||||
}
|
||||
|
||||
if (rRenderer != null) {
|
||||
setRenderer(null);
|
||||
}
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
matrix.current.appendChild(canvas);
|
||||
canvas.style.width = "100%";
|
||||
canvas.style.height = "100%";
|
||||
const init = async () => {
|
||||
setRain(await reglRenderer.init(canvas));
|
||||
};
|
||||
init();
|
||||
matrix.current.appendChild(canvas);
|
||||
setCanvas(canvas);
|
||||
|
||||
return () => {
|
||||
reglRenderer.destroy(rain);
|
||||
setRain(null);
|
||||
const loadRain = async () => {
|
||||
const renderer = await import(`./${useWebGPU ? "webgpu" : "regl"}/main.js`);
|
||||
setRenderer(renderer);
|
||||
const rain = await renderer.init(canvas);
|
||||
setRain(rain);
|
||||
};
|
||||
}, []);
|
||||
loadRain();
|
||||
}, [props.renderer]);
|
||||
|
||||
useEffect(() => {
|
||||
if (rain == null) {
|
||||
if (rRain == null || rRain.destroyed) {
|
||||
return;
|
||||
}
|
||||
const refresh = async () => {
|
||||
await reglRenderer.formulate(rain, makeConfig({ ...rest }));
|
||||
await rRenderer.formulate(rRain, makeConfig({ ...rest }));
|
||||
};
|
||||
refresh();
|
||||
}, [props, rain]);
|
||||
}, [props, rRain]);
|
||||
|
||||
return <div ref={matrix} {...elProps}></div>;
|
||||
});
|
||||
|
||||
8
js/bundle-contents.js
Normal file
8
js/bundle-contents.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Matrix } from "./Matrix";
|
||||
import inclusions from "./inclusions";
|
||||
import * as reglRenderer from "./regl/main";
|
||||
import * as webgpuRenderer from "./webgpu/main";
|
||||
globalThis.inclusions = inclusions;
|
||||
globalThis.reglRenderer = reglRenderer;
|
||||
globalThis.webgpuRenderer = webgpuRenderer;
|
||||
globalThis.Matrix = Matrix;
|
||||
10
js/index.js
10
js/index.js
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { Matrix } from "./Matrix";
|
||||
//import { Matrix } from "react-matrix-rain";
|
||||
|
||||
const root = createRoot(document.getElementById("root"));
|
||||
let idx = 1;
|
||||
@@ -23,6 +22,7 @@ const versions = [
|
||||
const App = () => {
|
||||
const [version, setVersion] = React.useState(versions[0]);
|
||||
const [numColumns, setNumColumns] = React.useState(10);
|
||||
const [rendererType, setRendererType] = React.useState(null);
|
||||
const onButtonClick = () => {
|
||||
setVersion((s) => {
|
||||
const newVersion = versions[idx];
|
||||
@@ -36,16 +36,20 @@ const App = () => {
|
||||
return newColumns;
|
||||
});
|
||||
};
|
||||
const onRendererButtonClick = () => {
|
||||
setRendererType(() => (rendererType === "webgpu" ? "regl" : "webgpu"));
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Rain</h1>
|
||||
<button onClick={onButtonClick}>Change</button>
|
||||
{/* <button onClick={newNum}>change number</button> */}
|
||||
<button onClick={onButtonClick}>Change properties</button>
|
||||
<button onClick={onRendererButtonClick}>Renderer: {rendererType ?? "default (regl)"}</button>
|
||||
<Matrix
|
||||
style={{ width: "80vw", height: "45vh" }}
|
||||
version={version}
|
||||
numColumns={numColumns}
|
||||
renderer={rendererType}
|
||||
density={2.0}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -69,6 +69,9 @@ export const init = async (canvas) => {
|
||||
};
|
||||
|
||||
export const formulate = async (rain, config) => {
|
||||
if (rain.destroyed) {
|
||||
throw new Error("Cannot formulate a destroyed rain instance.");
|
||||
}
|
||||
const { resize, canvas, cache, regl } = rain;
|
||||
rain.resolution = config.resolution;
|
||||
resize();
|
||||
@@ -150,10 +153,17 @@ export const formulate = async (rain, config) => {
|
||||
rain.tick = tick;
|
||||
};
|
||||
|
||||
export const destroy = ({ regl, cache, resize, doubleClick, tick, canvas }) => {
|
||||
export const destroy = (rain) => {
|
||||
if (rain.destroyed) {
|
||||
return;
|
||||
}
|
||||
const { regl, cache, resize, doubleClick, tick, canvas } = rain;
|
||||
window.removeEventListener("resize", resize);
|
||||
window.removeEventListener("dblclick", doubleClick);
|
||||
cache.clear();
|
||||
tick.cancel(); // stop RAF
|
||||
regl.destroy(); // release all GPU resources & event listeners
|
||||
rain.destroyed = true;
|
||||
};
|
||||
|
||||
export const type = "regl";
|
||||
|
||||
@@ -75,6 +75,9 @@ export const init = async (canvas) => {
|
||||
};
|
||||
|
||||
export const formulate = async (rain, config) => {
|
||||
if (rain.destroyed) {
|
||||
throw new Error("Cannot formulate a destroyed rain instance.");
|
||||
}
|
||||
const { resize, canvas, cache, canvasContext, adapter, device } = rain;
|
||||
rain.resolution = config.resolution;
|
||||
resize();
|
||||
@@ -197,10 +200,18 @@ export const formulate = async (rain, config) => {
|
||||
rain.renderLoop = renderLoop;
|
||||
};
|
||||
|
||||
export const destroy = ({ device, resize, doubleClick, cache, canvas }) => {
|
||||
export const destroy = (rain) => {
|
||||
if (rain.destroyed) {
|
||||
return;
|
||||
}
|
||||
const { device, resize, doubleClick, cache, canvas, renderLoop } = rain;
|
||||
window.removeEventListener("resize", resize);
|
||||
window.removeEventListener("dblclick", doubleClick);
|
||||
cache.clear();
|
||||
tick.cancel(); // stop RAF
|
||||
cancelAnimationFrame(renderLoop); // stop RAF
|
||||
// TODO: destroy WebGPU resources
|
||||
device.destroy();
|
||||
rain.destroyed = true;
|
||||
};
|
||||
|
||||
export const type = "webgpu";
|
||||
|
||||
@@ -29,7 +29,6 @@ const makeConfigBuffer = (device, configUniforms, config, density, gridSize, gly
|
||||
};
|
||||
// console.table(configData);
|
||||
|
||||
console.log(configUniforms, configData);
|
||||
return makeUniformBuffer(device, configUniforms, configData);
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { string } from "rollup-plugin-string";
|
||||
import image from "@rollup/plugin-image";
|
||||
|
||||
export default {
|
||||
input: "js/Matrix.js",
|
||||
input: "js/bundle-contents.js",
|
||||
external: ["react", "react-dom"], // keep them out of your bundle
|
||||
plugins: [
|
||||
peerDepsExternal(), // auto-exclude peerDeps
|
||||
|
||||
Reference in New Issue
Block a user