Jump to content

I am stuck in low FPS.


caymanbruce
 Share

Recommended Posts

I find that my game is always running at very low FPS (10 - 28 FPS) no matter what I try to improve the performance.

The worst part is I don't know where to begin debugging or profiling.

I have the following question, with priority in order.

  1. When FPS is low, is it always client side's problem? ( I just want to make sure if I am debugging on the right side, although I have also improved my server side in the process)
  2. How to profile it on Chrome? I have used the performance tab on Chrome but all I can see is the call tree from the ticker which goes all the way down to render function and renderCanvas function in PIXI's renderer. It doesn't help much with this information.
  3. I have tried commented out parts that are used to render different things in my game. But not leaving a single part out of my game would improve the FPS, the only way to get to 60 FPS is not to display the whole game scene (a container in app.stage) at all.

I hope this question is not too specific.

Link to comment
Share on other sites

2. look bottom-up, you see functions that are taking most of time.

Simple idea - look at "idle", if its big, that means the problem is on GPU side, if its small - its CPU. Most of problems on GPU side come from big overdraw, too many invocations of fragment shader. The solution heavily depends on platform.

Also, 100% is the time without idle, so if you measured 10 seconds, and idle is 9 seconds, then 100% is 1 second, which is good. "program" are actions on browser-api side, not js execution.

And I remember that you mentioned that you dont use webgl - in that case there's no way to optimize GPU side at all :( Please gather info about your auditory, how many users have webgl enabled, and which platforms/browsers do they use.

Link to comment
Share on other sites

Are you maybe rendering too much stuff that isn't on screen? Like if the server says there's a bunch of stuff nearby do you create and render sprites for all of it right away or just when each element is actually visible? And when stuff moves out of visibility do you remove it from the stage (or set its visible property to false)? It's kind of a wild guess but could cause low FPS..

Or maybe try copying and pasting the code to create your textures to a new minimal project that just has a for loop for each texture, to add sprites to the stage. If you still can't add enough stuff to the stage then at least you know it has nothing to do with the networking/MMO aspect and can start narrowing down and testing more from there.

Link to comment
Share on other sites

2 hours ago, Jinz said:

Are you maybe rendering too much stuff that isn't on screen? Like if the server says there's a bunch of stuff nearby do you create and render sprites for all of it right away or just when each element is actually visible? And when stuff moves out of visibility do you remove it from the stage (or set its visible property to false)? It's kind of a wild guess but could cause low FPS..

Or maybe try copying and pasting the code to create your textures to a new minimal project that just has a for loop for each texture, to add sprites to the stage. If you still can't add enough stuff to the stage then at least you know it has nothing to do with the networking/MMO aspect and can start narrowing down and testing more from there.

Thanks for your suggestions. What I currently do is to set the renderable attribute to false when the sprites are off-screen, and put these sprites into a lingerSprites array. A few seconds later if the renderable attribute is still false I will remove them from the lingerSprites array. I log the lingerSprites array and I find it is recycling these sprites as foods and other players go off screen. One thing I didn't do is to destroy the dynamic textures. As @ivan.popelyshev mentioned in one of the previous threads. But the foods are using static textures now and the only the players are using dynamic textures. I will only need to destroy them when the player is killed.

Link to comment
Share on other sites

@Jinz I have taken out the network stuff, and only run the local player with the server on. So there is only one player moving on a 10000 X 10000 TilingSprite map with a background picture preloaded. The FPS is 45 ~ 54 in Chrome, is this normal? Can I get to 60 FPS with single player?

Link to comment
Share on other sites

I've been using window.innerWidth and window.innerHeight for my TilingSprite dimensions (the same dimensions that I resize the renderer to) without problems. IDK why it would need to be so big (10000 X 10000) as you're using. OTOH I assume you mean Pixi.extras.TilingSprite, but if you mean Pixi.tilemap then IDK if those dimensions are normal or not.. If you comment out adding it to stage is the FPS good?

UPDATE: The TilingSprite example uses app.renderer.width and app.renderer.height for the dimensions. I'm pretty sure doing it my way causes scaling when resolution isn't 1 (soon to be my old way of doing it I think:)

Link to comment
Share on other sites

@Jinz I am using PIXI.extras.TilingSprite for the map. But I think the main problem is the player sprites. As I add more sprites onto my player it gets really slow. When it has 50+ sprites it is slow as hell. Average FPS is 14 ~ 20.  I think the same applies when there are multiple players on the screen. But PIXI is supposed to be capable of handling thousands of animated sprites. Why would this happen? I add the sprites only once and create them using "Sprite.from(canvasObject)". Would that be any problem?

Link to comment
Share on other sites

