Managing resources

B20d81438814b6ba7da7ff8eb502d039
0
Vilem_Otte 117 Jun 16, 2012 at 16:11

Hi,

it’s been a while since I posted some thread, and recently I’ve come to another nice problem - which needs a bit of optimization…

Managing resources (graphics ones - textures, models, skinned meshes, skinned anims, etc.) in game engine is not easy (especially in open-worlds) - as for now I’ve tried more ways of managing them, some better some worse. As for now the best I tried is this:

Basically you have a map containing <string s, pointer p> where string is name of resource and pointer is pointer to Templated class (e.g. manager is re-usable template class - where you supply Class on which it will operate). Also I store number of items and “reference counter” for each item.

So basically the class looks like this:

template <class T>
class CManager
{
public:
     T** mData;
     unsigned int* mRefCounts;
     unsigned int mCount;
     unsigned int mSize;
     map<string, T*> mDb;

     ..................
}

Where mData are actual data stored, mSize is current number of allocated items (reallocates all buffers to 2-times the size, when mCount == mSize), mCount is current number of used items, mRefCounts is array which contains reference counter for each item. mDb is actual “database” connecting string and pointer to object.

While this is good (imho) - loading method returns pointer to loaded item (if names match), otherwise schedule async load to “loading thread” (and returns address of new blank resource, till async loader swaps that address with address of loaded resource) - in both cases it actually increases reference counter for that item.

When one removes resource, it just decreases its reference counter (and if zero - then also frees that item - e.g. delete texture for textures, etc.).

So far seems okay, right?

This manager is dynamic, handles multiple same resources at time without problems, and doesn’t produce any garbage, it’s also pretty fast - because once you got your pointer to item - you just operate with the pointer to item, so you won’t call manager until you want to remove that item.

Yeah, but! I still wonder whether there are better ways (of course there are) for dynamic world? And whether they’re worth implementation, or this way is good enough.

Note: I know I could use F.e. hash from those strings instead of actually using strings, etc. - those are just details in this technique - e.g. same technique, using little different types, thats all. I wonder whether there is another good technique.

Also if you have some your way to manage resources, feel free to write it out here :). I’d be glad.

18 Replies

Please log in or register to post a reply.

88dc730f0f71e55be39de0ad103bd9ff
0
Alienizer 109 Jun 16, 2012 at 17:38

Using pointers “is” the best way. But I think there is a time when optimization becomes irrelevant, and actually becomes non-optimized. It may look good on paper, but not in practice.

For example, x /= 2 can be optimized with x *= 0.5f but in real life, they both take the same number if CPU cycle, so no optimization here. Perhaps this is a stupid example, but I couldn’t think of a better one.

So all I’m saying, you maybe trying too hard and overkill the problem, which perhaps is not a problem anymore the way you do it now!

6eaf0e08fe36b2c23ca096562dd7a8b7
0
__________Smile_ 101 Jun 16, 2012 at 18:17

Is there any need to store pointers to resources in two places, in mData and in mDb? Also, maybe better to put reference counter to resource itself?

Ceee4d1295c32a0c1c08a9eae8c9459d
0
v71 105 Jun 16, 2012 at 19:10

I have some doubts about how you manage reference counters, i see that you use a vector but with the map container there is no guarantee that you will find the items inserted at the same positions as they are in the map.
this is how i did in my engine

template < class T >
class CDataBaseNode
{

int References;
string FileName;
T *Item;

void IncReference() { References++; }
void DecReference() { References–; }

CDataBaseNode(const CDataBaseNode& object) { }
CDataBaseNode& operator=(const CDataBaseNode& object) { return *this; }

public:

T *GetItem() { return Item; }
int GetReferenceCount() { return References ; }
const char *GetFileName() { return FileName.c_str(); }

//////////////////////////////////////////
// constructor / destructor
// note that the named constructor is required
// uses the raii paradigm

CDataBaseNode( const string &filename )
{
FileName=filename;
Item= new T( filename );
References=1;
}

CDataBaseNode()
{
Item=NULL;
References=0;
}

\~CDataBaseNode()
{
if ( Item ) delete Item;
}

friend CDataBase<T>;

} ;

