unfoldedtorus

pixijs animating few rectangles gets slow on mobiles

Recommended Posts

Hi everybody,

I'm struggling with performance issues using pixijs.

I just have to slide few rectangles using a mask and animating borders and tinting after sliding animation.

It should be simple and easy to reach high FPS even on mobile, in fact it easily slows down

this is the profiler result during an animation: 1997839270_Screenshotfrom2018-06-2823-18-34.thumb.png.56a8d6d195e78b3d1cd874e10e6bd161.png

It seems the call ticker.update() is eating up a lot. I've changed my code, stopped ticker and called only when needed but the results is that on mobile devices it slows down even under 15fps

I'm managing fps my self trying to complete the slide in 200ms and animations in the following 200ms. so I start calling requestAnimationFrame 12 times and reducing if it takes longer than 200ms.

during each step I change position of all tiles on one column/row and call ticker.update() (I believe is something really basics that should not slow down at all)

I use a mask to hide some tiles in some places of the board, could be the mask an issue?

Someone can give me some hint? the following video shows how the animation  works (I'm not going to advertise here, I've already posted my game in the right section)

the game is already playable online: http://unfoldedtorus.com (in case a live sample would be better)

 thanks in advance

Michele

Edited by unfoldedtorus
mispelling

Share this post


Link to post
Share on other sites

Hi Ivan,

thanks for your quick reply.

you can play even without login, just dismiss login form and click single player.

probably kaspersky doesnt like non https sites. the server is from ovh in france I dont think this is an issue.

I can post here part of the code I use for animating: (the whle file is 900 lines I hope the following is enough to understand whan am I doing wrong)

/*the following is the code I call in each requestAnimationFrame to animate text and borders of each tile. then I call ticker.update() as shown in the function below
similarly I animate position and tint*/ 
const animateTileBorderAndText = (tileObj, steps, _color, radius, textSize, strokeThickness,
        _config) => {
        let pixiTile = tileObj.tile;

        let graphicsData = pixiTile.graphicsData[0];
        let shape = graphicsData.shape;
        let textStyle = pixiTile.children[0].style;
        let textInc = (textSize - textStyle.fontSize) / steps;
        let strokeInc = (strokeThickness - textStyle.strokeThickness) / steps;

        let prevColor = graphicsData.fillColor;
        let color = _color !== null ? _color : prevColor;
        let alpha = pixiTile.alpha;
        let h = shape.height;
        let w = shape.width;
        let rad = shape.radius;
        let radiusInc = (radius - rad) / steps;

        let r = (prevColor & 0xFF0000) >> 16;
        let g = (prevColor & 0x00FF00) >> 8;
        let b = prevColor & 0x0000FF;
        let rc = (color & 0xFF0000) >> 16;
        let rg = (color & 0x00FF00) >> 8;
        let rb = color & 0x0000FF;
        let redStep = (rc - r) / steps;
        let greenStep = (rg - g) / steps;
        let blueStep = (rb - b) / steps;
        let paintColor = prevColor;
        let goPaint = color !== prevColor;

        let animate = (s) => {
            if (s === steps) {
                textStyle.fontSize = textSize;
                textStyle.strokeThickness = strokeThickness;

                //pixiTile.tint = color;

                return true;
            }

            if (goPaint) {
                r += redStep;
                g += greenStep;
                b += blueStep;
                paintColor = (r << 16) + (g << 8) + b;
            }

            textStyle.fontSize += textInc;
            textStyle.strokeThickness += strokeInc;
            pixiTile.clear()
            pixiTile.beginFill(paintColor, alpha)
            pixiTile.drawRoundedRect(0, 0, h, w, rad + radiusInc * (s + 1))
            pixiTile.endFill();

        };
        return animate;
    };

/*i've tested this code calling console.time() and I'm sure this is not the source of slowness, maybe the way it changes pixi object could be the cause of slowness in ticker.update()*/

/*here the code that calls the above one for each tile and then calls ticker.update inside requestAnimationFrame*/

