Minimog Posted August 5, 2020 Share Posted August 5, 2020 I am working on a game that has few background graphics, these are stored in app bundle at big resolutions like 2000x2000 pixels to support bigger devices like iPad pro. Obviously something this big is an overkill for mobile and I wanted to ask if following optimisation assumption is correct: 1. I preload these assets with pixi loader 2. I create sprite for each texture 3. I then resize this sprite to fit device dimensions better i.e. 1000x1000 px for iphone When I inspect my sprites textures I still see them at 2000x2000 pixels even though sprite itself is 1000x1000. I am concerned that I am not optimising this correctly, especially because I use prepare plugin https://pixijs.download/dev/docs/PIXI.Prepare_.html for some of these assets, I upload my sprites not textures, but I still feel like I might be uploading those big 2000x2000 assets alongside which is a problem, as these occupy a lot of GPU memory. Hence this thread to clarify if my approach to this problem is correct. I don't want to create separate asset resolutions i.e. 2x / 3x etc if possible to avoid increasing final app bundle size. Quote Link to comment Share on other sites More sharing options...
Exca Posted August 5, 2020 Share Posted August 5, 2020 Best way is to make the textures in multiple sizes beforehand and then load only the ones that are your wanted size. In theory you could do resizing and dynamic texture generation in client also, but you would suffer qualitywise when compared to doing the resolution changes with software with better scaling algorithms. Anyways if you want to do without uploading large textures to gpu and while still using original sized textures then here's a short way how you could do that: - Create a temp canvas that is you target resolution size. - Get 2d context from it. - Draw your image to that canvas with scaling to correct size. - Create a basetexture that has that canvas as element. - Create your sprite using that basetexture. - Destroy your original image, basetexture and remove it from loader. I do not recommend using this method, but rather look into how you can have separate asset resolutions and load only the correct one. Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 5, 2020 Share Posted August 5, 2020 (edited) > I upload my sprites not textures prepare recursively looks everywhere and searches for textures. Sprite resize wont affect anything, you have to create a renderTexture of appropriate size, render your sprite there, and then use this new texture, and destroy old one. Alternatively: do it manually on canvas2d, and give this canvas to pixi. You can also set width=height=1 for canvas after its uploaded to save the regular memory, but turn off pixi gc so it wont release the texture from videomemory. Both images are in this case are RGBA, 4 bytes per pixel. You can use compressed textures to get 1 byte per pixel but that's special kind of compression, it has downsides that you have to be aware of. Edited August 5, 2020 by ivan.popelyshev Minimog 1 Quote Link to comment Share on other sites More sharing options...
Minimog Posted August 5, 2020 Author Share Posted August 5, 2020 (edited) @ivan.popelyshev Gotcha, so just to confirm that I am cleaning everything up correctly here is a snippet that works for me and resizes everything correctly, am I correct assuming that the only cleanup I need to do here is destroy a loader with original textures? loader.load((_, resources) => { const sprites = {}; Object.keys(resources).forEach(key => { const sprite = new Sprite(resources[key]?.texture); const { width, height } = sprite; // maxWidth is pre-determined beforehand if (width > maxWidth) { const ratio = height / width; const maxHeight = maxWidth * ratio; const renderTexture = RenderTexture.create({ width: maxWidth, height: maxHeight }); sprite.width = maxWidth; sprite.height = maxHeight; renderer.render(sprite, renderTexture); sprites[key] = new Sprite(renderTexture); } else { sprites[key] = sprite; } }); // sprites object is then sent to prepare plugin loader.destroy(); }); Edited August 5, 2020 by Minimog Add js syntax highlighting Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 5, 2020 Share Posted August 5, 2020 you have to destroy each original texture. "texture.destroy(true)" Quote Link to comment Share on other sites More sharing options...
Minimog Posted August 5, 2020 Author Share Posted August 5, 2020 @ivan.popelyshev Alright, I tried to do that after it's been uploaded to GPU i.e following my example above renderer.plugins.prepare.upload(sprite, () => { resources[key]?.texture.destroy(true); }); But this throws various errors related to sprite scale / width etc being undefined. It feels like old texture is still being referenced somewhere. Can this be the case with my previous example above? Quote Link to comment Share on other sites More sharing options...
Minimog Posted August 5, 2020 Author Share Posted August 5, 2020 Just to add to the above, calling reset and destroy methods on loader seems to also work. I looked up sprites in chrome's memory profiler and ones stored there are correct renderTextures Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted August 5, 2020 Share Posted August 5, 2020 You mixed everything and expect it to work. Please debug this thing and remove the unnecessary code. You dont need "prepare" because you are using renderTextures and when you form them - uploading happens automatically. Quote Link to comment Share on other sites More sharing options...
Minimog Posted August 6, 2020 Author Share Posted August 6, 2020 (edited) @ivan.popelyshev You are tight, I didn't even know renderTexture achieved what I wanted to do with prepare. I cleaned up the code and added appropriate destroy methods, could you please give this a final glance to confirm that I understood everything correctly. Only caveat here that I can't figure out when comparing this to prepare plugin, is how can I know when renderTexture is loaded to gpu? Prepare provided a callback for this. Or is renderTexture gpu upload synchronous? loader.load((_, resources) => { const sprites = {}; Object.keys(resources).forEach(key => { const texture = resources[key]?.texture; const sprite = new Sprite(texture); const { width, height } = sprite; const renderTexture = RenderTexture.create(); // Resize image if it is too big for the device if (width > maxWidth) { const ratio = height / width; const maxHeight = maxWidth * ratio; renderTexture.resize(maxWidth, maxHeight); sprite.width = maxWidth; sprite.height = maxHeight; } else { renderTexture.resize(width, height); } renderer.render(sprite, renderTexture); sprites[key] = new Sprite(renderTexture); sprite.destroy(); texture?.destroy(); }); loader.reset(); loader.destroy(); }); Edited August 6, 2020 by Minimog 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.