then in the resource manager

map< string , CDataBaseNode<T> * > DataBase;

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Jun 16, 2012 at 21:10

v71, could you paste that in again with the [ code ] … [ /code ] tags so the forum will preserve the formatting? :)

Ceee4d1295c32a0c1c08a9eae8c9459d
0
v71 105 Jun 17, 2012 at 09:19

I can do more, i could send the entire resource manager so others can use / enhance in their projects, does devmaster have a code repository ???

B5262118b588a5a420230bfbef4a2cdf
0
Stainless 151 Jun 17, 2012 at 10:48

Resource management is an issue that may not be relevant on many platforms.

For example if you are working in XNA you have 512Meg of storage and no physical medium, so just load everything at the start and be done with it.

On other platforms the main issue is managing the bus between physical and graphics memory.

So just doing a reference counter is not really worthwhile IMHO.

Indexing is an issue as well, I HATE using strings as resource ID’s. No matter what you do, somewhere in your code you are going to walk the string which is a waste of T states.

My resource handlers are very simple, they create an array with pointers to the physical resource and either use an enumeration for resource id’s or return an integer when the resource is loaded.

Sit back and look at what you are doing objectively, doesn’t matter how efficient your resource management code is, how clever the underlying structure, if it isn’t required, then it’s a waste of T states

6837d514b487de395be51432d9cdd078
0
TheNut 179 Jun 17, 2012 at 13:23

@Stainless

For example if you are working in XNA you have 512Meg of storage and no physical medium, so just load everything at the start and be done with it.

Gah! No! I loath waiting for a game to load like that. I’ve tossed quite a few games on the Win Phone that took several minutes to load. At the least, bring me to a menu and give me some interaction and music before pulling a stunt like that. I lean more towards the load it when you need it design. A snappy game brings a happy gamer.

As for resource management, I use a B-tree with cache management. It’s similar in logic to what you do, but IMO better structured :) I don’t put ref counters at the root of the database nor do I use std map as it’s implementation specific. Instead I use smart pointers at the node level. When I need to cleanup memory, I iterate over the child nodes and delete any resources with a 0 count. For open based games, I will keep the index. If I run into that resource later and it has been unloaded, I will load it in-place. For level based games, each level will have its own set of indices and I will continue to flush the database as I progress along.

B5262118b588a5a420230bfbef4a2cdf
0
Stainless 151 Jun 17, 2012 at 13:27

@TheNut

Gah! No! I loath waiting for a game to load like that. I’ve tossed quite a few games on the Win Phone that took several minutes to load. At the least, bring me to a menu and give me some interaction and music before pulling a stunt like that. I lean more towards the load it when you need it design. A snappy game brings a happy gamer.

So you are happy to have the game pause when it loads a sound effect?
Or have to wait 30 seconds when you walk around a corner?

Most games have a splash screen that they make you look at saying “Hey I wrote this and don’t copy it”, why shouldn’t that wasted time to used to load in the resources?

Once they are in you don’t have any delays, anywhere

Ceee4d1295c32a0c1c08a9eae8c9459d
0
v71 105 Jun 17, 2012 at 15:11

My resource manager uses string only for storage and for reference counting.
I use also an array which is basically a wrapper for an stl vector of pointer, i don’t need to search for an item in the database at runtime.
I do only in the initial phase and occasionally for editing models which are composed of many meshes , when i render i just walk with an iterator the vector of pointers.

6837d514b487de395be51432d9cdd078
0
TheNut 179 Jun 17, 2012 at 15:44

Not quite like that Stainless. I mean don’t load all your game resources at the start so that loading levels later happens fast. I dislike when developers trade one long load time for quicker load times later, even if most of the stuff you just loaded won’t even be used. Just be progressive about it. Load X graphics and sounds now and cache them. When switching levels or scenes, load any new content that hasn’t been loaded yet. There are many games that have abhorrent loading systems, some of which I’ve played on the Win Phone where they preload the entire game before you even get to the menu screen. Just staring at artwork for over a minute is very annoying.

