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