Pixi.js: Out of Memory when repeatedly generating textures?

Created on 2 Dec 2015  路  9Comments  路  Source: pixijs/pixi.js

I've started to toy a bit with pixi.js and got puzzled when I changed an example and ran out of memory.

Essentially, I call

if (graphics_texture) graphics_texture.destroy();
graphics_texture = graphics_mask.generateTexture();

on every frame.

Now bear with me - I know this is not an efficient way, and shouldn't be done like this, but from my understanding, I should not run out of memory here.
This is the full code - it's http://pixijs.github.io/examples/index.html?s=demos&f=masking.js&title=Masking&v=v3.0.8 with an alpha mask.

var renderer = PIXI.autoDetectRenderer(800, 600, { antialias: true });
document.body.appendChild(renderer.view);

// create the root of the scene graph
var stage = new PIXI.Container();

stage.interactive = true;

var bg = PIXI.Sprite.fromImage('_assets/BGrotate.jpg');

bg.anchor.x = 0.5;
bg.anchor.y = 0.5;

bg.position.x = renderer.width / 2;
bg.position.y = renderer.height / 2;

stage.addChild(bg);

var container = new PIXI.Container();
container.position.x = renderer.width / 2;
container.position.y = renderer.height / 2;

// add a bunch of sprites

var bgFront = PIXI.Sprite.fromImage('_assets/SceneRotate.jpg');
bgFront.anchor.x = 0.5;
bgFront.anchor.y = 0.5;

container.addChild(bgFront);

var light2 = PIXI.Sprite.fromImage('_assets/LightRotate2.png');
light2.anchor.x = 0.5;
light2.anchor.y = 0.5;
container.addChild(light2);

var light1 = PIXI.Sprite.fromImage('_assets/LightRotate1.png');
light1.anchor.x = 0.5;
light1.anchor.y = 0.5;
container.addChild(light1);

var panda =  PIXI.Sprite.fromImage('_assets/panda.png');
panda.anchor.x = 0.5;
panda.anchor.y = 0.5;

container.addChild(panda);

stage.addChild(container);

// let's create a moving shape
var mask = new PIXI.Sprite();
stage.addChild(mask);
mask.position.x = renderer.width / 2;
mask.position.y = renderer.height / 2;

// CHANGE A //

mask.anchor.x = 0.5;
mask.anchor.y = 0.5;

var graphics_mask = new PIXI.Graphics()
graphics_mask.lineStyle(0);
graphics_mask.clear();
graphics_mask.beginFill(0x8bc5ff, 0.4);
graphics_mask.moveTo(-120, -100);
graphics_mask.lineTo(120, -100);
graphics_mask.lineTo(120, 100);
graphics_mask.lineTo(-120, 100);
graphics_mask.lineTo(-120, -100);
var graphics_texture = graphics_mask.generateTexture();


container.mask = mask;

var count = 0;

stage.on('click', onClick);
stage.on('tap', onClick);

function onClick()
{
    if(!container.mask)
    {
        container.mask = mask;
    }
    else
    {
        container.mask = null;
    }
}

var help = new PIXI.Text('Click to turn masking on / off.', { font:'bold 12pt Arial', fill: 'white' });
help.position.y = renderer.height - 26;
help.position.x = 10;
stage.addChild(help);

animate();

function animate()
{
    bg.rotation += 0.01;
    bgFront.rotation -= 0.01;

    light1.rotation += 0.02;
    light2.rotation += 0.01;

    panda.scale.x = 1 + Math.sin(count) * 0.04;
    panda.scale.y = 1 + Math.cos(count) * 0.04;

    // CHANGE B //
    if (graphics_texture) graphics_texture.destroy();
    graphics_texture = graphics_mask.generateTexture();

    mask.texture = graphics_texture;
    count += 0.1;

    mask.rotation = count * 0.1;

    renderer.render(stage);
    requestAnimationFrame(animate);
}

If you run the code, chrome's memory usage rapidly increases, until it runs out of memory. Can someone explain what I do wrong? Even if I tax the CPU, my memory usage should remain neutral (or at least not increase until WebGL "runs into a snag").

Most helpful comment