const slideRow = (direction, conf, tilesMap, pointerDown, pointerUp, SEM) => {

        let moveOf = conf.tileFullSize;
        let moveOfAbs = 1;

        let rowIdx = getRowToSlide(pointerDown.y, pointerUp.y, conf.tileFullSize);

        if (direction === SLIDE_LEFT) {
            moveOf = -moveOf;
            moveOfAbs = -1;
            conf.torusModel.slideRowLeft(rowIdx); //slide the underling model
        } else {
            conf.torusModel.slideRowRight(rowIdx); //slide the underling model
        }
        conf.torusModel.valuate();

        /**set number value of hidden tiles */
        beforeSlideSetRow(rowIdx, direction, tilesMap, conf);

        let slideFns = [];
        let postSlideFns = [];

        let rowToSlide = tilesMap[rowIdx];

        for (let i = -1; i <= conf.cols; ++i) {
            rowToSlide[i].col += moveOfAbs;
            /**push function to change position of tiles in given steps */
            slideFns.push(slideSingleTile(rowToSlide[i], moveOf, 'x', conf, SEM, tilesMap));

        }

        let steps = conf.animationSteps;
        let s = 1;
        let startTime = performance.now();

        const postSlideAnimations = (time) => {
            if (s < steps) {

                for (let i = 0; i < postSlideFns.length; ++i) {
                    postSlideFns[i](s);
                }

                s++;
                /**update pixi ticker when all position are updated */
                conf.ticker.update();
            } else {
                //update internal configuration data
                conf.updateSlideTimeDelta(time - startTime);
                PUBSUB.publish(conf.SLIDE_CODE, conf.torusModel.getData());
                conf.updateSlideSteps();
                conf.SEMAPHORES.slide = true;

                return 0;
            }

            return requestAnimationFrame(postSlideAnimations);
        };


        const animateSlideRow = (time) => {
            if (s < steps) {
                for (let j = 0; j < slideFns.length; ++j) {
                    slideFns[j](s);
                }
                s++;

                conf.ticker.update();
            } else {

                conf.updateSlideTimeDelta(time - startTime);
                /**generate function to animate background borders and text */
                slideFns.forEach(f => postSlideFns.push(...f(s)));

                console.log(postSlideFns);

                conf.ticker.update();

                /*run borders-text-background animations*/
                steps = conf.animationSteps;
                s = 0;
                startTime = performance.now();
                return postSlideAnimations(startTime);
            }


            return requestAnimationFrame(animateSlideRow);
        };

        animateSlideRow(startTime);


    };

 

if it comes out to be time consuming for you but you think there's a way to make it run faster I can pay for your consultancy

thanks a lot

Michele

Edited by unfoldedtorus
no js highlight

Share this post


Link to post
Share on other sites

Well, first i see that postSlideAnimations is calling pixi update and the function that calls it also calls pixi ticker update. Pixi ticker calls render() each time, so, multiple times in a frame.

Text resize can be extremely slow, because it causes resize of temporary canvas. First switch off the text and look at results, if its text, then you probably have to specify text canvas size before you start animation, I cant recommend anything except reading "PIXI.Text" code. However, it will still call one "texImage2d" to upload that text to videomemory, that can be slow too. If you prerender all the characters ytou need or use bitmap text or try pixi-sdf plugin, it can save performance. But first you test if the text is actual problem.

For each tile you have one graphics and one text - that's two drawcalls with a shader switch - extremely not effective. How many tiles are there? For PC its fine to have up to 200DC, for mobile its 50DC. So, 25 tiles max on mobiles. Dumping all the texts  and graphics borders in prepared atlas (with photoshop or whatever you can use) will drop it down to 1 shader switch and 1 drawcall total.

I know its difficult to understand because i use terms not from Pixi API but from low-level Webgl/2d context, but every html5 coder have to research that as soon as they get their first "2FPS" issue :) PixiJS has good architecture and collection of general things that you can use fast. Its highly likely that you'll deal by using sprites and stuff, or patching Text to suit your needs. There are many settings you can try when you read the code and understand how it actually works with webgl. And if pixi cant give you something,c certain plugins can.

Share this post


Link to post
Share on other sites

on my side your update are perfect at (~~16ms)

 

image.thumb.png.f4389dd449ce44d52b6e0a28613453aa.png

 

the fps meter are not ok on your game, because you not have a constant update.
You maybe stop the game update between click.
Try to add a constante update, and look your fps, you will alway keep 60.
But if you stop update and start again each time click called , the fps meter will go crazy, but it not the reel frame rate, he will need time to be stabilize.

image.thumb.png.7e9e2ec593c79926805094ced359f90c.png

 

So on my side , it not seem to lag! .

Share this post


Link to post
Share on other sites

Hi Jonforum,

yes the ticker is stopped at the beginning, and is updated only when a slide gesture is catched just to complete the slide animation.

So you suggest to start the ticker  and let it go even if the player is looking at the ranking? or just thinking?

When there are few tiles on desktops no problems occurs. the issue is on mobile where using pixi the way I used is slower than making this animation with pure html dom element+css.

