Handle for asynchronously loaded resources

Started by
7 comments, last by Codarki 12 years, 6 months ago
When I load resources (synchronously) I return a Handle<T>. This is a pointer to the real resource & a pointer to a reference counting variable. A copy of the handle is returned if the resource is requested again from another place.
For asynchronous resource loading I considered adding a second layer of indirection inside the handle. The idea is to be able to use many copies of the handle in different places immediately and place the actual resource "under" them once loading has finished. Of course I still had to check for null if I wasn't sure whether the resource has loaded yet.
What do you think of this? Does it seem like a bad idea to always carry around that double level of indirection even for stuff that isn't even asynchronously loaded?
Advertisement
It all depends.

If you have tens of thousands of assets in flight in a given gameplay session, with textures and models and shaders and animations and any number of other resources being loaded through this mechanism, then yeah, the double indirection is going to add up and bite you.

If you're writing a Mario clone, it's almost certainly no big deal.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

So don't bother with the double indirection; instead allow your handle class to indicate its ready state and allow the resource loader to patch in the correct pointer later via an atomic operation.

So don't bother with the double indirection; instead allow your handle class to indicate its ready state and allow the resource loader to patch in the correct pointer later via an atomic operation.

I haven't figured out how to patch in the correct pointer later. How is this possible if I don't know where the different copies of the resource handle are located?
When I load a resource, I synchronously construct the resource's header, and return a handle to it immediately. The header contains a "is loaded" flag. The asynchronous load fills in the header in the background (which may involve setting pointers to newly loaded blobs), and then atomically sets the flag to true.

In practice, this is basically a double-indirection, as you've got a handle to the header, and the header may have a pointer to some other data.
Double-indirections are themselves not a problem -- what is a problem is non-locality. If your double (or tripple, or quadruple) indirections cause you to jump between 3 (or 4, or 5) different memory areas in a completely random pattern, then it's going to lead to bad performance. If your indirections are all fairly local, then it's not a problem.
Hm, can you explain a little bit more about how the header concept is improving the locality of the indirection?
Sorry, I didn't mean to imply that simply having a header will improve locality -- just that double indirection isn't that bad as long as your locality is good.

One tactic that I use for having good locality, is to pre-load the sizes of all my resources into an array, so that when loading a resource, I can allocate enough space for both the header and any other required allocations before even starting the asynchronous load. In a lot of cases, the header and data areas are in a contiguous allocation.

Another tactic is to store all headers in a pool structure. Usually when performing operations on a resource, you will also be performing similar operations on other resources of the same type. So by storing all the headers of that resource-type together, you improve the locality for cases where you access many different headers in succession.
Ah, ok.
I assumed you and the previous posters were more concerned about the locality of the two pointers than about the actual resources.
If you decide to use a proxy class which implements the interface, to be able to lazy initialize or asynchronous loading. Then you can add a method which will remove extra layers of indirection, by returning the proxied objects... I've used this once for multithreaded loading of textures asynchronously.


class texture2d
{
public:
// ...
virtual shared_ptr<texture2d> optimized() const = 0;
};

class asynchronously_loaded_texture2d : public texture2d
{
shared_ptr<texture2d> result;

public:
// ...
shared_ptr<texture2d> optimized() const
{
if (!loaded())
wait_until_loaded();

return result;
}
};

This topic is closed to new replies.

Advertisement