Hi! I'm a developer too. :D Just like you! I know that developers are proud, and that a large danger we often run into is that we think of ourselves being flawless, and dismissing confused users as nubs that should RTFM. So, knowing that, I try the best to avoid that.

With that said, let me retrace my steps - in hopes that it will help this nice engine and improve it further:

  • I saw dozens of engines on html5gameengine.com, and tried to figure out which one i could possibly use for my projects in the next years.
  • I checked out PixiJS, saw the masks example, and wondered "can it do alpha masks?".
  • I saw the example with alpha masks, wondered why they didn't work in the first example, and while I didn't see anything explicit, I saw a forum post saying that Graphics uses Stencil and are faster, and Sprites use WebGL. So I figured that may be the reason. I set off to change your sample with the teddy bear to use a semi-transparent mask.
  • I realize I can't draw on a Sprite object directly, and that there is no Graphics object on it, like in Flash.
  • I see the generateTexture() method on the Graphics object - and an obsolete post with SetTexture() on the Sprite object. Nowhere in these steps does it say that it is absolutely necessary to call destroy() on Texture to avoid a leak.
  • I run into the memory leak by tossing away the Textures.
  • I try removing references. I see an example using Texture.destroy(). I figure: that must be it. Call it, no effect. Hence my post here.

A critical piece of information (if you do not destroy this, and toss this object away, you have a memory leak 100% of the time) hidden deep in the documentation, where the leak is "implied", does feel like a pitfall. It also feels surprising, with a garbage collected language. And that there is a destroy() and a reallyDestroy(). Now I know that I'm interfacing directly with WebGL and I need to be careful - thanks to JiDW.

Please note I don't want to be rude. But I find it sad to be immediately told to "RTFM" by the owner, after a post from JiDW that essentially welcomed me into the pixiJS community. I hope that through my post you can retrace why I ran into the situation, and I hope you understand that when checking out a lot of different engines I did not have the time to completely read the entire API from A to Z just yet.

I also don't mind reporting such pitfalls, if it helps the engine or its documentation.

All 9 comments

Haven't tested or read with attention so sorry if I'm wrong but probably :

graphics_texture.destroy(true);

instead of

graphics_texture.destroy();

To destroy the base texture.

You are completely correct, when I add "true", memory remains stable. I thought that garbage collection would take care of this, and didn't think it was necessary to pass "true".

Another (related) question - it seems like I fell into a beginner's trap. Are there any others to look out for? Or any resources that tells pixijs newbies what to look out for?