Btw I'm going to try your advice too as well as test cahcingAsBitmap and texturifying tiles, so animating using filters or mask instead of changing textsize and border radius at each update.

I'll let you know my results as soon as I complete the test.

thanks

Share this post


Link to post
Share on other sites
11 minutes ago, unfoldedtorus said:

Hi Jonforum,

yes the ticker is stopped at the beginning, and is updated only when a slide gesture is catched just to complete the slide animation.

So you suggest to start the ticker  and let it go even if the player is looking at the ranking? or just thinking?

When there are few tiles on desktops no problems occurs. the issue is on mobile where using pixi the way I used is slower than making this animation with pure html dom element+css.

Btw I'm going to try your advice too as well as test cahcingAsBitmap and texturifying tiles, so animating using filters or mask instead of changing textsize and border radius at each update.

I'll let you know my results as soon as I complete the test.

thanks

yes i suggest this only for help you debugging , but keep your idea to update only when the player slide gesture for your final deployed project.

Share this post


Link to post
Share on other sites

So I did some testing,

using ticker.update() where necessary or letting it go does not affect performance.

While Creating textures starting from rectangles and animating them improves a lot. Baiscally its twice as fast moving 100 tiles,  increasing to 300 tiles in case of sprites affects just a little bit the performance while slows down twice using Graphics objects with text.

I've created a little repo to show these results

https://github.com/MikBin/pixi-tiles

here there are screeenshots of Chrome DevTools:

100 tiles comparison (left using GRAPHICS right using SPRITES)

100_TILES_GRAPHICS.thumb.png.154e19b9301bb6b24140698dbdab0cfb.png100_TILES_SPRITES.thumb.png.dd5cfc7ccb81e61fc7d3bc1a2969fe25.png

300 tiles left using GRAPHICS right using SPRITES

300_TILES_GRAPHICS.png.65f0d33689d332b73767c0a5477a502a.png300_TILES_SPRITES.png.e3b6103c6ea14308ed52babbe69da966.png

Now my issue is how to animate rounded borders and numbers if I have Sprites?

Is there any way to "blend" two textures gradually step by step where at the beginning and ending of animation just one of the two have to be visible?

I hope its clear what I mean. I've been looking on pixijs examples and searching a way using mask or filters but I'm getting a little lost. maybe is something easy to reach and one of you already know best practice to do it.

thanks for your help

Share this post


Link to post
Share on other sites

The only easy way to animate sprite it use pixi spriteAnimations , you can make a animation on afterEffect and make a spriteSheet with texturePacker.
Or also another pro way, it use Spine2d.

The hard way, if you not have software to create your animations, it to use pixi graphics and rendering textures.
Example:
Create multiple graphics square with multiple round border value + (pre Rendering as a textures).
https://codepen.io/staff0rd/pen/eByzQm?editors=0010
And add all textures to a spriteAnimations or create your own animations rendering.
https://pixijs.io/examples/#/basics/spritesheet.js

or you can also try use only  PIXI.Sprite.from(renderTextures);
And use sprite.renderable = false,
or merge texture sprite in a update and change the sprite._texture = MyTextureListe[i++]..
and use _texture._updateUvs() for update.

You have a lot of way.

Maybe another guy will have a better idea, unfortunately this is the only idea that can come to my mind to help you.
 

Share this post


Link to post
Share on other sites

hard to say, maybe you should better test on your side with your specific Setup and code.

Here my performance test with spine2d animation (x350 complexe very high resolution)

And this one with SpriteAnimation , it very more fast than the spine2d aniamtion.
But maybe if you juste hack the texture update, it will very more fast.
 

 

if you make your own test, keep us informed it is always useful to have statistics in different context.

Share this post


Link to post
Share on other sites

Tests done by animatin using a sequence of precomputed textures;

Performance is very good, here some screenshots: (100 sprites on left, 500 on right)

100_TILES_WITH_ANIMATION_TEXTURE_BASED.png.d5387ca333a7740c147cca122fd227ef.png500_TILES_TEXTURE_ANIMATION.png.9ea6b5ad1d5b2a8835d16cce865d453d.png

just on question about rendered textures it seems are not antialised, here a sample:

Is there any workaround?

tiles_antialias.thumb.png.653870cbab8532e609acb0c3170fdd91.png

Share this post


Link to post
Share on other sites

I dont think it'll help. forceFXAA doesnt even work in pixi-v4. "antialias" can fix only straight lines if they are edges of your texture, and for insides there are filtering modes, like `PIXI.SCALE_MODES.LINEAR` and NEAREST.  By default its linear so its ok, make sure that you didnt enable NEAREST somewhere.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.