P5-wrapper / react

A wrapper component that allows you to utilise P5 sketches within React apps.
https://P5-wrapper.github.io/react/
MIT License
494 stars 50 forks source link

How not to re-render the canvas once state changes? #463

Open nijatmursali opened 2 weeks ago

nijatmursali commented 2 weeks ago

I have couple of states where I change on different parts of the project:

which are:

    const [sketchDimensions, setSketchDimensions] = useState(SHOE_DIMENSIONS);
    const [selectedSegment, setSelectedSegment] = useState(null);
    const [activeSidebar, setActiveSidebar] = useState(1);

    // and so on 

and I'm using @P5-wrapper/react as a p5 wrapper.

I have a canvas component:

const Canvas = memo(({ sketchDimensions, setSketchDimensions, selectedSegment, setSelectedSegment, activeSidebar, selectedView, currentView }) => {
    if (sketchDimensions.length > 0) {
        console.log('initialized canvas');

        return (
            <ReactP5Wrapper
                sketch={shoeSketch}
                sketchDimensions={sketchDimensions}
                setSketchDimensions={setSketchDimensions}
                selectedSegment={selectedSegment}
                setSelectedSegment={setSelectedSegment}
                // and others
            />
        );
    }
});

and problem appears when the state changes and it creates new canvas elements and GPU increases around 0.8-0.9GB every time one of the states change.

task manager

is it possible not to re-render the canvas every time state changes?

jamesrweb commented 2 weeks ago

The canvas does re-render when the state changes, it checks all props except some "built ins" but for sure all custom props and if they change, the canvas will re-render. There must be something wrong with the memo or surrounding component structure if you're seeing the canvas doesn't do so, since that would imply it never receives the new state.

nijatmursali commented 2 weeks ago

I have updateWithProps like following:

    p5.updateWithProps = props => {
        if (props.sketchDimensions !== dataDimensions) {
            dataDimensions = props.sketchDimensions;
        }
        if (props.setSketchDimensions !== setDataDimensions) {
            setDataDimensions = props.setSketchDimensions;
        }

        if (props.selectedSegment !== initVariables.selectedSegment) {
            initVariables.selectedSegment = props.selectedSegment;
        }
        if (props.setSelectedSegment !== initVariables.setSelectedSegment) {
            initVariables.setSelectedSegment = props.setSelectedSegment;
        }

        if (props.activeSidebar !== initVariables.activeSidebar) {
            initVariables.activeSidebar = props.activeSidebar;
        }
    };

and sketchDimensions are updated every time either user writes on input field or changes the point on p5 side.

and the Canvas component is called in parent component:

       <Canvas
        sketchDimensions={sketchDimensions}
        setSketchDimensions={setSketchDimensions}
        selectedSegment={selectedSegment}
        setSelectedSegment={setSelectedSegment}
        activeSidebar={activeSidebar}
        setActiveSidebar={setActiveSidebar}
        selectedView={selectedView}
        currentView={currentView}
    />

where state is declared like:

    const [sketchDimensions, setSketchDimensions] = useState(SHOE_DIMENSIONS);
    const [selectedSegment, setSelectedSegment] = useState(null);
nijatmursali commented 2 weeks ago

The thing is that memo works just fine, and once for example sketchDimensions change then it re-creates the canvas. But our actual problem is that recreating the canvas is too costly and it increases the GPU usage quite a lot because we are drawing our shoe model in 2D and using segments to select and do actions with them.

I'm wondering if it is possible for example get rid of cache (or basically the memory) that comes from previous canvas if the new one is created.

jamesrweb commented 2 weeks ago

The first canvas is destroyed before the next one is created. The rest is pure P5, if there's a memory or cache issue, it's upstream but I would argue there's potentially something else causing the spike: data structures, reference checks, etc potentially.

Still, this library just wraps P5 and provides a convenient API for React applications to use it. If there's a low level issue that you think exists, you will have to raise an issue with P5 in their repository then.

We have no internal caches or anything. It's a simple, "sketch changed? Destroy old, create new." And "props changed? Pass to the sketch functions updateWithProps method if it's set". That's really it.

nijatmursali commented 2 weeks ago

I see, I'm going to post it also in p5 issues.