I need to generate texture atlases and sprites at runtime, and I’m finding the bottleneck is Sprite.Create.
So, for instance, in one case I create 10 texture atlases, send the bits to them, and create 80 sprites (in total) from them. My timing info:
0.20ms for creating the 10 textures (in total)
0.08ms for LoadRawTextureData/Apply for those 10 textures (in total), 173KB total.
14.3ms for SpriteCreate for 80 sprites (in total).
That last time is really surprising to me. I’m passing SpriteMeshType.FullRect to Sprite.Create, so it shouldn’t need to inspect the texture bits at all, right?
Is there any way to speed this up? What is Sprite.Create doing under the covers to take so much time? It seems like it should just be storing a rect and some other metadata in some internal structure.
Ok, to answer my own question, it looks like Unity is retrieving the texture pixels to calculate the sprite outline. Callstack for my call to Sprite.Create with SpriteMeshType.FullRect.
It doesn’t seem like it should be doing this. Maybe this is fixed in Unity 2018? (I’m using Unity 2017.4.1f1).
Bump. I’d like to see this fixed too if possible. Sprite.Create causes my game to hang for a second if I call it dozens of times in a loop (Unity 2019.2.9). This happens when each player spawns their character in the pre-game lobby, I create their sprites at this point for mod support.
It’s kind of tangled up with my project and customized for it (so there’s extra stuff in here that you don’t need), but these are the two core files I’m using. The MeshRenderer/mesh used is just a default unity quad.
You need to construct FakeSprite by providing an atlas Texture, and a rect/pivot for the sprite in that atlas.
It seems that you are generating a Tight mesh for the Sprite? When the Sprite is created, this will attempt to generate a mesh based on the outline of the Sprite which involves reading the pixels of the texture. If the SpriteMeshType.FullRect is used instead, it will not do so and save time in generating the Sprite. However, this will increase the fill rate during rendering.
Oh thanks, didn’t know that. But I couldn’t verify a change in rect. I tried comparing fullRect vs tight but they’re printing out the same rect (the sprite slices that I feed in have plenty of transparency so I was expecting tight to produce a tighter rect):
Sorry for not replying to this issue! This has been fixed in 2017.4.2f2, where the fallback physics shape was always generated, resulting in the pixel reading (first improvement line). If FullRect is specified without further parameters (the generatePhysicsShape parameter will be False by default but you can specify it to be False if you want), there will not be any pixel reading.
even though it will have lesser performance while rendering in the scene? What if the sprite Im creating has the exact pixel bounds of the rect? Is it strictly superior in that case?
Yes, Sprite creation will be faster as using FullRect will use directly use the rect of the Sprite as the mesh for rendering, instead of reading the outline of the Sprite from its texture and tessellating that to generate the mesh for rendering for Tight.
This generally has better performance for Sprites whose rect includes a lot of empty space, as this will reduce the amount of overdraw because Sprites are considered as transparent geometry.
If the Sprite you are creating has the same outline as the rect, then the generation from using Tight will produce the same results as if you were using FullRect. If you know this beforehand, using FullRect will be better, since it will produce the same result without needing to read the outline of the Sprite and tessellate that. Since the created mesh will be the same, the rendering performance will be equal in both cases.
Do take note that the outline detection is based on the alpha values of the pixels in the texture, so there may be cases where your Sprites may appear to take up the entire rect visually, but may not be the case for outline generation.