Jump to content

Pixi.js performace tweaks.


Recommended Posts

Hey, I'm making mobile game called EPIC IDLE RPG. Its over year and half of making it solo. Lately I hit performance(20-25fps) issues on 1920x1080. I started thinking about moving game logic into web worker, but it's big task to do. I use mostly preloaded pixi.sprites, pixi.animatedSprites, spine2d for characters and webm videos for backgrounds animations.  What I can do to improve performance?

Here how game looks: 



Link to comment
Share on other sites

Without actually seeing the project running on browser I cant give a direct answer what part requires optimizations.

First you should profile what actually causes the fps to drop. Is it cpu bound (profiler helps with this) or is gpu the culprit.

One good way to start is to check the amount of gl commands & drawcalls you have to find out if gpu is the issue. Using a tool like spectorjs makes that easy. Usually drawcalls under 100 is good and under 50 when targeting mobile. If you have a lot more then make sure you are using some kind of texture atlas / spritesheet. That allows the drawing to happen in batches which reduces a lot of overhead.

For cpu issues, profiling and checking what takes most of the processing time and then optimizing from that is usually a good way to reach better performances easily. Logic is very rarely the part that takes most of cpu time, so wouldn't worry about moving that into webworker unless you first profile and see that the logic is actually the reason for slow fps.

Link to comment
Share on other sites

  • 2 weeks later...


Sorry for late response.

Thanks for spectorJS tool. I have even 250 draws(Game menu).

I use spritesheets for every static and animated images. Only videos and backgrounds are loaded separately. 

From profilling the most costful operations are:

  • requestAnimationFrame(250-300ms).
  • In some views loading of spine(300ms). Can I somehow preload them fully? 
  • Sometimes decoding of image(300ms). My guess there wasn't enought of space on gpu to hold it?

I use PIXI.shared.Loader with app.renderer.plugins.preload for every image,video and spine2d.

What can be the best solution other than downscaling images? Backgrounds and vidoes are 1920x1080, when spritesheets are 2048x2048 and in case 4096x4096.

Edited by jakubdev
Link to comment
Share on other sites

250 game draw calls is pretty much. Optimizing that would help. How many gl operations you have? Those have a small cpu penalty and having a lot of operations will cause cpu issues. Usually drawcall optimizations help with this also.

The loading / decoding I think are cases where a new image is uploaded to gpu. Using compressed textures can help with this. Also disabling texture garbage collection could help, though then you would need to manage texture destruction manually. PIXI.Settings.GC_MODE = PIXI.GC_MODES.MANUAL

Link to comment
Share on other sites

Can't find something called gl_operations. 

Here video of capture of spectorJS: https://www.awesomescreenshot.com/video/6480331?key=c8e20181ea0a99a2ef3fcfb097509375

Maybe that's only issue of doing everything on main thread? I'm thinking about moving game logic fully to web worker or WASM. Read today about guy making GBA emulator for mobile and using assembly script he achieved 160fps when normal javascript was on 120fps. Maybe multithreading gonna be best answer and keeping main thread just for pixijs, but don't know atm how difficult it is to use AssemblyScript.

Edited by jakubdev
Link to comment
Share on other sites

It's the commands summary. 32 is really good for that.

Before implementing game logic in wasm I would suggest that you confirm that the logic is actually the culprit by profiling and checking what takes the time in your requestAnimationFrame. Could you save the performance profile and I could take a look? Or is the game somewhere online?

Link to comment
Share on other sites

The long time from around 2.5s to 4.5s is when images are decoded and uploaded to gpu. That part would be faster with compressed textures.

Same image decode causes the long time around 7.5s mark.

Around 10s the players moves to game from menu or some other event? Lots of video textures created there and for some reason measureText takes a long time (5ms). Do you have a lot of texts?

After that the time until around 15s mark there's a texture upload on almost every frame. Do you have some frame by frame animations going on that are single image for every frame? Or it might be the video that generates those.

There's few frames dropped when changing menustate after that, but as it's only a single time I wouldnt worry about that too much.

Does the actual gameplay start around 14.8s? There's at least two updates that take some time and would benefit from having a small loadscreen / some static screen so that player doesnt notice frame drops. The game world is created at that point if I assume correctly and as a consequence few new images are decoded on the next render.

Most of the time after that is spent with updateTransform. Optimizing the amount of objects on scenegraph / culling / using visible=false to block updates from some containers could help.

It looks like that in start battle new elements are created for healtbars/mp bars/ui. These could be pooled/be already ready in memory. Or have them as they are now and just make sure you destroy them afterwards. Few frames are dropped in the creation.

After the battle has started (around 15.8s) you have multiple moveFunctions generating through request animation frame. Is this intentional? They seem to take pretty many frames. Mostly spending time by getting the bounds & updating transforms. Also some frames with a pretty long text updates.

Most of the high cpu usage at the end of profile looks to be coming from updateTransform & render. UpdateTransform being the larger one often. This usually means that you have a lot of objects.  Porting your game logic to wasm wouldnt help unless you would port pixi to wasm as well. As otherwise you would just end up calling javascript from wasm very often and the benefits would pretty much be lost.

Using a chrome plugin like pixi.js devtools helps in analyzing the scenegraph and optimizing it.

Link to comment
Share on other sites

Hey, First of thanks for help.

2.5s-4.5s,will try compressed texture. 

Found out BASIC/etc1s. It weight only 54kb for same spritesheet.


Can't make Basis work. I get stuck on loading without any error. (screen of loader being stuck) 

Installed `yarn add @pixi/basis`. Moved `basis_transcoder.wasm` and `basis_transcoder.js` into my game assets folder.
In my initPixi added lines:






What compression type you recommend for images with alpha?

EDIT 4: 

For videos I found out veed.io where it reduces 60-70% of size after compressing.

7.5s    There was 5 Spine2d champions loading. Can I preload spine2d?

10s Not really. Menu contantains the most 18 texts. This was guild view. Used there five .webm with alpha and one mp4 without alpha for background. That is bad idea as user can see that something was loading(Black squares for 1s). Will try later .png instead of video to see how it will work.

14+s It was battle prepare view where 10 characters load at once plus looping video 5-30s for battle background. There are every few seconds small bottlenecks. 
Currently when battle starts everything happends immedietly without spinner or loader. I was thinking at start about game loader,but it was to big. Maybe small spinner would be best idea.

Good idea to preload hp/mp bars. I started preloading skills as first use of spell was costful. Currently I use spell and after use I hide it and reset afterwards.

Yes I made "Character control system". Every character moves simultaneously and decides on his own without any turns. So they need to know where to go, whom to attack.
Doesn't update transform also calculate functions that stops him? I made logics for detecting enemy if in range, deciding what to do, moving, use skills. I thought they are calculated into that updateTransform.

How I can use pixi.js dev tools to optimize? It only shows displayObjects, container etc.


Edited by jakubdev
Link to comment
Share on other sites

I havent used compressed textures myself so dont know how they should be done or what is good for different types.

The updateTransforms are most likely a result of getbounds or other similar function being called. Didnt look into what calls them that much, only noticed that a lot of time is being spent on those.

The devtools is good for checking what items could be set to invisible and to also check where you have lots of objects. As the only result I have encountered where transformupdates take most of the time is something with a lot of objects on scenegraph.

Link to comment
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.

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.


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...