@caymanbruce You are asking about the same problem everywhere and answer is still "get your users webgl": :) PIXI layer performance is not a problem when you are using canvas2d, it will be the same if you delete all the code and make your own renderer from scratch.

There is one thing about 2d context: it can also be sped up. Please check on target machine that its actually has optimized 2d.

Link to comment
Share on other sites

Maybe using 'RenderTexture's to reduce the sprite count could help? E.G. group together parts that move the same and render each group to a 'RenderTexture' - and maybe redraw the 'RenderTexture's every several frames to give the different parts some independent animations or when you need to add/remove parts.

Or maybe can do a simplified version for Canvas-rendering and a juicier version for WebGL-rendering that has more sprites per player and dynamic filter effects, etc..

Link to comment
Share on other sites

9 hours ago, ivan.popelyshev said:

@caymanbruce You are asking about the same problem everywhere and answer is still "get your users webgl": :) PIXI layer performance is not a problem when you are using canvas2d, it will be the same if you delete all the code and make your own renderer from scratch.

There is one thing about 2d context: it can also be sped up. Please check on target machine that its actually has optimized 2d.

Do you mean it is a problem or not a problem? I am confused with your words in italics as you suggest I use webGL. Thing is, I can play other io browser games on my laptop very smoothly, and my laptop doesn't support webGL, which means those games are built in Canvas2D. Hence in theory if I optimize my code correctly it should be as smooth as those games without using webGL.

Link to comment
Share on other sites

4 hours ago, ivan.popelyshev said:

Then disassemble them :) Ther'es probably some tricks with temporary canvases involved that can be reproduced with PIXI RenderTexture. I have a lot of those tricks in gameofbombs.com (I made IO-like game long time ago).

I thought this is simple work in PIXI.js. All I put are some simple circle shape sprites on the map and animate them. I am using temporary canvas object to build the sprites. But when should I use RenderTexture? Is RenderTexture faster than using canvas like this?

const canvasObject = document.createElement('canvas');
...
const texture = Texture.fromCanvas(canvasObject); 
const sprite = Sprite.from(texture);

I have tried using RenderTexture before. But some features are missing in this class, maybe I just don't know enough of it. 

When using RenderTexture, I need to use an image or a PIXI.Graphics object to draw it on a renderer, which doesn't allow me to use gradient color because I can't get the canvas context.

 

UPDATED: I realize I can turn on WebGL in Chrome browser on my Macbook Air. Shame.:wacko: The frame rate goes up instantly from 35 FPS to 60 FPS. It's very smooth. I think PIXI.js team has done tons of work improving performance of WebGL. But why is Canvas2D performance so bad in comparison?  One other thing I don't understand is I have used "forceCanvas: true" in my Application setting, how come turning on WebGL gives a performance boost to my game?

Having said that I still hope to develop my game in Canvas2D just because other io games can do it without client performance issue.

Link to comment
Share on other sites

Just google "Why is WebGL faster than Canvas"

WebGL gives PIXI direct access to the GPU, and as such it can choose how to batch up certain commands for the GPU to run in an efficient manner. A simple example being, if you are drawing the same sprite over and over, you don't need to keep telling the GPU the details of the sprite every time, you just say here's your current texture, now draw here, here, here, here and here. Therefore WebGL is very very quick at drawing the same texture multiple times at once. Other tricks like masking and filters can be done totally on the GPU too.

The Canvas API does not give you direct access to the GPU. You are using a general purpose simple API provided by the browser; every command you make will use some CPU to use this API, and it cannot optimize how to send the draw commands to the GPU. In the above example, each draw call is it's own thing; the batching up of super fast drawing of the same texture cannot occur. In a large generalization, the way to get performance up in the Canvas renderer is to make less draw calls.

So, your game WILL ALWAYS be slower in Canvas renderer than WebGL renderer, and there's nothing you can do about that. It's not unreasonable to have a 2 code paths on occasions, one for clients using WebGL, and one for using Canvas. The WebGL version has all the bells and whistles and is the actual true vision of the game. Canvas mode (for me at least) is 'let the player play and experience the game', which comes at the cost of lower resolution textures, lower resolution game, less (or no) fancy effects.

Other io games are doing this because they're written their games to perform well on the Canvas renderer. It's like making a game for a console. If you write a game targetted at a PS4, then run it on a PS3, then it's not going to perform well. You often have to tweak the game vision to match the hardware you are targetting.

 

 

 

Link to comment
Share on other sites

The other trick for Canvas Renderer is to pre-render whole chunks of foods (256x256 pixels) into RenderTexture's (may be overlapping) and render them instead. Its what i'm doing for tiles in my bomber.

