Jump to content

what might cause 90% of my execution time to be spent in pix's emit function after running the program for awhile?


timetocode
 Share

Recommended Posts

Apologies for what is a very vague question. My game runs fine for quite awhile, but eventually starts to spend 90% of its time (checked via chrome's profiler) in the emit function from pixi. I can't really share the ~18k lines of code, but I only do a few things in pixi so maybe someone knows right away that one of these things is an issue. It is a bit difficult for me to find the issue as it does take quite awhile to appear.

Are any of these things suspects for causing pixi to 'emit' more and more over time?

  1. animating by changing a sprite texture:  this.body.texture = PIXI.Texture.fromFrame('someArt'+frameNumber+'.png')
  2. creating a mask by drawing some shapes to a regular old canvas, having a sprite made from PIXI.Texture.fromCanvas(canvas), and then updating this mask via this.visibilityMask.texture.update() *once every frame*
  3. using a few PIXI.Graphics objects, and redrawing the graphics object from scratch every frame (only a few objects though, less than 10)
  4. updating x, y, rotation, mask, tint of a few objects every frame -- I have a networking layer which is constantly updating the properties of many of the objects.

Any ideas? Sorry for the abstract problem.

The game runs at a solid 60 fps with most of its profiled time spent 'idle' for at least 30 minutes. Going forward I'm just going to comment out large sections of the game and leave it running when I'm done working for the day. Maybe one of these mornings I'll return to find the program without a leak, and then I'll have narrowed down the search.

The problem does appear to be in the game client and not the game server, as opening up fresh clients without restarting the server yields 60 fps again.

Thanks!

Link to comment
Share on other sites

It feels like there's a memory leak. Either a PIXI object that contains an event emitter is not getting destroyed correctly, or listeners are being added but not removed, and so the number of Event Emitter listeners grows and grows.

PIXI objects that contain an Event Emitter that I'd start looking at mostly would be:

Texture, BaseTexture, DisplayObject (which in turn is inherited by Container which is in turn inherited by Graphics, Sprite, Text, BitmapText, Mesh & ParticleContainer)

But these also contain Even Emitters

Ticker, SystemRenderer, TextStyle (if on v4)

I'd be very tempted to put some logging within EventEmitter within the 'EventEmitter.prototype.on' function, and log out the number of listeners for each event when another listener is added.

I'd also be tempted to put logging into the constuctor and destroy method for a DisplayObject, to count out via a global var how many active objects there are. (+1 the total after a constructor is called, -1 when destroy is called).

In one of those cases I'd hope you'd see something abnormal!

Link to comment
Share on other sites

  • 4 weeks later...

The problem, unlikely to affect many people, was in using PIXI.Texture.fromCanvas(canvas) and texture.update() incorrectly.

The leak came from my line-of-sight visualization that casts a roguelike fog-of-war, allowing walls to block player vision.

It worked by doing some complicated geometry to cast rays from the player to all of the corners of all the walls, and calculating triangles that were visible. It then put a black sheet over the entire screen, and 'cut out' the visible area from the sheet using the visibility triangles. This black sheet was created on a regular html5 canvas using compositing functions, and then converted to a sprite via pixi's sprite from canvas stuff. It was then used as a mask over the game to create the effect.

The leak arose from the combination of two things: creating a sprite from the canvas every frame, and calling mySprite.texture.update(). The combination of these two things somehow creates a runaway effect where pixi will 'emit' an event, probably 'onBaseTextureChanged' more and more over time until choking the cpu. I'm not sure if this would be considered a bug, as 'onBaseTextureChanged' does not lead to any change occurring -- it won't in fact change the texture. I do agree that pixi should not automatically change the sprite just because the canvas changes. If it did, then changing the canvas might get really expensive. For a fringe feature like this, I certainly prefer the ability to perform many changes to the canvas and then decide myself when to let pixi send this image to the GPU (or however that actually works, idk).

So this is probably a pretty rare problem, but in case anyone happens to come across this post the solution follows,

To create a sprite that obtains its texture from a canvas, and to be able to draw to the canvas and experience changes in your sprite, do this:

var mySprite = new PIXI.Sprite(PIXI.Texture.fromCanvas(canvas)) // do this once only, and keep the reference to canvas and mySprite

Now you can do whatever you want to the canvas. But you won't see any changes actually occur to mySprite. When you want mySprite to update itself to the state of the canvas, call:

mySprite.texture.update() // mySprite will now look like canvas

That's all you need to do.

If you call 

yourCodeThatDrawsToACanvas(canvas);

mySprite = new PIXI.Sprite(PIXI.Texture.fromCanvas(canvas);

mySprite.texture.update();

every frame as I did, you get a memory leak which when profiled will show all of the execution time being spent in pixi emit.

 

Here's a video on my tumblr that shows the 'shadow effect' if anyone is curious: http://timetocode.tumblr.com/post/144288582221/playing-around-on-the-new-conveyor-belts-before-i. Note: a separate function uses the same line-of-sight triangles to show/hide entities that walk around a corner. Code based on this article: http://www.redblobgames.com/articles/visibility/

 

 

Link to comment
Share on other sites

And that's why EventEmitter is evil. Hear that, @xerver ? :)

Btw, use new BaseTexture(canvas) and not fromCanvas, that way it will be ok. PIXIv4 will gc videomemory after 30s or more of ont doing anything with that texture, and javascript gc will hather your rogue baseTexture. It works just fine in new rpgmaker MV version that's not released yet.

 

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