I’m trying to optimize an interactive scene I’ve been working using R3F. In this scene, there’s a long list of blocks, and the user can interact with those blocks. After a few dozen blocks the performance of the interactions starts to deteriorate, so I’m trying to fix this. The scene looks like this:
My original implementation was pretty naive. I just grab a list of blocks and render them. In a very simplified version, it looks something like this. Although it has a lot more happening:
const Block = () => {
return (
<Box ...args>
<MeshTransmissionMaterial ...args />
<Edges ...args />
</Box>
);
}
// Canvas.tsx
// ...
return <Canvas>{blocks.map(block => <Block block={block} />)}</Canvas>
I couldn’t wrap my head around re-implementing this with Instances, so I was trying to avoid recreating geometries and materials on every render. The geometry was straightforward. For the material, I’m trying something like this:
// helper.ts
const boxGeometry = new BoxGeometry(cubeSize, cubeSize, cubeSize);
const materialCache = new Map<string, Material | Material[]>();
// Block.tsx
const Block = ({block}) => {
const blockRef = useRef<Mesh>(null);
const blockMaterialId = useMemo(
() => `block-${block.type}-${block.state}`,
[block.type, block.state]
);
const blockMaterial = materialCache.get(blockMaterialId);
return (
// if there's a material for this block type + state, we use it and avoid creating a new instance
<Box ...args geometry={boxGeometry} {...(blockMaterial && { material: blockMaterial })}>
{!blockMaterial && (<MeshTransmissionMaterial ...args />)}
<Edges ...args />
</Box>
);
}
This is working fine as long as the block type for a given block doesn’t change. Among the block interactions the user might trigger a change in the block. When this happens, with my optimization, I end up saving a standard material in white instead of the mesh transmission material in blue, and I’m not exactly sure why.
If someone would be able to shed some light. I’m also curious to hear how I could refactor this block component to work with instances. It seems like this would give me the best result in terms of performance, but I’m not sure how to extract the material with the right args to use with the instance component.
Also worth mentioning, I’m using Drei’s Box, Edge and MeshTransmissionMaterial for the rendering, plus react-spring. If you’re curious, this is the live version of the scene: https://stacks-nakamoto-block-simulator.vercel.app/. And the code is here: GitHub - vicnicius/stacks-nakamoto-block-simulator: A Stacks block simulator for educational purposes
My current attempt to improve the performance is here: WIP by vicnicius · Pull Request #16 · vicnicius/stacks-nakamoto-block-simulator · GitHub
Any help or feedback is appreciated.
Thanks in advance!