For example, Flash has a lot of "traps" - such as "avoid calling functions", "avoid creating objects, reuse them as much as possible" - things that work, but that will get you into performance problems quickly. pixijs seems to have a few there too (not calling "destroy" with true when you're done with a texture). Is there any place I can start and avoid most/all the little pitfalls?

It has more to do with knowing (a bit of) what OpenGL is than PIXI.

For exemple, there is no GC in OpenGL/WebGL, you are in charge of the memory. You are dealing with a graphic library (PIXI) on top of another graphic library (opengl) that is a lot more low level than Javascript as it speaks directly to your GPU.

Fortunatly, PIXI deals with WebGL for you, so the only thing you should keep in mind is that what's easy/good in canvas 2d may not be easy/good in WebGL.

It's really easy to draw a text in canvas 2D, it's not easy in WebGL. It's very quick to masks elements in canvas 2D, it can be a performance bottleneck in webGL. So just keep an open mind and test things, asks questions on the PIXI forum when you need help and that should be enough most of the time !

Many thanks. I'm comfortable managing my own memory, I just thought this was abstracted from me.
I'll keep that in mind and try to get into the PIXI forum. Thank you for your quick responses!

Another (related) question - it seems like I fell into a beginner's trap. Are there any others to look out for? Or any resources that tells pixijs newbies what to look out for?

You could read the docs:

http://pixijs.github.io/docs/PIXI.Texture.html#destroy

Which inform you that the first param is whether or not to destroy the base texture.

For example, Flash has a lot of "traps" - such as "avoid calling functions", "avoid creating objects, reuse them as much as possible"

This has nothing to do with Flash, and everything to do with JavaScript (or AS as it may be). That statement you made is true for any garbage collected language (for the most part). Avoiding function overhead is true for any low-latency program.

pixijs seems to have a few there too (not calling "destroy" with true when you're done with a texture). Is there any place I can start and avoid most/all the little pitfalls?

I don't see this as a pitfall, but just a lack of information about the API. Reading the docs for functions you use will clear this up.

Many thanks. I'm comfortable managing my own memory, I just thought this was abstracted from me.

JavaScript abstracts (kinda) memory management in the JS heap. However, the WebGL memory space is managed manually. Therefore we have the destroy methods to clean that up, and at the same time remove any references that might prevent the JS GC from cleaning up heap as well.

Hi! I'm a developer too. :D Just like you! I know that developers are proud, and that a large danger we often run into is that we think of ourselves being flawless, and dismissing confused users as nubs that should RTFM. So, knowing that, I try the best to avoid that.

With that said, let me retrace my steps - in hopes that it will help this nice engine and improve it further:

  • I saw dozens of engines on html5gameengine.com, and tried to figure out which one i could possibly use for my projects in the next years.
  • I checked out PixiJS, saw the masks example, and wondered "can it do alpha masks?".
  • I saw the example with alpha masks, wondered why they didn't work in the first example, and while I didn't see anything explicit, I saw a forum post saying that Graphics uses Stencil and are faster, and Sprites use WebGL. So I figured that may be the reason. I set off to change your sample with the teddy bear to use a semi-transparent mask.
  • I realize I can't draw on a Sprite object directly, and that there is no Graphics object on it, like in Flash.
  • I see the generateTexture() method on the Graphics object - and an obsolete post with SetTexture() on the Sprite object. Nowhere in these steps does it say that it is absolutely necessary to call destroy() on Texture to avoid a leak.
  • I run into the memory leak by tossing away the Textures.
  • I try removing references. I see an example using Texture.destroy(). I figure: that must be it. Call it, no effect. Hence my post here.

A critical piece of information (if you do not destroy this, and toss this object away, you have a memory leak 100% of the time) hidden deep in the documentation, where the leak is "implied", does feel like a pitfall. It also feels surprising, with a garbage collected language. And that there is a destroy() and a reallyDestroy(). Now I know that I'm interfacing directly with WebGL and I need to be careful - thanks to JiDW.

Please note I don't want to be rude. But I find it sad to be immediately told to "RTFM" by the owner, after a post from JiDW that essentially welcomed me into the pixiJS community. I hope that through my post you can retrace why I ran into the situation, and I hope you understand that when checking out a lot of different engines I did not have the time to completely read the entire API from A to Z just yet.

I also don't mind reporting such pitfalls, if it helps the engine or its documentation.

I'm sorry you felt like I was dismissing you, I wasn't. I was offering an alternative to your process you just outlined. Instead of seeing .destroy() and guessing "hey maybe I need to call that", I suggest your thought process should be "what does that do, let me investigate". You will have a much better experience integrating any library if you actively try to understand the things you are using, rather than just using code you found in examples. I recommend that if you see any function you don't already know about in any example, you should check the docs to ensure you know how to use it properly. I'm not suggesting to load up the docs and read the entire thing, that is silly. But when you are using a new function for the first time, and you have no idea what it does; read the docs about it before using it.

You asked multiple questions, and I was just answering them; no RTFM, no dismissal intended. The docs _are_ the resources that tell you what to lookout for. "What is this .destroy() thing I saw in an example?" The docs will answer that.

I've started a new Wiki Page titled Tips, Tricks, and Pitfalls where we can collect "pitfalls" like this that users may need to be aware of. I took the liberty of adding the ones you faced here, if you find any others you think will be useful for new users. Please feel free to add them to that page. I expect that each section will include reference links to the documentation for more information about the specific features or functions it mentions.

FWIW I like how you reacted to this @englercj :+1:

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

readygosports picture readygosports  路  3Comments

softshape picture softshape  路  3Comments

courtneyvigo picture courtneyvigo  路  3Comments

lucap86 picture lucap86  路  3Comments

Makio64 picture Makio64  路  3Comments