JAA Posted July 19, 2017 Share Posted July 19, 2017 Simple question. How to cleanup? I want to do this well as I want to destroy every sprite and texture without exiting the window. I then want to create a load more textures and sprites, but some of the textures will be ones that were previously destroyed, so I want to avoid the 'Resource already exists' problem. I have created loads of sprites, and since I need to access them quickly I have them in a quick lookup, so I clean these up by going through each one and using the function destroy. However, I also want to clean up all the textures.They are in PIXI.utils.TextureCache I think. How do I clean these up? Is there any other references to these textures that need cleaning? Are there any other issue I might encounter? Quote Link to comment Share on other sites More sharing options...
yahiko Posted July 19, 2017 Share Posted July 19, 2017 One solution could be to clear your stage like this: stage.removeChildren(); ... And hope the JavaScript GC works as expected Quote Link to comment Share on other sites More sharing options...
xerver Posted July 19, 2017 Share Posted July 19, 2017 You need to call the `destroy` method of sprites and/or textures you are done with. Since you created them, it is your responsibility to destroy them. Simply call `.destroy()` on each of the objects you created. `.destroy(true)` on a Sprite for example will destroy the sprite, the texture it has, and the base texture that texture uses. Where is gets complex is that destroying an object twice is an error, so if you have two sprites sharing a texture and you call `.destroy(true)` on both of them, you may get exceptions. This is why it is generally a good idea to track any resources you create and destroy them manually each. xwz86 1 Quote Link to comment Share on other sites More sharing options...
yahiko Posted July 19, 2017 Share Posted July 19, 2017 Simply removing those sprites from their containers is not enough? Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted July 20, 2017 Share Posted July 20, 2017 The problem is with Text objects, they have their own textures that has to be destroyed. Sprites mostly use the textures from loader. Also there's "PIXI.utils.BaseTextureCache" if you want to know which textures are stored right now in memory. Quote Link to comment Share on other sites More sharing options...
JAA Posted July 20, 2017 Author Share Posted July 20, 2017 Thanks for these replies. It looks a little more complicated than I thought. I create my sprite from a style sheet. So I think this means I cannot just do destroy(true) as several sprites will share the same base texture and get the exception @xerver is talking about. In the sprite's destroy function it says I can pass an object. Since I am creating my sprite from a frame using the following: texture = PIXI.utils.TextureCache["textureId"]); rect = new PIXI.Rectangle(x, y, w, h); textureWithFrame = new PIXI.Texture(texture, rect); Option 1: I am guessing my SPRITE's destroy call needs to look like this: spr.destroy({children: true, texture: true, baseTexture: false}); Is this correct? The 'texture' is the one from the frame (textureWithFrame) so that needs to be destroyed as it is only used in one sprite. baseTexture is the entire spritesheet so cannot be destroyed as several sprites share this. Q: Will all the textureWithFrame be removed from PIXI.utils.TextureCache? How do I cycle through PIXI.utils.TextureCache destroying each element? Option 2: Since all the textures (including the ones like textureWithFrame) are stored in PIXI.utils.TextureCache, then I should destroy my sprite with: spr.destroy(false); Then I need to cycle through PIXI.utils.TextureCache and destroy every texture without worrying at all where it was used. Q: How do I cycle through PIXI.utils.TextureCache destroying each element? So which option is best, Option 1 or Option 2? Or am I completely getting this wrong? Quote Link to comment Share on other sites More sharing options...
xerver Posted July 20, 2017 Share Posted July 20, 2017 19 hours ago, yahiko said: Simply removing those sprites from their containers is not enough? No, Textures will continue to have references in certain internal caches until they are cleaned up (via .destroy usually). Additionally, WebGL memory is not managed. It is up to the programmer to manage the WebGL memory, and therefore you need to tell Pixi when to remove it from GPU mem. 6 hours ago, JAA said: I am guessing my SPRITE's destroy call needs to look like this: spr.destroy({children: true, texture: true, baseTexture: false}); Yes, that will destroy the sprite, it's texture, all children and all their textures; but leave all the base textures alive. 7 hours ago, JAA said: Q: Will all the textureWithFrame be removed from PIXI.utils.TextureCache? How do I cycle through PIXI.utils.TextureCache destroying each element? If it is used by a sprite you called the destroy on above, then yes, otherwise no. You should not be using the TextureCache or the BaseTextureCache. These are private structures and are not meant to be used or interacted with by users (see here and here). Not all objects are in this cache. For example, when constructing a texture with `new` (new PIXI.Texture()), that texture is not in the cache. Looping through the internal private cache to clean up is not a valid resource management strategy. We will remove things from that cache when you call destroy. Instead you should be managing your own resources. When you create a texture, store it somewhere. When you are done with it, call destroy. Same for other pixi objects you create. I generally abstract this concept into a resource manager (which allows me to properly track object lifetimes and implement pooling as necessary). This is why I mentioned it is often best (IMO) to simply call .destroy() with no params so that only this object is destroyed, no chance of destroying twice and only what you want is destroyed. In games I've done in the past I've used a key-based resource storage system where different maps or levels of my game have a key and they use that key to fill the resource manager with objects and then I am done with a level I can clear them all by just saying "mymanager.destroyAll('some_key')". A naive, example of something like this (that I wrote in about 30 seconds) could be: class ResourceContainer { constructor() { this.objects = [[], [], []]; } addResource(type, object) { this.objects[type].push(object); } destroyResources(type) { this._destroy(this.objects[type]); } _destroy(arr) { for (let i = 0; i < arr.length; ++i) { arr[i].destroy(); } arr.length = 0; } } class ResourceManager { constructor() { this.containers = {}; } addResource(contianerKey, type, object) { if (!this.containers[contianerKey]) { this.containers[contianerKey] = new ResourceContainer(); } this.containers[contianerKey].addResource(type, object); } //.. and similar createX() methods createSprite(contianerKey, texture) { const sprite = new PIXI.Sprite(texture); this.addResource(contianerKey, ResourceManager.TYPE.SPRITE, sprite); } destroyAll(contianerKey) { const container = this.containers[contianerKey]; for (const key in ResourceManager.TYPE) { container.destroyResources(ResourceManager.TYPE[key]); } } } ResourceManager.TYPE = { SPRITE: 0, TEXTURE: 1, BASETEXTURE: 2, } Hopefully you can see how this can be expanded into something robust with many different features such as pooling and ref counting, but also should illustrate how you can track all your object creations, assign them an application "scope" (containerKey) and then cleanup scopes as needed. Obviously more work needs to be done to share objects between scopes, etc. I think this is a common enough task that we may include a plugin in v5. But right now you need to think of it like resource allocation in other languages. If you have the ability to create something, it is your responsibility to also clean it up. One final piece of advice: Don't mix Sprite.fromImage() and PIXI.loader. Use the loader or use fromImage, don't do both. You'll just end up confusing yourself and leaking. Good luck! Taz and yahiko 2 Quote Link to comment Share on other sites More sharing options...
JAA Posted July 21, 2017 Author Share Posted July 21, 2017 Thanks for your hints and tips xerver. I had something similar to your ResourceContainer, but I really do like the idea of your ResourceManager and will be using that from now on. One thing I am still confused about, namely ResourceManager.BASETEXTURE in your code. I load these with PIXI.loader. I then fill my own array with these (like your ResourceContainer) ready to destroy. However, how should I destroy these? Should I destroy these with in the loader? Can I just cycle through the array and call destroy on them. If I do destroy using the latter (cycling through my array) will my PIXI.loader be able to reload these, or do I need to call destroy on the PIXI.loader? Quote Link to comment Share on other sites More sharing options...
xerver Posted July 21, 2017 Share Posted July 21, 2017 I generally use the loader, pull out the resources I want and insert them with the "addResource" method above, then call loader.reset() so it drops references (and can be reused to load more stuff later). Then their lifetime is again managed by the resource manager. In fact I usually use my resource manager to load the assets necessary for a given key based on a manifest I bake at build time, so I know what resources each level requires and can just say .loadLevel(key, callback) and then .destroyLevel(key) and move on. Taz 1 Quote Link to comment Share on other sites More sharing options...
JAA Posted July 24, 2017 Author Share Posted July 24, 2017 Thanks xerver. I have tidied up my code and have it working great now. Once I get closer to finishing my game, I will let everyone know so you can all play it. xerver 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.