B20d81438814b6ba7da7ff8eb502d039
0
Vilem_Otte 117 Jun 17, 2012 at 21:05

Is there any need to store pointers to resources in two places, in mData and in mDb? Also, maybe better to put reference counter to resource itself?

Bah… no, it’s quite WIP - so not so much structured (but in the end I thought that putting it to structure would be right way to go of course).

I have some doubts about how you manage reference counters, i see that you use a vector but with the map container there is no guarantee that you will find the items inserted at the same positions as they are in the map.

Yup, I find them - but the code is more mess (means using a bit more code) than using it “structurized” (like you’ve shown)… not bad…

Anyway thanks for replies - it now works flawlessly with asynchronous loading … :ph34r:

To the usage - I currently have in-development a project with wide, open-world area - thats why I needed some more advanced resource management (and you don’t want to know that we actually used arrays in prime version ;) … where we found out that using arrays is good, but we can do better).

And also ad second problem:

I mean don’t load all your game resources at the start so that loading levels later happens fast.

Actually you can start menu without loading any game stuff (just menu stuff - you know GUI and some background scene). Then when you start game, I schedule needed stuff to load to loader thread and until it loads the stuff I show loading screen. When finished moving around world is purely dynamic - streaming data on background while playing without any visible lag for loading. Just as you could seen in other open-world games.

Ceee4d1295c32a0c1c08a9eae8c9459d
0
v71 105 Jun 18, 2012 at 10:33

Your code gave me an idea to create a resourcemanager without a supporting database ‘node’ , thanks for the unintentional hint

B5262118b588a5a420230bfbef4a2cdf
0
Stainless 151 Jun 19, 2012 at 14:05

If you want it to be really useful code, build in support for lost context events.

In all the platforms I work on, it is very likely that your game will be put into a dormant state by the OS.

When this happens, all loaded textures are lost.

When the game is brought back from the dead, you have to reload all your textures.

Having that functionality built into your framework would be very handy

B20d81438814b6ba7da7ff8eb502d039
0
Vilem_Otte 117 Jun 20, 2012 at 08:57

I actually don’t target any other platform than desktop (where one does not simply lost context) - there’re several main problems:

1.) Too slow (they’re, that’s all - all other platforms than desktop today)
2.) Too little display and not good interaction with used (mobile platforms problems - we’re making open-world game, it’s just not suited to mobile devices)
3.) Re-writing the code - porting to another system means rewriting LOTS of code, although we have already everything implemented for several OSes - targeting Linux-based, BSD-based and Windows systems.

Even though it has method ReloadTextures - it’s quite useful when editing in our own editing tool and one needs to edit single texture outside :)

NOTE: Not that I say mobile platforms are bad, but they’re quite unsuited for my current project(s) + I like gaming on desktop (laptops) more - I think we all are.

B5262118b588a5a420230bfbef4a2cdf
0
Stainless 151 Jun 20, 2012 at 10:24

you can still get

D3DERR_DEVICELOST even on a laptop, so you do need to be able to reload textures

B20d81438814b6ba7da7ff8eb502d039
0
Vilem_Otte 117 Jun 20, 2012 at 16:05

Ow… this never happened to me (though I’m using OpenGL, Linux and GLX on laptop - and I have no idea whether one can lose GL context in Windows - it never happened to me on Linux (and I wonder whether it’s possible …)).

Anyway I know GL_ARB_robustness gives some info on this and I might (and possibly will) also add it…

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Jun 20, 2012 at 16:36

I don’t think losing the device can happen in OpenGL on PCs; it could happen in D3D up to version 9, but that does not occur anymore in D3D10-11, IIRC.

B5262118b588a5a420230bfbef4a2cdf
0
Stainless 151 Jun 21, 2012 at 09:02

I think you cab still get these events, but I haven’t done any PC coding for a while.

One of the most common ways of getting them was to switch between fullscreen and windowed with alt - enter.

They may have done something about it while I was away, but I would put it in the code anyway. You never know when it may save your bacon.