GIF <-> Spritesheets & Optimizing Garbage Collection

While working on a virtual NFT gallery project, I’ve been recently experimenting with importing GIF’s into Unity and converting them into animated sprites.

I’m currently using Unity Gif Decoder to export each of the frames of GIF files loaded from the web into 2D textures. In order to convert them into an animated sprite and place them in the canvas, I need to convert them into Sprite objects.

I noticed big FPS spikes after decoding the textures when the task was finished every time it loaded a GIF. This didn’t happen at all when loading regular images, even if they were big in size, but this happened to even simple GIF’s. Using the profiler, I was able to pinpoint this to the timeframe when the task finished and it was loaded into the canvas.

As you may know, the GIF format is a bit archaic and can be pretty heavy in size. At first, I thought that the culprit could be loading in all frames at once, so I changed my code to add each frame into the image one by one and start playing the animation with any frames it had. While did this help optimize a bit, I was still getting 600ms+ lag spikes when loading the images. I checked to see how much garbage was generated during this timeframe and I was surprised to see 13MB of garbage getting cleaned.

I was storing each frame from the stream in a variable and generating a new sprite with it. Normally, creating a new sprite wouldn’t generate much garbage, but if a GIF had 120 frames, then that would be 120 sprites done in a single frame. The main issue here was that the async method was attempting to execute during the same frame so this would freeze the game for over half a second when loading one GIF. This would be extremely bad when loading a lot of animated images at once.

The fix for that? Waiting until the next frame. This can be done with awaiting until the end of the frame either with a yield (if using coroutines) or an await (if using tasks.) This makes the GC avoid having to collect all 13MB at once and it also avoids having to manually call it. After adding that line of code, my project was back to running at 200-300 FPS and there were no drops when loading GIF files compared to how it was dropping to 6 FPS when they appeared before.

In the code snippet above, since I loop through all of the frames, I added a wait for end of frame task at the end of each loop. This helps the garbage build up from the new sprites to a minimum per frame and will help a lot once I have a lot more files loading at once in the background.

Hol

Computer System Engineer, Game Dev, and Artist

All author posts