Designing an asset loading system

Started by
9 comments, last by kd7tck 11 years, 3 months ago

So I'm working on my first hobby game where I have to implement a proper asset loading system, in my previous projects I have had no problem fitting everything in memory at once. In this, I have estimated that VRAM will most likely be a limiting factor that makes that approach impossible. Though fitting it all in system RAM should not be an issue.

Now, I do not need every art asset loaded at once of course, and determining and limiting what needs to be loaded and what does not is not really what I wonder here. Instead I'm unsure on how to go about designing a proper asset loading system.

While I try to eliminate loading screens, beyond an initial, altogether by being smart about what is loaded and what is not the game still nicely needs to handle the sudden need for an unloaded art asset. Perhaps an object that requires it is suddenly spawned by an in-game event. The way I would like to handle this from the users perspective is to simply pause the game until it is loaded and pop up a "Loading" text on the screen.

I have two possible implementations in mind for this:

1. Finish executing the current frame, render the game state with the "Loading" text and do not do another update until the asset(s) are done loading asynchoniously. This does pose the limitation that any assets cannot be used in the frame they are requested as they may not have loaded yet, which can be an issue as the heaviest of them are both used for graphical and gameplay data.

2. Have the game-loop execute in a seperate thread, and pause that thread whenever an unloaded asset is used. The seperate rendering thread needs to handle this correctly (note that I already have a seperate rendering thread). This should pose few issues, but feels overly complicated for such a task.

So how do games usually do this?

Finally, I'm wondering if it is a good idea to have each State (ie startup menu, game running and the like) provide a seperate "loading state" that they enter when they want to show the user that they are loading something? It feels like it should not be their concern, but each state may need to show different loading screens and at least one of the different "loading screens" will need graphical information for the current game state.

Advertisement

How I Would Do It:

Don't load everything into ram at once, bad idea.

Instead convert all assets to raw format and store in a compressed archive system for latter access.

Then using a virtual filesystem linked to your archive system, load each asset only when you need it.

Physfs is great for this, check it out.

I don't think giving you thousand of lines of code will help you, might only confuse you more. Instead you should impliment your own system, that way you learn what is going on.

In regards to your comment on state loading, that is what most commercial games do.

If you store all data in raw format you will not need another synchronise loading thread. The time it takes to load raw data is insignificant on modern systems, and one thread should do.

I should add that a lot of the data is algorithimcally generated at runtime, in some cases the parameters used by the fairly time-consuming algorithm is not known until the last moment.

Just remember that in a majority of cases anything created at run time that is rendered to a texture (via ogl or dx) will need to run on the main thread or you will get intermittent errors.

-Aeramor

CTO at Conjecture, Inc.

Instead I'm unsure on how to go about designing a proper asset loading system.

...

The way I would like to handle this from the users perspective is to simply pause the game until it is loaded and pop up a "Loading" text on the screen.

I don't think it's 'proper' at all. This means once in a while players will have to stop and wait for disk asset to load, looking at "Loading" on the screen all the time. If this happens more than once during the game, I'd start cursing.

"Jump jump action! LOADING...FUCK! Action again! Shoot! Dodge! LOADING...#!@$!"

You might say, "but oh it's quick so user probably won't even see it". Don't assume so. File IO is one of the slowest IO operation. If user's computer is set to sleep the hard-drive after 5 minutes, then it has to wait until the hard drive spins again. What was a 10ms operation before becomes 5000ms.

What's wrong with loading screens?

What's wrong with loading screens?

Wont really work well at all for the game I'm designing. As it is a space-strategy game with a fair bit of ground action, and as the gameplay is planned showing loading screens at any time except game startup would very much interupt the flow of the game. The planets themselves need a fair bit of detail due to the zoom allowed, enough that I cannot fit all of them inte video memory except on high-end vide cards. Fitting them all into system RAM is both trivial and required, as they affect gameplay as well as their apperance.

