1
\$\begingroup\$

Let's assume for the sake of argument, that we have a resource engine in place, and that we use OpenGL. My question is how the engine/game classes should reference the resources.

Lightweight example : among others, we probably have a texture resource. If we now have a class that uses this resource, do we store:

  1. a resource id?
  2. a resource pointer?
  3. the resultant resource (e.g. the GLID of the Texture)

1 gives us the most flexibility, and neat features like being able to reload all textures on the fly (after context destruction), but costs us a lookup each time it is used, and might require a mutex lock.

Another example - a font resource, same questions -- do we hold an id, pointer or a direct font class?

Third example, a model resource has a reference to a texture it uses. Does it store id, pointer or direct link?

If the answers differ between uses, is there a sensible set of rules one can follow to decide what should be used in what case?


EDIT: we're talking about a general use, heavy duty (3d, streaming, resource groups, memory managing) resource manager here in a threaded environment.

\$\endgroup\$
6
  • \$\begingroup\$ My implementation uses all of those approaches. I have a TextureManager which stores key-value pairs (dictionary) of strings and texture objects (and in some implementations loads the textures too), and the texture object can be fetched from it by using the key string. Then I can also just store the reference to the texture object somewhere in my code, and use that instead of fetching it from the manager. The GLID is then stored in the texture object itself publicly, if it happens to be needed somewhere (like in dynamic texture generation or such). This has worked for me pretty well. \$\endgroup\$
    – Lasse
    Commented Feb 6, 2014 at 2:49
  • \$\begingroup\$ Using all approaches is the worst solution possible -- this way you can't for example neither reload all the textures with one sweep, nor guarantee thread safety of a particular resource type. Also, a lack of clear guidelines makes the code messy :/ \$\endgroup\$ Commented Feb 6, 2014 at 3:06
  • \$\begingroup\$ Those are good points. I also should point out that I have developed my stuff for my own needs. Also I have never encountered a situation where I have to reload the resources, so my implementation has no support for that. In my newest implementation, Texture objects load the textures on demand and thread-safely. The texture object knows if the texture is not loaded, and binds a default texture GLID instead of the one it has stored. The loading is then done either in background thread or in the rendering thread, depending on the settings. \$\endgroup\$
    – Lasse
    Commented Feb 6, 2014 at 3:14
  • \$\begingroup\$ Reloading is needed as the engine has editor capabilities, so some resources might be modified outside and of need of reload. Also, cleverly implementing that it allows for easy context switching (you just go through all resources and do second stage reload on all GL related ones on the new context). In a world where going fullscreen needs a context switch, this is really helpful :/ \$\endgroup\$ Commented Feb 6, 2014 at 3:17
  • \$\begingroup\$ You can store a sort of atomic state identifier within the resource header that tells clients if it's currently being loaded or if it is finished. Pointers don't improve thread safety, and I doubt GL indices are going to be constant through context switches. \$\endgroup\$
    – jmegaffin
    Commented Feb 6, 2014 at 3:19

1 Answer 1

1
\$\begingroup\$

I would always go with the first. Let's see why we should eliminate the other two:

Having a pointer directly to the resource object is not great. In terms of performance, it's really about the same as an index lookup (assuming an array or hash table of resources). However, it's not flexible. It's meaningless to have it stored in an external file, and requires an extra field for any magic number (to prevent temporal issues).

Storing the library handle directly is borderline useless. My biggest gripe is that it prevents you from associating various levels of detail as the same resource. The resource header that you look up with the ID should have an array or list that contains the library handles associated with detail levels. This also suffers from the same problems as the pointer on top of that.


In terms of implementation, you're best off doing something like this.

// 4 bytes in total
struct ResourceHandle
{
    unsigned int index :24;
    unsigned int magic :8;
};

The index field is used to look up the resource itself in an array or map. The magic field is used to ensure that if a resource is deleted and another is created that uses the same index, an old handle will correctly report that its resource has been deleted, rather than accessing the new, unrelated resource.

Each resource index has a magic field of its own, that is incremented each time the resource at that index is deleted. When returning the handle for a new resource, the magic value of that index is part of the result.

\$\endgroup\$
8
  • \$\begingroup\$ I can see the benefits of #1, however, what about the downsides? Additional cache-unfriendly indirections, and possible need for thread locks? \$\endgroup\$ Commented Feb 6, 2014 at 3:03
  • \$\begingroup\$ You'd have the same issue with the other two. And with the index, I for example have all my resource headers in a contiguous array. It's more portable to store the index as an offset rather than an absolute memory location. \$\endgroup\$
    – jmegaffin
    Commented Feb 6, 2014 at 3:06
  • \$\begingroup\$ I would not, because I wouldn't touch the global resource manager. Until a sane way to decentralize resource management is found, any accessing of that global central entity is violating a lot of rules that work against the cache and thread safety. Especially if your resource manager does anything else than just passing the resource (e.g. memory pooling/control). \$\endgroup\$ Commented Feb 6, 2014 at 3:10
  • \$\begingroup\$ What exactly are you doing that makes you think you'll be running into threading issues? Also, even though I am a data-oriented enthusiast myself, resource management is still one thing that really should be centralized. \$\endgroup\$
    – jmegaffin
    Commented Feb 6, 2014 at 3:13
  • \$\begingroup\$ While streamloading data, the resource manager will obviously run in it's own thread, separate from the rendering thread and others, so to safely request the resources I'll obviously need to guard them with a mutex (AFAIK). \$\endgroup\$ Commented Feb 6, 2014 at 3:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .