Jump to content

Caching frequently rendered elements


Recommended Posts

Hey, i'm making a music composer for a webapp, i'm using react-pixi to render all the information that i need for the song, they are basically made out of columns with inside of it the single notes, in total there are 3 different elements that are rendered, the only thing that changes are their position, i'm already using culling to not render elements which aren't visible, but when there are many notes on screen, it still lags, i've found the issue being the repetitive drawing of the notes.

What i want to ask is, is there a way to speed up the drawing of elements on screen by caching the elements to render ? will this help performance wise? worst case scenario i render 40 columns and 840 notes every +-50ms

I attached a video to show the issue i'm having now (WATCHOUT EARRAPE)

Link to post
Share on other sites

What i want to ask is, is there a way to speed up the drawing of elements on screen by caching the elements to render

it wont help.

The number of elements that are bounded to react, and number of elements that are managed to pixi are your problems.

As always with "100k bunnies" tasks - you have to realize that algorithm is divided by two parts.

1. high-level algo: write the algo that fills up all notes in current screen, maybe a bit bigger than current screen. Its your controlling algo, it updates your notes, not react!

2. low-level algo: choose something more effective than Container/Sprite combination. Put many elements in same Graphics or @pixi/tilemap , manage multiple elemetns like that, for example.

If I had more time I could help and post more information, but in this case - im sorry, you have to post more details and maybe source code :)

Link to post
Share on other sites
            <Stage
                width={s.width}
                height={s.height}
                options={pixiOptions}
            >
                <Container
                    anchor={[0.5, 0.5]}
                    x={xPos}
                >
                    {data.columns.map((column, i) => {
                        if (counter > 15) {
                            switcher = !switcher
                            counter = 0
                        }
                        counter++
                        if (!isVisible(i, data.selected)) return null
                        let standardBg = columnBackgrounds[Number(switcher)]
                        let bgColor = column.tempoDivider === 1 ? standardBg : column.color
                        return <Column
                            key={i}
                            data={column}
                            index={i}
                            sizes={sizes}
                            bgColor={bgColor}
                            click={functions.selectColumn}
                            isSelected={i === data.selected}
                        />

                    })}
                </Container>
            </Stage>

Here's the code that renders the elements, the issue seems to be with the <Column> element, with the profiler i saw that the issue seems to be with react, in fact if i return an empty <Container> it still lags as much as it did if i actually rendered the column component, if i return null, the lag disappears. I guess i have to do a more "low level" approach

Link to post
Share on other sites
function Column(props) {
    let { data, index, sizes, click, isSelected, bgColor } = props
    let color = isSelected ? selectedColor : bgColor
    function drawBg(g) {
        g.clear()
        g.beginFill(color, 1)
        g.drawRect(0, 0, sizes.width, sizes.height)
        g.lineStyle(1, 0x00000, 0.8)
        g.moveTo(sizes.width, 0)
        g.lineTo(sizes.width, sizes.height)
        g.moveTo(0, 0)
        g.lineTo(0, sizes.height)
    }
    let noteHeight = sizes.height / 21 - (noteMargin * 2)
    let noteWidth = sizes.width - (noteMargin * 2)
    function drawNote(g, note) {
        g.clear()
        g.beginFill(note.color, 1)
        g.drawRoundedRect(noteMargin, positions[note.index] * sizes.height / 21, noteWidth, noteHeight, noteBorder)
        g.endFill()
    }
    //index color
    return <Container
        pointerdown={() => click(index)}
        interactive={true}
        x={sizes.width * index}
    >
        <Graphics draw={drawBg} />
        {data.notes.map((note) => {
            return <Graphics draw={(g) => drawNote(g, note)} />
        })}
    </Container>
}

Ok this is the code that renders the column, the lag coming from an empty <Column> that i explained before seems to not be that influenctial, the biggest performance hit is that <Graphics draw={(g) => drawNote(g, note)} />, if i return null there, the lag is greately reduced, maybe instead of making a new <Graphics> component for each note i could just draw everything in <Graphics draw={drawBg}/> component

Link to post
Share on other sites

Ok so i'm having issues trying to generate the texture, i followed some code that i saw and it showed that this should work? but it doesn't, i get an error "sprite image prop is invalid", this is the code i use to generate the cache:

the cached elements are RenderTexture 
 

import {TempoChangers} from "../SongUtils"
import * as PIXI from "pixi.js"
class ComposerCache{
    constructor(width,height){
        this.width = width
        this.height = height
        this.cache = {
            columns: [],
            notes: []
        }
        this.app = new PIXI.Application({
            width: width,
            height:height,
        })
        this.generate()
    }
    generate = () => {
        TempoChangers.forEach(tempoChanger => {
            let column = new PIXI.Graphics()
            column.clear()
            column.beginFill(tempoChanger.color, 1)
            column.drawRect(0, 0, this.width, this.height)
            column.lineStyle(1, 0x00000, 0.8)
            column.moveTo(this.width, 0)
            column.lineTo(this.width, this.height)
            column.moveTo(0, 0)
            column.lineTo(0, this.height)
            let b = this.app.renderer.generateTexture(column)
            this.cache.columns.push(b)
        })

    }
}

And when i go to render it i just do:

        <Sprite
            image={cache.columns[data.tempoChanger]}
        >
        </Sprite>

 

Link to post
Share on other sites
Just now, ivan.popelyshev said:

the idea is to generate all possible notes, and re-use those textures. Better if its actually one texture and you use regions of it "new PIXI.Texture(baseTexture, frame)"

that's what i did, i'm now reusing all textures and pre render them, i think it might be because of pixi library i'm using for react (@inlet/react-pixi) which watching benchmarks seems to be 4 times slower than normal pixi when using many elements

Link to post
Share on other sites
Posted (edited)
16 minutes ago, ivan.popelyshev said:

 Also, you have to take profile of this app and see which JS functions are slow.

That's really hard... with react it's all a mess, everything is super nested but the main issue seems to come from the changing of state and the rendering, they do a 50/50. I'll try to reduce the amounts of state changes but i don't think i can change much

unknown.png

capture 21_20_32 (1).json

Edited by Specy
Link to post
Share on other sites

just in case , if you using react , you have debugger tools thats help you to debugg your app.
image.thumb.png.0d903e2264994a3faf7778518d9c644d.png

 

image.thumb.png.8cf7b388d4bcafd3855156a9edd81b7f.png

image.thumb.png.cfe858a950d4783dbf680f1f7d2168e5.png

 

i hope you are not working without this !😉 It should help a lot.
Also for pixijs! Another debugg tools avaible.
https://chrome.google.com/webstore/detail/pixijs-devtools/aamddddknhcagpehecnhphigffljadon

And also the native memory tool in devtool allow you check and compare all textures between 2 states.
This can help.

image.png.071e0e5044615df366cd9aee11332aed.png


 

 

Edited by jonforum
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...