Page 1 of 2

Best way to cache/preload images and other assets for later use?

Posted: Mon Nov 20, 2017 7:01 am
by therektafire
Hello everyone :) Everyone knows that loading your assets ahead of time at the beginning of the game is usually preferable to loading them only when they are needed (unless your game is really large and memory hungry or there's some other particular reason why you can't do that) but what I am wondering is what is the best way to do that in Love? Right now i am only taking care of image loading since I'm not particularly experienced at game dev in general and don't know how to make and parse level/save data/etc formats yet...

So for loading and working with images/sprites/etc I made a little module called "ghelp" (stands for "graphics help" of course :]) and one of the functions is a preload function that creates a table of images by first getting the names of all the files in a specific folder that contains the images (in my case "img/", puts them in a table called "files", then a new table called "images" is created and for each entry in the "files" table newImage is called to import the image for use in the game, with the name of the file used as the key and the extension removed to make it easier to write (so I can use, for example, images.foo instead of images["foo.png"]). Here is the code for that:

Code: Select all

preloadimgs = function()
        local dir = "img/"
        local files = love.filesystem.getDirectoryItems(dir)
        local images = {}
        local k = ""
        for i = 1, #files, 1 do 
            k = files[i]
            images[k:gsub("%p%a%a%a", "")] = love.graphics.newImage(dir..files[i])
        end
        return images
    end
    
Then at the top of the main.lua I import the ghelp module via require and call this function to create a local script-scope table with all the images in it.

My question is is this the correct way to do this kind of thing or are there better ways? Also would this way work for other kinds of assets or just images?

Re: Best way to cache/preload images and other assets for later use?

Posted: Mon Nov 20, 2017 7:45 am
by ivan
Generally, it depends on the size and number of assets you are working with.
Mobile platforms in particular don't have a lot of RAM.
For small games you can preload everything at the start and not worry about it.
If your assets are small but numerous, then it's fine to just load them "on demand".
For larger games, you may need to load/unload between levels.
I use simple ref counting to avoid reloading assets that persist between levels.

Re: Best way to cache/preload images and other assets for later use?

Posted: Mon Nov 20, 2017 1:06 pm
by zorg
Also, loading assets takes time, of course, but having data move from your storage mediums to the memory also blocks the thread you're doing that in; if you're loading many or big assets, there may be hiccups along the way; in those cases (after profiling whether this does affect you badly enough) would i go for a threaded asset loader approach; basically load the data into memory in another thread, and when it's done, signal back to the main thread; in the meantime, the main thread can just check the signal variable each cycle, and continue processing graphics, updating the world, animations, etc. while the data loads, giving you a more seamless game experience.

Re: Best way to cache/preload images and other assets for later use?

Posted: Mon Nov 20, 2017 3:39 pm
by ivan
zorg wrote: Mon Nov 20, 2017 1:06 pm Also, loading assets takes time, of course, but having data move from your storage mediums to the memory also blocks the thread you're doing that in
Or, if you're lazy like me you can just show a splash screen.
Works fine as long as there is nothing moving on the screen and the thread is not blocked for more than a few seconds.

Re: Best way to cache/preload images and other assets for later use?

Posted: Mon Nov 20, 2017 8:55 pm
by grump
ivan wrote: Mon Nov 20, 2017 3:39 pmWorks fine as long as there is nothing moving on the screen and the thread is not blocked for more than a few seconds.
Coroutines are another possibility to prevent blocking. I tried coroutines when I realized rendering took a while and I needed to display progress in my font rendering tool. It was very easy and painless to implement, even as an afterthought, with just a few lines of code.

Re: Best way to cache/preload images and other assets for later use?

Posted: Wed Nov 22, 2017 7:31 am
by zorg
grump wrote: Mon Nov 20, 2017 8:55 pm Coroutines are another possibility to prevent blocking. I tried coroutines when I realized rendering took a while and I needed to display progress in my font rendering tool. It was very easy and painless to implement, even as an afterthought, with just a few lines of code.
Coroutines are fine if the granularity of operations you have control over (i.e. lua commands and such) are small enough; io will still block a thread whether or not you use coroutines, if you want to read in a significantly large part of data (or if your HDD/etc is sufficiently slow/ has relatively gigantic seek times) - coroutines are serial in nature, they just allow "jumping around" the code more.

Re: Best way to cache/preload images and other assets for later use?

Posted: Wed Nov 22, 2017 10:06 pm
by grump
zorg wrote: Wed Nov 22, 2017 7:31 am Coroutines are fine if the granularity of operations you have control over (i.e. lua commands and such) are small enough; io will still block a thread whether or not you use coroutines, if you want to read in a significantly large part of data (or if your HDD/etc is sufficiently slow/ has relatively gigantic seek times) - coroutines are serial in nature, they just allow "jumping around" the code more.
You're not wrong, but for OP's use case, loading a bunch of image files at the start of the program, coroutines are totally fine. Unless you want to preload a single 500 MB video file and/or want the program to remain fully interactive during loading. A worker thread might be the better idea in that case.

I wrote a lot of stuff for a lot of large and small systems, and I've never had to load an atomic chunk of data so big that blocking became a problem that needed to be solved with threads. Not even when loading from optical media. It was always UX considerations (i.e. the smoothly spinning loading icon is a MUST HAVE) that led to threads for loading, never technical reasons (i.e. the OS deemed the program unresponsive). For local data, that is. Sockets are another story.

Re: Best way to cache/preload images and other assets for later use?

Posted: Wed Nov 22, 2017 11:18 pm
by ivan
I can confirm from experience that Zorg is correct.
Something like love.graphics.newImage is always going to block the thread (for large images).
Best case scenario, the frame rate will be choppy, especially if the assets you are loading vary in size.
There are ways to make it work using coroutines, if you read image by chunks and later pass it to ImageData,
but then you might as well use threads.

Re: Best way to cache/preload images and other assets for later use?

Posted: Wed Nov 22, 2017 11:31 pm
by therektafire
Well loading times or low framerate while loading stuff probably won't be an issue for me right now since I am just making small non release games right now to get some experience since I only have slightly above 0 as it currently stands :D But I will take the advice about coroutines into consideration. Honestly I will probably have more trouble making things like menus/splash screens/load screens/other game state dependent things

Re: Best way to cache/preload images and other assets for later use?

Posted: Wed Nov 22, 2017 11:52 pm
by grump
ivan wrote: Wed Nov 22, 2017 11:18 pm Best case scenario, the frame rate will be choppy, especially if the assets you are loading vary in size.
OP is specifically asking about loading a bunch of images at the start of the program, not about loading in the background. Frame rate is irrelevant.
therektafire wrote: Wed Nov 22, 2017 11:31 pm Well loading times or low framerate while loading stuff probably won't be an issue for me right now
You just need to care about the OS assuming your program froze because it loads assets for a while. Coroutines are an easy way to make sure that doesn't happen. Threading is the cleaner and more flexible solution, but also much more complicated to implement, as opposed to 5 lines of code for a coroutine.