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").
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:
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.
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:
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.