Just map which food goes in which texture, maintain only a few of them at one time, dont try to make renderTextures to cover all the map, just 2x from camera is enough.

Unfortunately, I cannot supply you with code. That's something you need to figure out on your own, its not about PIXI API, its all about tricks for specific game.

Link to comment
Share on other sites

@caymanbruce In that case its doesnt matter whether you use PIXI API or pure canvas2d,  you have to make some tricks with caching whole parts of the map. The idea is to reduce the number drawcalls by sacrificing memory.

Also I failed to bring those optimizations on mobile, my io-game is still desktop-only.

Link to comment
Share on other sites

Also maybe worth considering that (according to https://webglstats.com/) WebGL support is up to 96%. So maybe optimizing for WebGL would lead to better performance for almost everyone - I.E unless most of your players are in the 4% without WebGL support then forcing Canvas rendering for everyone might be counterproductive. Also you could remind people to enable WebGL and use a supporting browser whenever Pixi is unable to create the WebGL renderer, to help reach as much of the WebGL users as possible.. Anyway I'm still going back and forth about supporting Canvas rendering or not, for my own game, but that chart makes me less inclined to bother TBH, so thought I'd share as more food for thought..

Link to comment
Share on other sites

12 hours ago, themoonrat said:

The documented setting for forcing the canvas renderer works when creating a pixi application or auto detect renderer. If it's not working for you, you're not using the setting correctly! :)

I use forceCanvas like this, am I missing something?

global.gameWidth = window.innerWidth;
global.gameHeight = window.innerHeight;

const renderOptions = {
	backgroundColor : 0x00213C,
	autoResize: true,
	forceCanvas: true,
	resolution: window.devicePixelRatio
};

const app = new Application(0, 0, renderOptions);
app.renderer.autoResize = true;
app.renderer.view.style.position = "absolute";
app.renderer.view.style.display = "block";

app.renderer.resize(global.gameWidth, global.gameHeight);

document.body.appendChild(app.renderer.view);

 

Link to comment
Share on other sites

18 hours ago, ivan.popelyshev said:

@caymanbruce In that case its doesnt matter whether you use PIXI API or pure canvas2d,  you have to make some tricks with caching whole parts of the map. The idea is to reduce the number drawcalls by sacrificing memory.

Also I failed to bring those optimizations on mobile, my io-game is still desktop-only.

I have tried using RenderTextures on simple PIXI.Graphics but still get the performance hit. I think it's time for me to use some tricks. But can you shed some light on me about the caching tricks? I have tried "CacheAsBitMap" but not sure if I need to use that every frame because the foods can disappear and respawn every frame. Using it every frame may also be costly. And I have read that using off-screen canvas object can improve performance. But I have already used that in creating sprites. I have read that using integer positions performs better than floating point positions, so I have tried using "roundPixels: true" but it has no effect on anything. I tried using Math.floor on the positions but the movement, especially rotations are ugly. I have read that instantiating less objects will do the trick but all I have is a container with a dozen of sprites which are created before animation start. I can't find anything more useful at the moment.

Link to comment
Share on other sites

11 hours ago, caymanbruce said:

@Jinz

i have also thought about that, forcing all users to enable webGL. but many users are not aware about what webGL is and some browsers might not enable webGl by default. I am a little worry about my game will not reach many of those less tech-savvy people.

That could be helped by teaching people about enabling WebGL - whenever Pixi is unable to create the WebGL renderer for example.

TBH I haven't been able to figure which would reach more of the casual mmo gamers thou, doing just Canvas optimizations or doing just WebGL optimizations. I suppose if most of the IO games that successfully reach the target audience are skipping WebGL that's a good indicator, but I just don't understand why that's the successful approach..

Link to comment
Share on other sites

You can confirm canvas renderer like this for instance:

console.log(app.renderer instanceof PIXI.CanvasRenderer ? "Canvas" : "WebGL");

Also for the latest API version options is the first and only parameter for the Application constructor so you don't need to pass (0, 0, options) just (options): Doc

UPD: oh yeah;) Pixi already logs the renderer type for you but are sure it doesn't log "Canvas" when you use forceCanvas:true option?

Link to comment
Share on other sites

6 hours ago, caymanbruce said:

I have tried using RenderTextures on simple PIXI.Graphics but still get the performance hit.

How many are you rendering to each RenderTexture? The trick is to render many DisplayObjects to the RenderTexture, so that it will only take one draw call per-animation-frame to draw them, and then at a slower rate you can update the DisplayObjects and re-render them to the RenderTexture. But like's been said there's memory limitations to consider too..

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.

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...
 Share

  • Recently Browsing   0 members

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