Having low-resolution versions of them, for display purposes, makes it possible to view them all at a high level of zoom. Selectily loading them into VRAM as the player zooms in on a region is fairly straighforward and any latency from that can be hidden. It is rather the sudden unanticipated need for new data (ie, some event suddenly creates a new planet somewhere and it's height and terrainmap needs to be generated with an algorithm) that would prompt the "Loading" text, and given that there will be very few instances where the game cannot predict what data it will need ahead of time the player will only very rarely see the "Loading" text at all.

The regular textures that represent plain images will be few in number and most fairly low in resolution.

[quote name='PAndersson' timestamp='1357167726' post='5016884']
As it is a space-strategy game with a fair bit of ground action, and as the gameplay is planned showing loading screens at any time except game startup would very much interupt the flow of the game.
[/quote]

But this is exactly what you were trying to do. Having the word "Loading.." with the background of the game is not much different than a designated Loading screen. It becomes the loading screens.

It is rather the sudden unanticipated need for new data (ie, some event suddenly creates a new planet somewhere and it's height and terrainmap needs to be generated with an algorithm) that would prompt the "Loading" text, and given that there will be very few instances where the game cannot predict what data it will need ahead of time the player will only very rarely see the "Loading" text at all.

[/quote]

Depending on these unanticipated events, I'd try to hide the loading using some kind of animation. For example, if a new planet is born, instead of showing "Loading...", show a specialized screen, like "Dam da dum dum! A new planet is born!! Explore it for new resources!" showing some quick animation that users can't cancel out -- during which the loading takes place.

I have two possible implementations in mind for this:

1. Finish executing the current frame, render the game state with the "Loading" text and do not do another update until the asset(s) are done loading asynchoniously. This does pose the limitation that any assets cannot be used in the frame they are requested as they may not have loaded yet, which can be an issue as the heaviest of them are both used for graphical and gameplay data.
That is the traditional way of doing it. It's very good and safe. And by "safe" it means that memory allocation, deallocation, file I/O becomes centralized in one place (after/before rendering) rather than being sparsed all over the code.
If anything can be loaded anywhere, anytime, it's really hard to reproduce bugs, crashes, and you may probably be left with a few dangling pointers.
A good rule is to make everything valid during frame update (i.e. accessing an asset scheduled for unload won't hit a dangling pointer and you don't have to check for "null" ptrs every time) and manage creation destroys at the end/start of the frame.
2. Have the game-loop execute in a seperate thread, and pause that thread whenever an unloaded asset is used. The seperate rendering thread needs to handle this correctly (note that I already have a seperate rendering thread). This should pose few issues, but feels overly complicated for such a task.
That sounds awfully full of locks which means slow as hell.
If you want to do background loading (which is a good way to rule out loading screens), you weren't however that far from the truth:
As in the first method, you should request loading/unloads during frame time and tag that resources as "requested", then handle those requests outside the frame update. But this time, just do one lock and send those requests to the thread that handles loading/unloading.

When the bg thread finishes loading, the main thread will be informed when it finishes the frame and locks the mutext to send & receive data with that bg thread. The main thread will now tag the asset as "loaded" and can be used.

While the main thread tries to access a resource not yet been loaded, it should ignore it if possible (i.e. Graphics-related stuff, like a mesh not yet loaded) unless it's critical for the logic or physics (ie. terrain collision data). If that happens, then the main thread should stall until it is done (while checking for bg thread's response). You may want to load all data that could cause a stall at the beggining though.

Btw, this system could be extended (i.e. handling partially loaded data, like loading "LOD3" before "LOD0" and display that one until the highest quality is up; also handle "abort" to stop loading some resources) but I'll leave that for you to figgure.

Very important: Note that you shouldn't be requesting assets as soon as you need it. No background loading system can cope with that. You should request when you're reasonably sure you're gonna need them.
Sometimes this can be tied to gameplay (i.e. force the player to walk through a narrow corridor that leads to the next area, and he can't go back)

I didn't read everyones reply, so sorry if this has already been said.

The way i've done this in the past is to have a game thread and a load boss thread and a number of load worker threads.

The game thread queues up assets ids that need to be loaded (newly visible stuff in an oct-tree), then passes that queue over to the load boss.

The boss thread knows how many worker threads are available, and gives each worker a thing to load. When the thing is loaded, the worker thread

signals the boss thread, who gives the worker the next thing in the queue. Once the entire queue is processed and all worker threads are asleep the loader thread lets the game thread know that it is safe to proceed.

How much loading time you have will greatly depend on how large the chunks you request are. Take a look at "Jack and Daxter", the whole world is constantly streaming in and out. Now, there are some edge cases where you can run into an area that causes another are to be visible and end up in a situation where the game needs to load; when that happens jack trips, falls and gets up. This animation is enough to load in the missing assets and is pretty damn funny. If that had been a "Please wait, loading" screen, the whole thing would have been a lot more frustrating.

If loading the asset is going to take...

  • less than 10 ms, you can probably get away with loading it in the main game thread if you don't have to load more than one at a time.
  • more than 10ms, I'd make it a background process to prevent the game from stalling up.
  • more than 1 second, I'd try to predict when the asset is going to be needed just ahead of time and pre-load it to reduce latency.
  • more than 5 seconds, you probably need to rethink how you're storing your data or use fast loading imposters until the data is available.

From my personal experience, unless I trigger a level transition, I get annoyed waiting for an unsolicited "Loading..." screen.

[size="2"]Currently working on an open world survival RPG - For info check out my Development blog:[size="2"] ByteWrangler

This topic is closed to new replies.

Advertisement