I have particular situation where I need to improve the startup time of a PlayCanvas app that has many texture atlases.
The optimal way we think would work best for us is to create the atlases at runtime dynamically like this:
generate an offline atlas with Texture Packer or Free Texture Packer apps and only use the JSON data inside PC as a preloaded JSON resource
add all the sprite PNGs inside PC as texture resources (streamed not preloaded)
at runtime, based on the offline created atlas info (JSON at pt. 1), we create an empty pc.TextureAtlas of the required size
we then start async loading of sprite PNGs from pt. 2 and when each one is loaded we would like to partially update the texture atlas’s texture with the new sprite at the given location from the JSON info. We then release the sprite texture as it is not needed anymore, it should be available in the atlas.
What this means is that we would need to be able to partially update a pc.Texture. Is that possible?
Looking at the implementation of the pc.Texture I see that one possible way would be to use the pc.Texture.lock() method (taken from the comments there)
* // Fill the texture with a gradient
* var pixels = texture.lock();
* var count = 0;
* for (var i = 0; i < 8; i++) {
* for (var j = 0; j < 8; j++) {
* pixels[count++] = i * 32;
* pixels[count++] = j * 32;
* pixels[count++] = 255;
* }
* }
* texture.unlock();
but looking at the lock implementation it seems like it always recreates a buffer for the whole texture (not to mention that updating pixels one by one in JS might be really slow). What I would need is something like a wrapper over gl.texSubImage2D and I found a call to this in only one place in the engine for updating the mini-stats.
I don’t think that would work, I need to use the atlas even if it only has a single sprite added, your suggestion implies that I draw all the sprites to the canvas and create the atlas as a final step.
I think this is sub-optimal, I was wondering if you guys would accept a PR with API additions (some overloads maybe) to pc.Texture to allow partial updates based on WebGLRenderingContext.texSubImage2D() or WebGLRenderingContext.copyTexSubImage2D() (for in-GPU texture transfer) I think this would benefit PC on the long run. If that’s the case I can work on an implementation and discuss with you guys how that API would best look. @will what’s your take on this?
At the moment I’m working on the cookie texture support for clustered lights, and the way I do it is to generate and update the atlas based on visible lights. I start with an empty atlas like you, and have other textures I need to place into rectangular areas of that atlas.
So I’ve created a RenderTarget for this texture, that allows me to render to it. And I use a simple shader and render individual textures into it.
Here’s my work in progress code, but it’s pretty much done and works well.
Thanks @mvaligursky , I’ll give it a try with this approach and see how that goes. I had a look on how the texture is uploaded inside GraphicsDevice::uploadTexture and also looked inside the Texture implementation and adding partial updates is not that trivial especially if one considers the mipmaps as well. Your suggestion seems straightforward