1
\$\begingroup\$

The way I've learned to implement an asset manager is having unordered maps for each asset type (textures, shaders, meshes, etc.), each with a string key and object value and then having methods Add(), Get(), Load() for each asset type. For my skill level and the scale of projects I'll be doing this is probably good enough, however, I came across a blog that uses the concept of handles which I found quite interesting. It shows a handle as a wrapper around a uint32_t and then goes on about a manager class that follows a similar structure to what I said.

What I don't understand about this approach is how do you associate a handle with a object? With the method I've been using since it's just strings I can easily create an object and store the ID in it

AssetManager::AddTexture("Textures/player.png", "PlayerTexture");
AssetManager::AddMesh("Meshes/player.obj", "PlayerMesh");

// This could technically be written anywhere
Scene::AddObject(GameObject("Player", "PlayerTexture", "PlayerMesh"));

AssetManager::GetMesh(Object.MeshName);

static Mesh GetMesh(std::string ID) {
    return  meshes[ID];
}

But with handles wouldn't I need to make my object right after loading the asset? Or how else am I supposed to get the handle to the object unless I continue to use string IDs but for the handles, but I feel like that would defeat the purpose. Here's an example of trying to understand using handles.

TextureHandle playerTextureHandle = AssetManager::AddTexture("Textures/player.png");

// Needs to be here so I can access the variable
Scene::AddObject(GameObject("Player", playerTextureHandle));

AssetManager::GetTexture(Object.textureHandle);

struct TextureHandle {
    unsigned int handle;
}

static Texture GetTexture(TextureHandle handle) {
    return textures[handle.handle];
}

Writing this post another potential problem might be the fact I don't understand what a handle fully is so I have a feeling I might not be using it in the intended way.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ When you say that you "came across a blog post", then it would be nice to link it. Sharing your research helps everyone. In this case it would help to understand how the author defines "handle" (because that's one of those nebulous terms people tend to interpret slightly different, leading to communication problems) and what problems they are trying to solve by introducing that layer of indirection.. \$\endgroup\$
    – Philipp
    Commented Jan 2 at 8:52
  • \$\begingroup\$ A handle is an identifier. Your string literals are handles too. \$\endgroup\$
    – Caleth
    Commented Jan 2 at 14:24
  • \$\begingroup\$ @Philipp sorry here’s the link giordi91.github.io/post/resourcesystem \$\endgroup\$
    – Konjointed
    Commented Jan 2 at 17:25
  • \$\begingroup\$ Typically, "how do I use this" or "how do I implement this" is really dependant on what your purpose is with it. Why do think you need to use handles? What issue do you expect to solve by using handles instead of your current approach? \$\endgroup\$
    – Vaillancourt
    Commented Jan 3 at 13:55

1 Answer 1

2
\$\begingroup\$

My favorite fast food restaurant recently refactored their ordering process.

You used to go to the counter, order your food, and then stand there and wait while you wait for the food, and everyone else who is hungry waits in line behind you.

But now you go to the counter, say what you want, pay, and then you receive a receipt with a number on it. Then you wait with your number in hand, while the employee at the counter serves other customers and hands them receipts with numbers as well. You wait until your number gets called, go to a different counter, show them your number, and receive your food. Or when your number doesn't get called, you can go to that counter, show them your number and ask what takes so long. Or when you have a complaint, you can go to the manager, show them your number, and tell them you want your money back.

What does that have to do with your question, you ask?

The number of the receipt is a handle for a food order. It's a convention for communicating between the consumer and the restaurant regarding what food order they are talking about. Which is crucial, because without such a convention this asynchronous food serving process would end up in chaos.

So how would you adapt that process to asset handling in a game engine?

  • The consumer goes to the AssetManager and asks them "I would like to have one Resources/Textures/Environment/Walls/BrickWall.png, please."
  • The AssetManager looks into its array of loaded textures. There are three possible cases: Either the texture is already loaded, it is in the process of being loaded, or the consumer is the first one to ask for it. In the first two cases, it would return the index of the texture. In the last case, it would find a new index for that texture, initiate the loading process on a separate thread, and return the new number.
  • When the consumer now actually needs that texture, they again ask the asset manager: "Here is my number, is the texture ready yet?". The manager then either responds with a reference to the loaded texture, tells the consumer that the texture is still being loaded, or throws an exception because they don't even know the number the consumer provided.

Now you might wonder "Why convert the name of the texture to an ID number? Why not always ask the AssetManager for "Resources/Textures/Environment/Walls/BrickWall.png" all the time? Isn't that a lot more convenient?" Well, the problem is that a std::unordered_map with a string key is relatively slow. Because in order to find the right value, it has to perform a lot of string comparisons. And a string comparison means to compare every character in string A with every character in string B until there is a difference. Which can take a while if you are using very long strings that often start identical. This is not a problem if you do it occasionally, but it is a problem when you do it every single frame for every single wall segment that's on the screen.

But do you know what's the fastest way to find a value? Jumping to a specific array index. So if your values are in an array, and you know the index, then that lookup is lightning-fast. Fast enough that it is usually not a problem to do it in your inner loop.

\$\endgroup\$

You must log in to answer this question.

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