OOP Design and Resource Management
_Kronk_

Hey people. I just recently got C++ The Language, and it's really helping me see how amazingly bad my code is and is giving me some great ideas on how to make my game engine API more manageable and stable.

So, I have to have a resource manager in my design; it just makes sense. The way I ahve been doing this is to have a single class, ResourceManager, that loads all the datafiles, etc, and also contains all the drawing functions so that anything that needs to be drawn also has indirect access to the data; in this case a user might have an Animation class that contains a list of unsigned ints that are frame indices from the datafile, these are sent to the ResourceManager's draw functions as arguments, etc.

TL;DR

Should I use a Singleton(which means Resource::f() syntax) or do what Stroustrup seems to like in C++TL, which would be something like this:

#SelectExpand
1 2// ResourceInterface.h 3 4namespace Resources { 5 6void FlipBuffer(); 7void Draw(int x, int y, unsigned frame, unsigned a); 8... 9 10} 11 12// ResourceImplementation.cpp 13 14namespace Resources { 15 16 BITMAP * buffer; 17 DATAFILE * data; 18 19} 20 21void Resources::Draw(int x, int y, unsigned frame, unsigned a) { 22 23 set_trans_blender(0, 0, 0, a); 24 draw_trans_sprite(buffer, (BITMAP*)data[frame].dat, x, y); 25 26} 27 28// etc...

So, users of the Resource code can just

using Resources;
Draw(myX, myY, anim->CurrentFrame(), anim->A());

instead of (a) exposing the Resource interface directly and (b) having to use verbose syntax.

Personally, I prefer using the namespace. Which is better?

bamccaig

Neither. The "best" is to use a non-static (non-Singleton) instance of your resource manager object. Also, your resource manager probably shouldn't know how to draw anything. That's mixing up concerns. Your resource manager should manage resources well. Nothing more.

_Kronk_ said:

namespace Resources {
   // ...
}

void Draw(//...

Draw is supposed to be inside of the namespace. ;) But again, it shouldn't be. ;D I like to have a "renderer" object that knows how to draw to the back-buffer/display/screen.

_Kronk_
bamccaig said:

Neither. The "best" is to use a non-static (non-Singleton) instance of your resource manager object. Also, your resource manager probably shouldn't know how to draw anything. That's mixing up concerns. Your resource manager should manage resources well. Nothing more.

The reason I have it doing the drawing is that it keeps the allegro backend from users; any code relating to DATAFILEs or BITMAPs is completely contained in the Resource Manager. I suppose I could rename it "GraphicsManager" or something similar to show the relationship better. I'm actually planning on writing an RAII wrapper class for BITMAPs and DATAFILEs, so that a ResourceManager might end up being obselete anyway.

If I were to use a single instance of ResourceManager somewhere and then pass around references/pointers to it, that changes the user's relationship to it; you go from

#SelectExpand
1 2// Drawing_Thingy.h 3 4class Thing { 5 6 protected: 7 8 int x, int y, unsigned frame, unsigned a; 9 10 public: 11 12 void Draw(); 13} 14 15// Drawing_Thingy.cpp 16 17#include "Drawing_Thingy.h" 18#include "Resources.h" 19using Resources; 20 21void Thing::Draw() { 22 23 DrawImage(x, y, frame, a); 24 25}

which has an understood the ResourceManager relationship to

#SelectExpand
1 2// Drawing_Thingy2.h 3 4class Thing { 5 6 protected: 7 8 int x, int y, unsigned frame, unsigned a; 9 10 public: 11 12 void Draw(ResourceManager& r); 13} 14 15// Drawing_Thingy2.cpp 16 17void Thing::Draw(ResourceManager& r) { 18 19 r.DrawImage(x, y, frame, a); 20 21}

which has an understood a ResourceManager relationship, when in reality there is only one ResourceManager.

Another question is where would the ResourceManager live? If it were made a member of the Game class, you would have to use syntax like

things[i]->Draw(Game->Resources);

which isn't nearly as clean or easy to read as the namespace option.

  • edit

Well, I'll have to go for a regular instance-centric design anyway. Just realized that I can't have an RAII wrapper construct/destruct in a semi-global scope like a namespace when I'm using allegro :(

bamccaig
_Kronk_ said:

The reason I have it doing the drawing is that it keeps the allegro backend from users; any code relating to DATAFILEs or BITMAPs is completely contained in the Resource Manager.

...

...there is only one ResourceManager.

Another question is where would the ResourceManager live? If it were made a member of the Game class, you would have to use syntax like

things[i]->Draw(Game->Resources);

which isn't nearly as clean or easy to read as the namespace option.

You asked for good practices and I'm giving them to you. >:( Don't shoot the messenger. Global state is always a bad thing because it's too easy to break things. With the namespace solution, all anyone has to do to foil your plans is al_destroy_bitmap(ResourceManager::bitmaps[0]). Have fun with that. ::) With the Singleton solution, it's impossible to test without affecting the entire application, which can cause other strange things to break. With a local object you are in complete control of what can talk to it and it is in complete control of what its willing to tell them.

I wouldn't pass the resource manager into a thing and have it draw itself with the resource manager though. From the sound of it all of your things are going to have very similar Draw methods. I would try to implement a common interface (abstract base class) for all things drawable and have a renderer object draw those. Your things will implement this drawable interface. The one renderer object can know about the one resource manager and then it's just a matter of saying...

for(<each drawable thing>)
{
    renderer.render(<thing>);
}

The renderer can worry about figuring out which resource to draw for which thing and how based on the interface(s) (you can have multiple drawable interfaces, if you need them, and therefore many overloads of Renderer::render, but you probably won't need this unless you do something beyond drawing a sprite per thing).

_Kronk_
bamccaig said:

You asked for good practices and I'm giving them to you. Don't shoot the messenger.

Sorry, didn't mean to come across that way. I was just kind of frustrated trying to find the best solution. It seems like I've been working on this forever ::)

So where would the Renderer be? It wouldn't be global, so maybe a member of the game class?

Mark Oates

Don't put the cart before the horse. The most important thing to think about is how you want to use the interface. First ask yourself "What would make it the most reasonable and usable?"

For example, I hide all my image managing behind the following functions:

ALLEGRO_BITMAP *get_image(const char *filename);
bool preload_image(const char *filename);
bool reload_image(const char *filename);
void set_image_path(const char *filename);

The above functions include several convience behaviors, including that get_image will never return a null pointer and crash my program. If you haven't preloaded an image yet, no big deal, it will be loaded on first call to get_image and is ready from that point on. Whenever I draw, I draw with the image filename, which acts as the identifier:

al_draw_bitmap(get_image("monkey.png"), 50, 20, NULL);

This way I don't have to remember indexes or variable names. Everything is consistent.

There are a multitude of ways that these functions could be "solved" under the hood, but in the end, it doesn't matter. The important thing is that you should think about how you want to use it first. Then solve for that.

Passing around pointers and instances, and juggling namespaces and whatnots is only going to make things worse.

axilmar
_Kronk_ said:

Personally, I prefer using the namespace. Which is better?

Neither.

Remember that object-oriented design is about objects. You shouldn't have any manager classes, it's a mistake. You should have objects that represent the actual entities of your program.

So, from the text you posted, I can infer the following objects:

  • Datafile. Obvious.


  • Bitmap. Bitmaps can be loaded separately, or returned from datafiles.


  • other resources like bitmap that can be loaded separately, but also be in a data file, like sounds, fonts, colors, etc.


  • Animation class. When created, it should be setup with the appropriate resources loaded by the above classes.


  • GameObject or Sprite or something like that; this object knows how to use an animation object in order to have a visual presence in the game world.

jmasterx

You always want to think, things might change, I might need more than 1 of something. In fact ideally (but not likely) your game should be able to be instanced multiple times and run separate instances of itself on multiple threads happily. I realize the underlying graphics or audio apis might not allow this but you want to think with this idea in mind; anything is bound to change, and singletons are bad.

Namespaces are a bit like packages in Java, or folders in a file system.

Because C++ relies heavily on libraries you want to know where classes come from.

Internally it can be used to organize though:

gui::widget::TextBox* textBox = new  gui::widget::TextBox();
base::Entity* hunter = new character::enemy::Hunter();

That's overkill but you get the idea. They are used to organize your classes, not to be your classes.

bamccaig
_Kronk_ said:

Sorry, didn't mean to come across that way. I was just kind of frustrated trying to find the best solution. It seems like I've been working on this forever ::)

I hope you didn't take me too seriously. :) It might seem awkward or tedious at first until you actually start doing this. You will [hopefully] see how much easier it is to write and organize though. :)

_Kronk_ said:

So where would the Renderer be? It wouldn't be global, so maybe a member of the game class?

That is an option. It could just be a local variable in whatever function/method the main game loop is in. Where it is depends on your design. It really doesn't need to be known to much of the program though. Mostly only the part that renders the game, which is hopefully only done in one part of the program. It can probably be a local variable somewhere, but it might make sense to make it a member of an object instead.

axilmar said:

Remember that object-oriented design is about objects.

Yes.

axilmar said:

You shouldn't have any manager classes, it's a mistake.

Not really. Managing/manipulating/organizing objects is a necessary thing and in a truly object-oriented world it would be objects that do these things also.

axilmar said:

You should have objects that represent the actual entities of your program.

Yes.

axilmar said:

When created, it should be setup with the appropriate resources loaded by the above classes.

It's debatable whether a bitmap object should know how to load itself from a file. It's especially awkward in C++ where throwing exceptions from a constructor is not so simple. I've found that it's often useful to have a separate class do this. The bitmap object itself becomes just a dumb data object and then you have higher-level objects that manipulate them.

AMCerasoli
axilmar said:

Remember that object-oriented design is about objects. You shouldn't have any manager classes, it's a mistake.

I don't know about classes manager (I don't use them when doing OOP), but resource managers it's also a very good idea in OOP.

A lot of objects may use the same resources like sounds or images, if you create 50 instances of the same object obviously you're not going to load an image per each object.

I use to send a pointer to the resource manager to each of my objects, and if the objects needs an image or sound etc... talks with the resource manager, and the resource managers do all that it needs to do to find that file: search in the .exe directory, sear on internet etc...

axilmar
bamccaig said:

Not really. Managing/manipulating/organizing objects is a necessary thing and in a truly object-oriented world it would be objects that do these things also.

Truly object-oriented worlds do not have manager objects. Look around you; do you see any manager object? you don't.

Quote:

It's debatable whether a bitmap object should know how to load itself from a file.

Not debatable at all, for me. The bitmap knows its internal organization of information, and so the bitmap should be able to load itself from a file.

Quote:

It's especially awkward in C++ where throwing exceptions from a constructor is not so simple.

There is no problem in throwing exceptions from constructors. If an exception is thrown from a constructor, the destructor of all up to that point constructed objects will be invoked, as if the object was deleted.

Quote:

I've found that it's often useful to have a separate class do this. The bitmap object itself becomes just a dumb data object and then you have higher-level objects that manipulate them.

And throw simplicity out of the window, for no benefit?

A lot of objects may use the same resources like sounds or images, if you create 50 instances of the same object obviously you're not going to load an image per each object.I use to send a pointer to the resource manager to each of my objects, and if the objects needs an image or sound etc... talks with the resource manager, and the resource managers do all that it needs to do to find that file: search in the .exe directory, sear on internet etc...

You can always create the resource once and then share it amongst all parts of your code. You don't need a resource manager for that.

AMCerasoli
axilmar said:

You can always create the resource once and then share it amongst all parts of your code. You don't need a resource manager for that.

If you want and unorganized code, and repetitive code all the time, you can do what you say.

isn't the same:

std::list<C_bullet*> bullets;
bullets.push_back(new Fast(&Res_manager));
bullets.push_back(new Blow(&Res_manager));
bullets.push_back(new Vola(&Res_manager));

Than:

#SelectExpand
1ALLEGRO_BITMAP *bullet_img = al_loa... 2ALLEGRO_BITMAP *exp_img = al_loa... 3ALLEGRO_BITMAP *air1_bmp = al_loa... 4ALLEGRO_SAMPLE *snd_blow1 = al_load... 5ALLEGRO_SAMPLE *snd_blow2= al_load... 6ALLEGRO_SAMPLE *snd_fall1= al_load... 7ALLEGRO_SAMPLE *snd_fall3= al_load... 8 9std::list<C_bullet*> bullets; 10bullets.push_back(new Fast(bullet_img, sound_touch, sound_roll, snd_fall3)); 11bullets.push_back(new Blow(bullet_img, exp_img, air1_bmp, snd_blow1, snd_blow2)); 12bullets.push_back(new Vola(air1_bmp, air2_bmp, snd_fall1, snd_fall3)); 13 14al_destroy_bit... 15al_destroy_bit... 16al_destroy_bit... 17al_destroy_sam... 18al_destroy_sam... 19etc...

And those are just ~3 arguments per objects... if they where more would be worse.

Audric
axilmar said:

Truly object-oriented worlds do not have manager objects. Look around you; do you see any manager object? you don't.

It's a proof that we don't live in an object-oriented world;)

axilmar

If you want and unorganized code, and repetitive code all the time, you can do what you say.

Your example isn't fair. When you are using the resource manager, you are forgetting to tell the resource manager which resources will be needed. Your code should have been like this:

Res_manager.load_bitmap("bullet");
Res_manager.load_bitmap("exp");
Res_manager.load_bitmap("air1");
Res_manager.load_sample("blow1");
Res_manager.load_sample("blow2");
Res_manager.load_sample("fall1");
Res_manager.load_sample("fall2");
Res_manager.load_sample("fall3");

std::list<C_bullet*> bullets;
bullets.push_back(new Fast(&Res_manager, "bullet", "touch", "roll", "fall3"));
bullets.push_back(new Blow(&Res_manager, "bullet", "exp", "air1", "blow1", "blow2"));
bullets.push_back(new Vola(&Res_manager, "air1", "air2", "fall1", "fall3"));

There is little difference between your approach and mine.

Audric said:

It's a proof that we don't live in an object-oriented world;)

But objects is what exists around us.

AMCerasoli

... Are you crazy? why would I tell the object which resource to load from outside?. The object already know that...

#SelectExpand
1 2class bullet : public C_bullet{ 3 private: 4 ALLEGRO_BITMAP* bm1; 5 ALLEGRO_SAMPLE* sn1; 6 public: 7 bullet(RES_MANAGER *res_manager): 8 bm1 (res_manager->bm_loader("bullet.png")), 9 sn1 (res_manager->sn_loader("sound1.ogg")) 10 {} 11 12 ~bullet(){ 13 res_manager->unneeded_res("bullet.png"); 14 res_manager->unneeded_res("sound1.ogg"); 15 } 16};

And that way all I have to do is just:

bullets.push_back(new bullet(&Res_manager));

axilmar

... Are you crazy? why would I tell the object which resource to load from outside?. The object already know that...

How would you reuse the code then? your bullet example is not reusable code.

Audric

Sharing code between bullet and another class is irrelevant.

The point of a resource manager is to get a kind of cache, where the first demander of a resource causes the load, and any other demander will be given a reference to the same resource, avoiding a second disk access and uncompress.

(The second demander may be a second instance of "Bullet", or a very different class like the HUD if it uses the same sprites to display ammo)

AMCerasoli

In the real world objects aren't reusable, at least not this kind of objects. This is the end of the chain, a bullet.

A bullet has its purposes, if you want a big bullet then you would do:

big_bullet : public C_bullet{..

All the rest would be the same, so the reusable code here is C_bullet which has physics functions and variables that are going to be needed by all the kind of bullets... Well at least that's how I see it.

If you want to change just the images used by your big_bullet I would create another object... After all it's another object.

big_black_bullet : public C_bullet{.. // this makes more damage so I change that here
big_red_bullet : public C_bullet{..   // this makes less damage

How to change the physics?, simple:

#SelectExpand
1 2class big_black_bullet : public C_bullet{ 3 private: 4 ALLEGRO_BITMAP* bm1; 5 ALLEGRO_SAMPLE* sn1; 6 public: 7 bullet(RES_MANAGER *res_manager): 8 bm1 (res_manager->bm_loader("bullet.png")), 9 sn1 (res_manager->sn_loader("sound1.ogg")) 10 { 11 12 set_damage(30); // change the damage. 13 14 } 15 16 ~bullet(){ 17 res_manager->unneeded_res("bullet.png"); 18 res_manager->unneeded_res("sound1.ogg"); 19 } 20};

bamccaig
axilmar said:

Truly object-oriented worlds do not have manager objects. Look around you; do you see any manager object? you don't.

Our world isn't a purely object-oriented world. An object is something "tangible and relatively stable." There are a ton of things in our world that don't fit that description. This post isn't an object in the real world.

There still are "managers" though. The laws of the universe, for example, manage much (everything?) of what happens. We can't actually move in space. We can only operate muscles inside of us that interact with our limbs that interact with our surroundings; our surroundings then push us around. :P

axilmar said:

Not debatable at all, for me. The bitmap knows its internal organization of information, and so the bitmap should be able to load itself from a file.

A bitmap and an image file (even a bitmap file) are two different things. They're very closely related (especially the bitmap file), but they are still distinct.

axilmar said:

There is no problem in throwing exceptions from constructors. If an exception is thrown from a constructor, the destructor of all up to that point constructed objects will be invoked, as if the object was deleted.

Yes, but you are still responsible for cleaning up any non-RAII state that the constructor has done so far. I didn't say that it wasn't possible. I just said it was awkward.

axilmar said:

And throw simplicity out of the window, for no benefit?

There is benefit. It makes more sense. In the real world, an image doesn't do anything. It's a static object. It can be manipulated, but it needs something external to manipulate it. The same is true of most other objects. There are things that the objects themselves can do, but there are also things that can be done with the objects.

The latter is usually the one that we're dealing with. A hammer doesn't pound in nails. A person, for example, pounds in a nail with a hammer. The hammer has attributes that make it good for pounding in nails, but the hammer itself doesn't do anything.

axilmar said:

You can always create the resource once and then share it amongst all parts of your code. You don't need a resource manager for that.

That's what a resource manager does for you. ;) It can potentially do it much easier. But if you're going to be sharing these objects then it's all the better that they be just dumb (preferably immutable) objects.

A bullet should not have functions on it to describe its physics, IMHO. A bullet should just describe the attributes of the bullet itself (and since it does nothing, doesn't really need any functionality). It's a dumb object. It might have a mass, maybe it has a shape, but how it interacts with the world is up to the particular world that its in. :P

AMCerasoli
bamccaig said:

A bullet should not have functions on it to describe its physics, IMHO. A bullet should just describe the attributes of the bullet itself (and since it does nothing, doesn't really need any functionality). It's a dumb object. It might have a mass, maybe it has a shape, but how it interacts with the world is up to the particular world that its in.

Yes bamccaig, and we're going to spend the rest of our life creating a perfect and completely similar world in which we live on. At the end with 80 years old axilmar is going to have a library of 50.000.000 files, which actually don't do anything, you would need to join 30 classes to make a ball move in the screen... ahahahahaha. I think you went too far body.

class ball : public texture, public material, public molecules, public atoms{..

;D

bamccaig

You don't have to model every last detail. You only need to model what exists in your world. That doesn't change how they should be manipulated. Usually objects interact with other objects. Sometimes they interact in weird ways with the entire world. If you try to store that in each object then the entire world is going to have to know about the entire world. If you use higher-level managers or service objects then you can centralize this functionality much easier, IMHO.

Ultimately, the language doesn't put any restrictions on you. A method is just a dumb function that gets an implicit argument passed to it. It's up to the programmer to figure out what is appropriate where. Bullet::shoot(Player &) could just as easily be Player::shootWith(Bullet &). So which is it? Both? It could instead be, World::shoot(Player &, Bullet &). The Player doesn't need to know about the Bullet and visa-versa.

_Kronk_

I think I'm finally starting to see the light on all this :) Thanks guys. Up until this point, I've been kind of half-heartedly OOP, but it's obvious now how effective it can be to put more thought into my designs.

A rough outline of my new design:

#SelectExpand
1 2int main() { 3 4 init_allegro(); 5 6 try { 7 8 Game game; 9 game.Run(); 10 11 } 12 13 catch(MyException& e) { 14 15 cout << "Exception: " << e.name << endl << "Cause: " << e.cause << endl; 16 17 } 18 19} 20 21void Game::Run() { 22 23 try { 24 25 ResourceManager resources(cfg file name); 26 ObjectManager objects(cfg file name); 27 Map map(cfg file name); 28 Renderer renderer(); 29 30 if(NeedUpdate()) { 31 32 objects.Update(); 33 34 map.Draw(renderer); 35 objects.Draw(renderer); 36 renderer.FlipBuffer(); 37 38 } 39 40 } 41 42 catch(MyException& e) { 43 44 cout << blah; 45 46 } 47 48}

I'm also going to be working from a RAII standpoint in code as well. I'm kind of stoked. This is going to be much less of a beast to work with than my previous design ;D

axilmar
Audric said:

Sharing code between bullet and another class is irrelevant

Reuse is irrelevant? since when?

Quote:

The point of a resource manager is to get a kind of cache, where the first demander of a resource causes the load, and any other demander will be given a reference to the same resource, avoiding a second disk access and uncompress.

A variable is good enough for this purpose. No need for a cache.

In the real world objects aren't reusable, at least not this kind of objects. This is the end of the chain, a bullet.

In the real world, we should reuse as much code as possible.

Quote:

If you want to change just the images used by your big_bullet I would create another object... After all it's another object.

So if all a class does is change the images, why not have one class and load the images to the object externally using a function? it makes more sense.

bamccaig said:

There still are "managers" though. The laws of the universe, for example, manage much (everything?) of what happens. We can't actually move in space. We can only operate muscles inside of us that interact with our limbs that interact with our surroundings; our surroundings then push us around.

That's not management, that's interactions based on rules.

Quote:

A bitmap and an image file (even a bitmap file) are two different things. They're very closely related (especially the bitmap file), but they are still distinct.

I didn't say otherwise. Still, a bitmap should use the image file internally to load itself from disk. That's proper OO.

Quote:

Yes, but you are still responsible for cleaning up any non-RAII state that the constructor has done so far. I didn't say that it wasn't possible. I just said it was awkward.

That's what smart pointers are for. There is nothing awkward to it.

Quote:

The latter is usually the one that we're dealing with. A hammer doesn't pound in nails. A person, for example, pounds in a nail with a hammer. The hammer has attributes that make it good for pounding in nails, but the hammer itself doesn't do anything.

Then you would need a person object that knows everything. Not good OOP.

Quote:

That's what a resource manager does for you.

And therefore a resource manager is redundant.

Quote:

It can potentially do it much easier.

I've yet to see an example of how a resource manager makes it easier. Anyone care to post one?

Quote:

A bullet should not have functions on it to describe its physics, IMHO. A bullet should just describe the attributes of the bullet itself (and since it does nothing, doesn't really need any functionality). It's a dumb object. It might have a mass, maybe it has a shape, but how it interacts with the world is up to the particular world that its in.

No, that's part of the Bullet subclass, not an external object. The common attributes of bullets belong to the Bullet class, and the particular physics of the world belong to the Bullet subclass.

Yes bamccaig, and we're going to spend the rest of our life creating a perfect and completely similar world in which we live on. At the end with 80 years old axilmar is going to have a library of 50.000.000 files, which actually don't do anything, you would need to join 30 classes to make a ball move in the screen... ahahahahaha. I think you went too far body.

On the contrary, I'd have one Bullet class.

Oscar Giner
axilmar said:

I've yet to see an example of how a resource manager makes it easier. Anyone care to post one?

Not needing to keep track of duplicate bitmaps accross all yout program manually. Also, the example posted earlier assumes that the resources needed are known at compile time (they're hardcoded). It's also nice that, when entering a new level, the resource manager automatically frees any resource that will not be used by the new map, but keeping the ones that will be used (a lot of games fail at this though, and always unload everything and reload again the needed resources at each map change). How do you do this without a resource manager?

jmasterx

A resource manager can do reference counting on expensive resources like bitmaps.

This way each class that might need a Bitmap just requests it from the RM and the class calls releaseBitmap() once it is destroyed and so resources are never duplicated and only freed when they are no longer needed.

AMCerasoli
axilmar said:

In the real world, we should reuse as much code as possible.

Who is saying the contrary?. A resource manager is exactly the best way to reuse code.

Quote:

So if all a class does is change the images, why not have one class and load the images to the object externally using a function? it makes more sense.

Not in OOP. I made that just as an example, I would change more than just an image if I were creating a new class.

Quote:

I've yet to see an example of how a resource manager makes it easier. Anyone care to post one?

I can't believe that you're saying that... I'm definitively not going to take the time to explain you how a resource manager makes things easier, cleaner and if you are doing it in a different thread even faster, because:

  1. You haven't considered a game with more than 20 images and...

  2. You attitude seems very stubborn.

I can't believe that you, as a experienced programmer, haven't realize of the benefits of a resource manager. :-/

Audric
axilmar said:

Audric said:

Sharing code between bullet and another class is irrelevant

Reuse is irrelevant? since when?

It's irrelevant to the issue that was discussed so far.

axilmar said:

Audric said:

The point of a resource manager is to get a kind of cache, where the first demander of a resource causes the load, and any other demander will be given a reference to the same resource, avoiding a second disk access and uncompress.

A variable is good enough for this purpose. No need for a cache.

You don't seem to understand that the resource manager is generic and dynamic. You write it with for example a nice std::map<string, BITMAP *>, and you'll never have to modify one line of its code while you write the game and use more and more resources.

bamccaig
axilmar said:

That's not management, that's interactions based on rules.

What enforces the rules? A "manager" of some sort. Call it "God", call it the universe, call it whatever you want. In the real world we don't know what is responsible, but in your game you need something to be responsible. You can have a hundred different objects all enforcing little rules or you can have higher-level objects that enforce all (or all related) rules. These higher-level objects have the advantage of being able to see more of the big picture too, without violating any design principles.

axilmar said:

I didn't say otherwise. Still, a bitmap should use the image file internally to load itself from disk. That's proper OO.

A bitmap is a bitmap. It is a map of bits. ;) That's all it is. It doesn't need to know anything of files, file formats, file systems, file permissions, etc. It can, but that doesn't mean that it should. It should ideally do one thing and do it well. It's already a map of bits. It doesn't need to be a file reader/parser as well. :P

axilmar said:

That's what smart pointers are for. There is nothing awkward to it.

Compared to other languages, it is awkward. :)

axilmar said:

Then you would need a person object that knows everything. Not good OOP.

No, you wouldn't. You could have a higher-level class, lets call it God (for a laugh), that knows about both a Person and a Hammer. Person might know that it hits a Nail with a Hammer to pound it into Lumber, but its not the Person itself that repositions the Nail in the Lumber. :) It's the Game or the World or the God or the PhysicsEngine.

axilmar said:

And therefore a resource manager is redundant.

The idea behind a resource manager is that its a repository of resources that can be easily passed around the application, granting access to shared resources (potentially with restrictions or limitations). You haven't shown anything equivalent. Anything equivalent that you do propose will be a resource manager with a different name. Even if you inline the code, it's still a resource manager. Just a poorly organized one.

axilmar said:

...and the particular physics of the world belong to the Bullet subclass.

If that doesn't set off design alarm bells I don't know what will. :-/

axilmar

Not needing to keep track of duplicate bitmaps accross all yout program manually

That's sweet for GUIs, where bitmaps are defined externally, but not for games, where bitmaps are defined internally. You could do the same by a global variable, or a struct that contains a global variable. It does not seem that what you mention requires a resource manager.

Quote:

the resource manager automatically frees any resource that will not be used by the new map

Smart pointers handle this gracefully.

Quote:

How do you do this without a resource manager?

How about simple functions that load resources when required, plus smart pointers to resources? I see no need for a resource manager. The resource manager tests, at each request, if the resource is loaded, and if not, it loads the resource. I don't think that this is necessary for the simple games. Perhaps it is necessary for dynamically loaded games, where resources are loaded as the game runs. But then, the resource manager will do a lot more things, like keeping stats of video memory occupied, keeping important textures around etc.

jmasterx said:

This way each class that might need a Bitmap just requests it from the RM and the class calls releaseBitmap() once it is destroyed and so resources are never duplicated and only freed when they are no longer needed.

Why request the bitmap from the resource manager and not simply access it from a struct? when the bitmap is needed, it is usually already loaded in another point in the code.

Who is saying the contrary?. A resource manager is exactly the best way to reuse code.

A resource manager is not related to code reuse at all, unless the same requests are meaningful in different games, which is a very rare case.

Quote:

You haven't considered a game with more than 20 images and...
I can't believe that you, as a experienced programmer, haven't realize of the benefits of a resource manager.

The first thing I coded for my GUI was the resource manager. However, a find a resource manager for a game a strange idea.

I prefer not to do this:

class Bullet {
public:
    Bullet(ResourceManager &rm) {
        m_bitmap = rm.getBitmap("explosion");
    }
};

I prefer this:

Resources res;
res.m_explosion_bitmap = load_bitmap("explosion.png");
class Bullet {
public:
    Bullet(Resources &res) {
        m_bitmap = res.m_explosion_bitmap;
    }
};

In other words, I prefer resources loaded in a step prior to game objects being constructed, for various reasons (which I can explain, if you wish).

Audric said:

It's irrelevant to the issue that was discussed so far.

Reuse is not irrelevant. We are essentially discussing good design, and reuse is one part of good design.

Quote:

You don't seem to understand that the resource manager is generic and dynamic. You write it with for example a nice std::map<string, BITMAP *>, and you'll never have to modify one line of its code while you write the game and use more and more resources.

You may not have to modify the resource manager code, but you certainly have to modify the loading resources code. So, if you have to modify the loading resources code, then you don't need a resource manager.

Do you, in your games, load bitmaps dynamically, when first requested? I don't, and I can't imagine anyone doing so in a game, unless you do a game so heavily textured that you need to load textures from disk dynamically, ala Rage. But then, your resource manager code is not a simple std::map<string, BITMAP *>, it's a lot more complicated.

bamccaig said:

What enforces the rules?

The program itself.

Quote:

These higher-level objects have the advantage of being able to see more of the big picture too, without violating any design principles.

And then you complicate the design to the degree that your code doesn't make sense and cannot be reused or modified a few weeks later.

Quote:

It can, but that doesn't mean that it should. It should ideally do one thing and do it well. It's already a map of bits. It doesn't need to be a file reader/parser as well. :P

I didn't say that. I agree, a bitmap doesn't need to be a file reader/parser. However, a bitmap shall internally use a file object that is a file reader/parser. It's important be able to tell the bitmap "bitmap, load yourself from this resource". If you can't do that, the responsibility of supplying an appropriate resource falls onto you, and suddenly you must know two things instead of one.

Quote:

Compared to other languages, it is awkward.

No, these other languages are the awkward ones. Assuming that you are talking about languages with garbage collection and non-deterministic resource management.

Quote:

No, you wouldn't. You could have a higher-level class, lets call it God (for a laugh), that knows about both a Person and a Hammer. Person might know that it hits a Nail with a Hammer to pound it into Lumber, but its not the Person itself that repositions the Nail in the Lumber. :) It's the Game or the World or the God or the PhysicsEngine.

Absolutely wrong design, and very distant from the OO theory.

Quote:

The idea behind a resource manager is that its a repository of resources that can be easily passed around the application, granting access to shared resources (potentially with restrictions or limitations).

As I said above, a resource manager makes sense for the GUI, but not for a game.

Quote:

You haven't shown anything equivalent.

I did. See above.

Quote:

Anything equivalent that you do propose will be a resource manager with a different name. Even if you inline the code, it's still a resource manager. Just a poorly organized one.

What I propose is not equivalent, and therefore not a resource manager ;-).

Quote:

If that doesn't set off design alarm bells I don't know what will.

That's proper OO: new behaviors are introduced by subclassing, honoring the is-a relationship.

Where and how did you learn OOP?

SiegeLord
axilmar said:

I prefer this:

Resources res;
res.m_explosion_bitmap = load_bitmap("explosion.png");
class Bullet {
public:
    Bullet(Resources &res) {
        m_bitmap = res.m_explosion_bitmap;
    }
};

I'm sure others will tear you apart on most of your other nonsense, but this I just had to address myself. Do you seriously suggest a Resources class with thousands of members, one for each object type? What if your game is extensible via configuration files (like any game should be)?

axilmar
SiegeLord said:

I'm sure others will tear you apart on most of your other nonsense, but this I just had to address myself. Do you seriously suggest a Resources class with thousands of members, one for each object type?

"Nonsense"...please. Hear me out first.

I do seriously suggest a Resources class with thousands of members. Here are the advantages of this solution:

  • type safety. If you user a resource manager, and have thousands of resources, type errors will not be caught until you run the game and test each and every resource. With my solution, type errors will be limited to the resource loading code.


  • name safety. If you user a resource manager, and have thousands of resources, managing thousands of resource name strings scattered all over the code quickly becomes a liability. With my solution, if you misspell a resource name, expressed as a variable identifier, the code will not compile.


  • CPU and ram performance. With the resource manager, searching a map by using a string as a key can seriously hurt performance, especially if this action is repeated at every frame. With my solution, none of this happens, and the CPU cycles and memory can be used for other purposes.


  • GPU and vram performance. If bitmaps are not loaded all together in one phase, i.e. if bitmaps are loaded the first time they are needed, and if there are many many bitmaps, then there is the danger of the video ram being exhausted, and perhaps important bitmaps may be loaded in ram instead of vram. Creating a system where textures are loaded quickly from disk requires extremely fast disks, otherwise the video performance will be problematic. See the recently released game "Rage" for such an example: its megatexture solution failed spectacularly on PCs with slow disks. When the player turned around quickly, the textures could not be loaded as fast as needed, resulting in very bad graphics that were slowly and visibly replaced with good graphics.


  • player convenience. If bitmaps are loaded as they needed from the disk, there might be a case of slowdown during the action, i.e. the framerate dropping because of I/O. I don't like that in games, and most other people wouldn't like that either. So, as a programmer, I prefer to load all the appropriate files before engaging on the action. But loading all the files at one place defeats the purpose of a resource manager.

Now that I've said all this, I think that it is valid to say that there is no point in a resource manager in a video game, when a resource manager can have all the above-mentioned disadvantages.

A Resources class may not be a monolithic class, if you are worried over built times.

Quote:

What if your game is extensible via configuration files (like any game should be)?

Extensible with what? new graphics and sounds? my approach still uses configuration files to load the resources, so I fail to see where is the problem.

Except if you mean other extensions via configuration files that I am unaware of.

Oscar Giner
axilmar said:

That's sweet for GUIs, where bitmaps are defined externally, but not for games, where bitmaps are defined internally.

What!? Any medium sized game will have the bitmaps defined externally... For very small games it's ok to have the resources hardcoded into the code, but you don't want to do that for anything bigger.

Quote:

Smart pointers handle this gracefully.

Not in the scenario of bitmaps defined externally.

Quote:

A resource manager is not related to code reuse at all, unless the same requests are meaningful in different games, which is a very rare case.

A resource manager is just that, and it can be easily reused for different games.

Quote:

In other words, I prefer resources loaded in a step prior to game objects being constructed, for various reasons (which I can explain, if you wish).

And I agree with that, and I don't see how that has anything to do with using or not a resource manager :o.

<edit>

axilmar said:

type safety. If you user a resource manager, and have thousands of resources, type errors will not be caught until you run the game and test each and every resource. With my solution, type errors will be limited to the resource loading code.

name safety. If you user a resource manager, and have thousands of resources, managing thousands of resource name strings scattered all over the code quickly becomes a liability. With my solution, if you misspell a resource name, expressed as a variable identifier, the code will not compile.

But then resources must be hardcoded into the game code. You want to separate code from level dessign and art. What you say is only useful for small games.

Quote:

CPU and ram performance. With the resource manager, searching a map by using a string as a key can seriously hurt performance, especially if this action is repeated at every frame. With my solution, none of this happens, and the CPU cycles and memory can be used for other purposes.

You can perfectly store a pointer to the resource once, so you don't need to access the map each frame.

Quote:

See the recently released game "Rage" for such an example: its megatexture solution failed spectacularly on PCs with slow disks.

Nope, the problem was elsewhere (there was a bug that affected 2 core CPU's, and also bad video drivers). After the updates I get constant 60 fps on my 3 years old CPU and HD. The slowdown wasn't because of the HD (it works fine on XBOX360 and PS3, and I don't think their HD's are that fast), it was because of way too high CPU usage that Id solved in the first patch (and also to workaround a bug in ATI drivers that lead to a very big CPU usage by the driver when streaming the textures).

Quote:

player convenience. If bitmaps are loaded as they needed from the disk, there might be a case of slowdown during the action, i.e. the framerate dropping because of I/O. I don't like that in games, and most other people wouldn't like that either. So, as a programmer, I prefer to load all the appropriate files before engaging on the action. But loading all the files at one place defeats the purpose of a resource manager.

You can force loading/unloading resources only at level change or when you want. And no, that doesn't defeat the purpose of a resource manager.

SiegeLord
axilmar said:

Extensible with what? new graphics and sounds? my approach still uses configuration files to load the resources, so I fail to see where is the problem.

Except if you mean other extensions via configuration files that I am unaware of.

A concrete example. In my game I have an objects folder where you define new game objects, like so:

File: slime.obj

class = land_creature
sprite = slime

File: slime_colossus.obj

class = land_creature
sprite = colossus

Internally there's just one class (LandCreature), but the configuration files control the sprite + behaviour of the specific type of the object.

How would you handle that in your hard-coded Resource class?

Edgar Reynaldo

Oscar already covered this, but I thought I would point it out as well - using a resource manager doesn't mean you can't load all your resources at the same time.

You can preload everything you want to, and then just get references to it later, as well as use an UnloadAll method to clear levels.

axilmar

What!? Any medium sized game will have the bitmaps defined externally... For very small games it's ok to have the resources hardcoded into the code, but you don't want to do that for anything bigger.

Yes, bitmaps will be defined externally. But you will need to address those bitmaps somehow from your code. So, you don't get away with hardcoding some references in the code anyway.

Quote:

Not in the scenario of bitmaps defined externally.

Please elaborate. In my opinion, an std::shared_ptr<BITMAP *> will successfully keep a bitmap around until no longer needed. Why do you think otherwise?

Quote:

A resource manager is just that, and it can be easily reused for different games.

I repeat: the resource manager class can be reused, but the code that uses the resource manager cannot be reused, as it is custom per game.

Quote:

And I agree with that, and I don't see how that has anything to do with using or not a resource manager

Someone suggested earlier that a resource manager is used in cases like this:

class Bullet {
public:
    BITMAP *bmp1;
    Bullet(ResourceManager &rm) {
        bmp1 = rm.getBitmap("bmp1");
    }
};

I disagree with the above, i.e. loading graphics dynamically, when game objects are created, for the reasons I explain in my previous post.

Quote:

But then resources must be hardcoded into the game code. You want to separate code from level dessign and art. What you say is only useful for small games.

Some kind of hardcoded reference is required at code level. It cannot be avoided. The resources will not be magically loaded.

Quote:

You can perfectly store a pointer to the resource once, so you don't need to access the map each frame.

But that's what I said and people told me it was nonsense.

Quote:

Nope, the problem was elsewhere

Ok. Thanks for the info.

Quote:

You can force loading/unloading resources only at level change or when you want. And no, that doesn't defeat the purpose of a resource manager.

But if you don't need dynamic loading of resources, and if you want type and name safety that the resource manager doesn't provide, then why do you need a resource manager? I don't see any reason for it.

SiegeLord said:

How would you handle that in your hard-coded Resource class?

Where did I say that in my code, resources will be hardcoded? I never said that.

Anyway, I'd load the objects from the config file. But, somewhere in the code, there would be a reference to the slime object, like this:

LandCreature slime = resourceManager.loadObject("slime");

So the above can be happily replaced with:

LandCreature slime = Object::load("slime");

If I had a config file, I'd do this:

LandCreature slime = Object::load(al_get_config_value("slime"));

You probably are talking about a configuration file, not a resource manager.

Oscar already covered this, but I thought I would point it out as well - using a resource manager doesn't mean you can't load all your resources at the same time.You can preload everything you want to, and then just get references to it later, as well as use an UnloadAll method to clear levels.

So why do you need a resource manager, if you don't load your resources dynamically?

SiegeLord

???

Ok, let me make my configuration files a bit more clear...

File: slime.obj

class = land_creature
sprite = slime.png

File: slime_colossus.obj

class = land_creature
sprite = colossus.png

And inside the constructor of the land creature:

{
  Bitmap = ResourceManager.GetBitmap(Config.GetString("sprite"));
}

Or to put it another way, how are you implementing:

LandCreature slime = Object::load("slime");

Because to me all you've done is created a resource manager where the resource is now objects.

EDIT: Oh, and let me make this perfectly clear. At no point anywhere in the source code is there any mention of such a thing as a "slime" or a "slime_colossus". There's no mention of "slime.png" and "colossus.png" anywhere either. All those things are loaded at runtime.

l j

Resource managers seems to make many things easier for me.

It becomes easier to share the same resource without loading the same resource multiple times by mistake.

Loading resources from script/data files also becomes easier, without again, having to worry about duplicating resources in memory.

axilmar
SiegeLord said:

Oh, and let me make this perfectly clear. At no point anywhere in the source code is there any mention of such a thing as a "slime" or a "slime_colossus". There's no mention of "slime.png" and "colossus.png" anywhere either. All those things are loaded at runtime.

So, you are not talking about a resource manager actually, you are simply talking about creating instances of objects from configuration files. That's hardly the same as what we have been discussing.

taron  said:

Resource managers seems to make many things easier for me.It becomes easier to share the same resource without loading the same resource multiple times by mistake.Loading resources from script/data files also becomes easier, without again, having to worry about duplicating resources in memory.

Ok. How is that more helpful than having a variable to the resource?

SiegeLord
axilmar said:

So, you are not talking about a resource manager actually, you are simply talking about creating instances of objects from configuration files. That's hardly the same as what we have been discussing.

What? Seriously... what?

Objects use resources. It doesn't matter how they are created, they need resources. I manage those resources using a resource manager. How will you manage resources using your technique in my case? I've shown you an implementation of a constructor that uses a resource manager. Please show me one that uses your technique.

axilmar
SiegeLord said:

What? Seriously... what?Objects use resources. It doesn't matter how they are created, they need resources. I manage those resources using a resource manager.

The resource manager discussed in this thread is one about loading resources dynamically, on a need basis, by name of the resource.

Your resource manager is a facility that allows you to instantiate game objects from configuration files.

Can't you see the difference?

Quote:

How will you manage resources using your technique in my case? I've shown you an implementation of a constructor that uses a resource manager. Please show me one that uses your technique.

Already shown it to you:

Object slime = Object::load("slime");

The above is not a resource manager, it's a simple function that loads a slime object.

SiegeLord
axilmar said:

The resource manager discussed in this thread is one about loading resources dynamically, on a need basis, by name of the resource.

How does my constructor not fit that description? The game might instantiate a slime at any point during the runtime, then it would need the slime.png resource loaded, which would be taken care of by the resource manager. Slime might be deleted, or the level might be unloaded and then the slime.png resource should be unloaded too. The ability to create objects based on the description in a configuration file is not resource management per se, but it is a use case that requires a resource manager.

EDIT: Or how about this. Forget objects and take your description:

Quote:

The resource manager discussed in this thread is one about loading resources dynamically, on a need basis, by name of the resource.

And now answer me, how do you handle the case where the name of the resource is a dynamic value, unknown at compile time.

I don't see where this confusion is coming from, I think I'm being very clear in what I am asking.

Matthew Leverton

Have you not yet realized that axilmar will politely argue semantics on any given topic until Jesus' second coming? Even if you fundamentally agree on something, it's doubtful you'd ever come to that conclusion...

Anyway, if your method works without any major problems, it's silly to argue it's wrong because it introduces an extraterrestrial object. Programming is about getting things done, not creating a model of the Real World(TM).

axilmar
SiegeLord said:

The ability to create objects based on the description in a configuration file is not resource management per se

Cool, that's what I meant.

Quote:

but it is a use case that requires a resource manager.

Does it really require a resource manager? given that 1) resources are loaded before the game begins, 2) careful management of vram is required and 3) using tree maps or hash maps may cause performance problems during the game, are you sure that a resource manager like the one we discuss is required?

Quote:

And now answer me, how do you handle the case where the name of the resource is a dynamic value, unknown at compile time.

In a use case you describe, tree/hash maps are only required when the resources are loaded and passed to the created objects. Even if you don't create all the objects at the start of a game's level, you can create the stubs that will create the game objects, and thus solving the problems I mentioned above.

Can you now answer me how would you handle the problems I described earlier?

Quote:

I don't see where this confusion is coming from, I think I'm being very clear in what I am asking.

You formed the question in such a way that it was not clear (at least to me) what was the use case.

Have you not yet realized that axilmar will politely argue semantics on any given topic until Jesus' second coming? Even if you fundamentally agree on something, it's doubtful you'd ever come to that conclusion...

I apologize for being too analytical, but I believe that the devil is in the details, as they say. Cases like these need a deep analysis, they are not easy.

For example, imagine making a game for the PC using Allegro 5. There is no problem with using a resource manager, since a modern PC has plenty of RAM and CPU power.

But if you transfer the game to an iPhone or a console, the game may not run properly, due to severe CPU and RAM limitations.

There might be a case that a level that loads bitmaps dynamically, as suggested, starts smoothly on an iDevice or console, but then it hungs up or slows down when a certain bitmap is loaded while the game is running.

I have seen such an example on the iPhone games developed by the company I work for. The game run smoothly on the simulator, which run on a Mac, and also run smoothly on the iPad and latest iPhone, but slowed down on an earlier iPhone. It was a case of resource management detail that caused a massive code restructuring and set us back more than a month.

See, I don't argue for the sake of arguing :-).

Oscar Giner
axilmar said:

using tree maps or hash maps may cause performance problems during the game,

You only use search trees or hash maps in your editors (for ease of use, being able to give things a name). But once you "compile" the resources into a format for use by the game itself, all accesses are by index.

Quote:

careful management of vram is required

That's a thing a resource manager can do pretty well. For example resources can have a priority, so in case of low vram, resources with low priority are moved to system RAM.

axilmar

You only use search trees or hash maps in your editors (for ease of use, being able to give things a name). But once you "compile" the resources into a format for use by the game itself, all accesses are by index.

So, if I am to use an index, which must be known at compile time, why not use a simple variable? what's the pros of an index over a variable?

Quote:

That's a thing a resource manager can do pretty well. For example resources can have a priority, so in case of low vram, resources with low priority are moved to system RAM.

What about resources that are already requested from the resource manager? if I have a pointer to a bitmap, and then the resource manager moves my bitmap to the system memory, then my bitmap will be invalid.

Furthermore, in order for the resource manager to do this, it will have to process every resource it currently has, and therefore the game will probably pause, even for a few seconds (because determining the best possible way to lay out different sized bitmaps in memory is not a trivial task). Which is not acceptable, I think.

Oscar Giner
axilmar said:

So, if I am to use an index, which must be known at compile time

No, it's not known at compile time. Where did I say that?

Quote:

What about resources that are already requested from the resource manager? if I have a pointer to a bitmap, and then the resource manager moves my bitmap to the system memory, then my bitmap will be invalid.

If you want to allow that, you'll need a Bitmap class that takes care of this scenario, and the program always interfaces through this, never directly accessing allegro bitmaps.

axilmar

No, it's not known at compile time. Where did I say that?

You said earlier (quote):

But once you "compile" the resources into a format for use by the game itself, all accesses are by index.

So, I imagine you are talking about something like Allegro 4's resource files, aren't you? so, you are talking about index constants produced by the tools.

If you want to allow that, you'll need a Bitmap class that takes care of this scenario, and the program always interfaces through this, never directly accessing allegro bitmaps.

But it's not possible to solve it, even with a Bitmap class: once the Bitmap class obtains the pointer, the bitmap must not be moved, otherwise the pointer will be invalid.

SiegeLord
axilmar said:

1) resources are loaded before the game begins

As many people have pointed out, this is orthogonal to using a resource manager or not.

Quote:

2) careful management of vram is required

I think you overstate the care needed. You get many benefits of a resource manager (the simple std::map<string, ALLEGRO_BITMAP*> variety) even with infinite VRAM simply because resource manager avoids loading identical files multiple times.

Quote:

3) using tree maps or hash maps may cause performance problems during the game

As many people have pointed out, you can cache the lookup in a member variable of the class. This is a straw-man argument.

Quote:

In a use case you describe, tree/hash maps are only required when the resources are loaded and passed to the created objects.

I still don't understand how I am being misunderstood. That's the entire point. Resource managers are needed when dynamic objects are created that use resources.

Seriously... this is ridiculous. I am going to write up a fully functioning resource manager example, and ask you to modify the code with your technique. Then it'll be clear exactly what I and you mean.

Edgar Reynaldo
axilmar said:

But it's not possible to solve it, even with a Bitmap class: once the Bitmap class obtains the pointer, the bitmap must not be moved, otherwise the pointer will be invalid.

class Bitmap {
//...

   operator BITMAP* () {return bitmap;}
};

Problem solved. Every time you need your BITMAP* you get it from your Bitmap. Then it will never be invalid.

SiegeLord

Alright, here it is:

main.cpp#SelectExpand
1#include <allegro5/allegro.h> 2#include <allegro5/allegro_image.h> 3#include <map> 4#include <string> 5#include <vector> 6#include <iostream> 7 8using std::cout; 9using std::map; 10using std::string; 11using std::pair; 12using std::vector; 13 14ALLEGRO_BITMAP* verbose_load(const char* filename) 15{ 16 cout << "Loading: " << filename << std::endl; 17 return al_load_bitmap(filename); 18} 19 20struct SResourceManager 21{ 22 ALLEGRO_BITMAP* Load(string filename) 23 { 24 BitmapsIt it = Bitmaps.find(filename); 25 if(it == Bitmaps.end()) 26 { 27 ALLEGRO_BITMAP* bmp = verbose_load(filename.c_str()); 28 if(!bmp) 29 cout << "Couldn't load: " << filename << std::endl; 30 assert(bmp); 31 Bitmaps.insert(pair<string, ALLEGRO_BITMAP*>(filename, bmp)); 32 return bmp; 33 } 34 else 35 { 36 return it->second; 37 } 38 } 39 40private: 41 typedef map<string, ALLEGRO_BITMAP*>::iterator BitmapsIt; 42 map<string, ALLEGRO_BITMAP*> Bitmaps; 43}; 44 45struct SObject 46{ 47 SObject(SResourceManager& manager, string name, ALLEGRO_CONFIG* config) 48 { 49 const char* bitmap_name = al_get_config_value(config, name.c_str(), "bitmap"); 50 assert(bitmap_name); 51 Bitmap = manager.Load(bitmap_name); 52 cout << "Created object: " << name << std::endl; 53 } 54private: 55 ALLEGRO_BITMAP* Bitmap; 56}; 57 58int main() 59{ 60 al_init(); 61 al_init_image_addon(); 62 63 SResourceManager manager; 64 vector<SObject*> objects; 65 66 ALLEGRO_CONFIG* objects_config = al_load_config_file("objects.ini"); 67 assert(objects_config); 68 69 ALLEGRO_CONFIG_SECTION* it; 70 const char* section = al_get_first_config_section(objects_config, &it); 71 72 while(section != NULL) 73 { 74 string section_name(section); 75 if(section_name != "") 76 { 77 objects.push_back(new SObject(manager, section_name, objects_config)); 78 } 79 80 section = al_get_next_config_section(&it); 81 } 82 83 al_destroy_config(objects_config); 84 85 return 0; 86}

There is very limited error detection and cleanup, but those things are separate from the question at hand. The code opens up an objects.ini file, which contains object names followed by bitmaps that represent an object, like so:

objects.ini#SelectExpand
1[apple] 2bitmap = apple.bmp 3 4[pear] 5bitmap = pear.bmp 6 7[poisoned_apple] 8bitmap = apple.bmp

The task of the code is to create each object and load its associated bitmap. Note that two of the objects share the same bitmap. However, if you run the code you get this output:

Loading: apple.bmp
Created object: apple
Loading: pear.bmp
Created object: pear
Created object: poisoned_apple

Although three objects were created, only two bitmaps were loaded.

So yeah... please show me how you'd solve this issue in your code. I've attached the current versions of the files to this post. The code expects the objects.ini file to be in the same directory, and the bitmap paths to be specified relative to the working directory. Keep in mind that I'll be testing your code with a different objects.ini file and different bitmaps. My code can handle such case, can yours?

EDIT:

And before you claim that my code doesn't do the pre-loading bit you want... think of the code in question as the pre-loading code. It's a bit inefficient in the sense that you'll have to delete the objects after you're done, but it gets the job done.

Edgar Reynaldo
SiegeLord said:

So yeah... please show me how you'd solve this issue in your code. I've attached the current versions of the files to this post. Keep in mind that I'll be testing your code with a different objects.ini file and different bitmaps. My code can handle such case, can yours?

Them's are fightin' words, pilgrim!

Get Ready To Rummmmmmbbbbbblllllle!

;D

Oscar Giner
axilmar said:

So, I imagine you are talking about something like Allegro 4's resource files, aren't you? so, you are talking about index constants produced by the tools.

When I said "compile" (and this is why I put it between quotes) I wasn't talking about the C/C++ source code, but about the resources themselves. The same as you compile windows resources into a binary form. The tools just output a format where all extra info like resource names is lost, and only the indexes are kept. The code never uses those indexes directly: if you have for example a map editor, the editor will output a map format that refers to the resources by index.

All this of course if you really care about the performance of hash maps or trees, which you shouldn't even on a 10 years old CPU. It has the drawback that adding new resources means you have to regenerate all the game files (maps, animations...) so the indexes get updated.

Note: the point is not that you should use this (unless you're working with ancient hardware) but that it's possible to have a resource manager without the supposed performance problems you claim about hash maps or search trees.

Quote:

But it's not possible to solve it, even with a Bitmap class: once the Bitmap class obtains the pointer, the bitmap must not be moved, otherwise the pointer will be invalid.

You know, there are ways to inform the Bitmap objects that the bitmap has changed.

axilmar

Problem solved.

Not solved at all:

BITMAP *bmp = load_bitmap(bitmap1);
Bitmap bitmap1(bmp);
bmp = move_bitmap_to_system_ram(bmp);
draw_bitmap(bitmap1); //KABOOM: bitmap pointer is invalid.

SiegeLord said:

please show me how you'd solve this issue in your code.
think of the code in question as the pre-loading code.

I think you have seriously misunderstood the thread. People are not talking about using a resource manager at the loading stage, but during the actual game. The code that has been suggested as an example of good design is this:

#SelectExpand
1class bullet : public C_bullet{ 2 private: 3 ALLEGRO_BITMAP* bm1; 4 ALLEGRO_SAMPLE* sn1; 5 public: 6 bullet(RES_MANAGER *res_manager): 7 bm1 (res_manager->bm_loader("bullet.png")), 8 sn1 (res_manager->sn_loader("sound1.ogg")) 9 {} 10 11 ~bullet(){ 12 res_manager->unneeded_res("bullet.png"); 13 res_manager->unneeded_res("sound1.ogg"); 14 } 15};

I repeat, in case it was not understood: the above is proposed as a solution DURING THE ACTUAL GAME. Not at the loading stage.

I find it bad design to use a resource manager during the actual game, for the reasons I outlined above.

When I said "compile" (and this is why I put it between quotes) I wasn't talking about the C/C++ source code, but about the resources themselves. The same as you compile windows resources into a binary form. The tools just output a format where all extra info like resource names is lost, and only the indexes are kept. The code never uses those indexes directly: if you have for example a map editor, the editor will output a map format that refers to the resources by index.

But in a Win32 application, resources compiled into a binary form are accessed either by number or by name.

So, I ask you again: how would you access those resources in your game, if not by index or by name?

Quote:

All this of course if you really care about the performance of hash maps or trees, which you shouldn't even on a 10 years old CPU.

I think you are mistaken. If a tree map or hash map has a lot of items, as in thousands of items, then searching it adds noticeable delays to a video game that must be updated every 16 milliseconds (for a 60 frames per second update), when coupled with all the other things that must be calculated, especially for devices like consoles and phones.

Quote:

but that it's possible to have a resource manager without the supposed performance problems you claim about hash maps or search trees.

Sure you can, if you precompile the tree or hash map into an array. But, if you do this at every created object:

class Bullet {
    BITMAP *myBitmap;
    Bullet(ResourceManager &rm) {
        myBitmap = resourceManager[RESOURCE_EXPLOSION];
    }
}

int main() {
    ResourceManager rm;
    rm.loadResources("resource1.res");
    ...
    bullets.push_back(new Bullet(rm));
}

Why not do this, which is exactly the same and spares you the trouble of using a resource manager and compiling resources?

class Bullet {
    BITMAP *myBitmap;
    Bullet(BITMAP *bmp) {
        myBitmap = bmp;
    }
}


int main() {
    BITMAP *explosionBitmap = load_bitmap("explosion.bmp");
    ...
    bullets.push_back(new Bullet(explosionBitmap));
}

Oscar Giner
axilmar said:

So, I ask you again: how would you access those resources in your game, if not by index or by name?

Read what I said again. The code itself never has hardcoded any resource index (if you want that then of course you'll need something similar to Windows resources: a generated header with all the #define's for the indexes, but that's bad since it means you need to recompile your game when resources change). The resource indexes needed are always stored in the game files (generated config files, map/level files, item/enemy/whatever files...).

Quote:

I think you are mistaken. If a tree map or hash map has a lot of items, as in thousands of items, then searching it adds noticeable delays to a video game that must be updated every 16 milliseconds

Do you know that search time of a binary search tree is just O(log2(n)), right? It's very efficient when the number of items is very high. And a hash table is even more efficient, you can get O(1) in most searches with a good hash function.

Edgar Reynaldo
axilmar said:

Not solved at all:

BITMAP *bmp = load_bitmap(bitmap1);
Bitmap bitmap1(bmp);
bmp = move_bitmap_to_system_ram(bmp);
draw_bitmap(bitmap1); //KABOOM: bitmap pointer is invalid.

You're overlooking the really obvious here - don't use BITMAP*'s directly, that's why you're having problems.

Bitmap bmp("PrettyPicture.bmp" , VIDEO);
bmp.MoveToSystemRam();
blit((BITMAP*)bmp , screen , 0 , 0 , 0 , 0 , bmp.W() , bmp.H());

With an implicit cast from Bitmap to BITMAP* it's even easier, and then you always have a valid BITMAP*.

If you're going to write a Bitmap class, at least have it take full ownership of the BITMAP* it uses. If you want to call destroy_bitmap((BITMAP*)bmp); that's your own dumb fault.

axilmar said:

I think you have seriously misunderstood the thread. People are not talking about using a resource manager at the loading stage, but during the actual game. The code that has been suggested as an example of good design is this:
...
I repeat, in case it was not understood: the above is proposed as a solution DURING THE ACTUAL GAME. Not at the loading stage.

A single lookup during a constructor call is not expensive at all, and you don't need to repeatedly look up the same resource once you've acquired it. And a resource manager allows you to quickly and easily load and unload levels at a time.

axilmar said:

I think you are mistaken. If a tree map or hash map has a lot of items, as in thousands of items, then searching it adds noticeable delays to a video game that must be updated every 16 milliseconds (for a 60 frames per second update), when coupled with all the other things that must be calculated, especially for devices like consoles and phones.

I think you're over estimating how many lookups actually need to be made. You don't have to perform a lookup for every constructor call if you don't want to. You can easily use a lazy lookup scheme with a static resource pointer for shared resources. You only have to look it up with the resource manager once.

I think you're avoiding the main problem that a resource manager solves - simple and quick loading and unloading of resources.

Take your example :

int main() {
    BITMAP *explosionBitmap = load_bitmap("explosion.bmp");
    ...
    bullets.push_back(new Bullet(explosionBitmap));
}

You will have more than just a few resources to load and unload during your game :

int main() {
   ResourceManager rm;
   rm.LoadLevel("Level1.txt");

   BITMAP* explosion = rm.GetBitmap("Explosion");
   // dynamically create new explosions using the single reference acquired from the lookup

   rm.UnloadLevel();
}

Do you really think that manually loading every single resource is a better solution than using a manager that can manage a set of resources by itself?

A further bonus of a resource manager is that it can throw an exception and log an error if there is a request made for a resource it does not have. That may not give you compile time error checking, but it's better than nothing.

axilmar

Read what I said again. The code itself never has hardcoded any resource index

Then how are you going to use those resources in the game?

Quote:

Do you know that search time of a binary search tree is just O(log2(n)), right? It's very efficient when the number of items is very high. And a hash table is even more efficient, you can get O(1) in most searches with a good hash function.

But you have to take into account the cache and the memory access speed. It's not only the algorithm that counts. That is, if you want to have a 60 FPS game on a device like an iPad, for example. And that's valid only if the tree is balanced.

You're overlooking the really obvious here - don't use BITMAP*'s directly, that's why you're having problems.

But the resource manager will do use BITMAP*s directly.

Quote:

A single lookup during a constructor call is not expensive at all.

Even if you have a game where you have lots of objects (like bullets, for example) created all the time?

Even if it is not that expensive, what about the other issues? type and name safety, for example?

Quote:

I think you're over estimating how many lookups actually need to be made. You don't have to perform a lookup for every constructor call if you don't want to.

But the suggested solution was to use the resource manager within the constructor of an object in order to get the resource.

Quote:

I think you're avoiding the main problem that a resource manager solves - simple and quick loading and unloading of resources.

Does loading and unloading of resources really need a resource manager? why not use a simple context structure with shared pointers to loaded resources?

Quote:

Do you really think that manually loading every single resource is a better solution than using a manager that can manage a set of resources by itself?

But if you're going to request the resource from the resource manager, why not load it directly? i.e. why this:

ResourceManager rm;
rm.LoadLevel("Level1.txt");
BITMAP* explosion = rm.GetBitmap("Explosion");

And not this?

BITMAP* explosion = load_bitmap("Explosion");

Since you have to refer to the resource from the code anyway, why is a resource manager needed?

Quote:

Do you really think that manually loading every single resource is a better solution than using a manager that can manage a set of resources by itself?

In the example you posted, you are loading every single resource, by doing this: BITMAP* explosion = rm.GetBitmap("Explosion");

Quote:

A further bonus of a resource manager is that it can throw an exception and log an error if there is a request made for a resource it does not have. That may not give you compile time error checking, but it's better than nothing.

The same thing can be done by a simple loading function.

SiegeLord
axilmar said:

I repeat, in case it was not understood: the above is proposed as a solution DURING THE ACTUAL GAME. Not at the loading stage.

Are you being daft on purpose? The only reason I said that my code was the pre-loading code is because you yourself wanted everything being pre-loaded. My code, my edit notwithstanding, was exactly meant to show what happens during a game.

With that clarification over, my challenge still stands. I am sure at this point that you will not do it because: a) it's impossible to do it using your method, b) you're a demagogue.

Edgar Reynaldo
axilmar said:

But the resource manager will do use BITMAP*s directly.

You have to remember who owns the BITMAP* though, it's the manager, not the user of the pointer. And you could just as easily have the manager return a pointer to the Bitmap which would always give you the correct BITMAP* when asked for it. (Use the Bitmap, not the BITMAP*).

axilmar said:

Even if you have a game where you have lots of objects (like bullets, for example) created all the time?

I already answered this, you can still use a static pointer that is only looked up once, if it is really such a concern.

axilmar said:

But the suggested solution was to use the resource manager within the constructor of an object in order to get the resource.

Like I said, not necessary if performance is really a concern, and your lookups are actually your bottleneck.

axilmar said:

Does loading and unloading of resources really need a resource manager? why not use a simple context structure with shared pointers to loaded resources?

Well, you're going to have to store your resources somewhere, why not in a manager that can unload them all at once instead of one at a time?

Assume you use shared pointers instead - if you dynamically load resources, you still need a container with all the shared pointers in it so they stay loaded as long as necessary. That's all a manager is, a glorified container with the ability to load and unload resources when necessary. I think this is really just splitting hairs anymore. Both should work equally well.

axilmar said:

But if you're going to request the resource from the resource manager, why not load it directly? i.e. why this:

ResourceManager rm;
rm.LoadLevel("Level1.txt");
BITMAP* explosion = rm.GetBitmap("Explosion");

And not this?

BITMAP* explosion = load_bitmap("Explosion");

Since you have to refer to the resource from the code anyway, why is a resource manager needed?

Because then you don't need to destroy every resource manually, you can just call rm.UnloadLevel(); and be done with it. And if you use shared pointers, you still need a container to hold them in so they don't go out of scope and so you can get them when you need to copy them.

axilmar said:

In the example you posted, you are loading every single resource, by doing this: BITMAP* explosion = rm.GetBitmap("Explosion");

No, that's just the lookup. The loading is done in LoadLevel. And when you're done you call UnloadLevel. Simple, neat, quick.

axilmar said:

The same thing can be done by a simple loading function.

And if you have dynamic resources then you need a container to hold them in. That's all a resource manager is! How would you combine scripting with shared pointers? You would still have to have a container for them.

I don't see what we're arguing about here. ???

bamccaig

See here.

The smart pointer solution isn't enough in itself to work for a dynamic game (i.e., one that isn't hard coded) while simultaneously sharing resources.

Let's say that you have a lot of resources and want to only load the ones that are needed for the current level. When you switch levels, how will you know which resources are needed, which ones need to be loaded anew, and which ones can be cleaned up? And where will you get those already loaded resources? A shared pointer is insufficient here. Sure, you have already loaded the resource, but where is it? How do you get the shared pointer if not from a resource manager?

You can't address it by name. Everything about it is dynamically configured. The static code can't know what it's dealing with. For all intents and purposes, it's an orphaned object at this point because there's no "repository" of resources to fetch it from. The last object that used it from the previous level will be destroyed, dropping the smarter pointer reference count to 0, causing that resource to be destroyed also. Now you have to reload all of shared resources all over again for nothing. Talk about waste.

A resource manager can solve all of these problems easily. Once again, anything you come up with to explain how to solve this problem will be a resource manager by a different name. :P What you're describing is premature optimization.

ZOMG, what if we decide to port it to my wrist watch?! No. Choose efficient data structures and algorithms and the compiler should already do a good job of keeping things efficient. A good abstract design should already make it easy to make backend changes to speed things up if necessary without a total rewrite. If you have to hack it up on some obscure platform because you're pushing the limits of what it can do then hack it up on that platform. Reliability and integrity are more important than performance. It doesn't matter how quickly you calculate the wrong answer.

Oscar Giner
axilmar said:

And that's valid only if the tree is balanced.

I think it's obvious it's a balanced tree. It's stupid implementing a tree that's not balanced, specially since the tree is constant (no insertion or deletion of elements after the initial load). So are you telling me it was necessary that I said that explicitly? Come on, this is getting ridiculous (even more).

Quote:

But you have to take into account the cache and the memory access speed.

Yes, and that may be a good reason to use a hash table over a tree, since it's much more cache friendly (unless somehow you make all the nodes in the tree to be stores consecutively in memory).

Quote:

That is, if you want to have a 60 FPS game on a device like an iPad, for example.

Even if accessing a tree was as slow as you say (which it isn't), you only access it the first time you want to get a resource, after that, you have the resource object stored somewhere, no need to access it through the resource manager all the time.

Quote:

Then how are you going to use those resources in the game?

I already explained that, read please. Are you making fun of us? I'm starting to think that (and not only for this thread).

axilmar

To keep the long story short, keep Matthew's bills down, and avoiding replying individually to everyone, here is the deal.

The problem.

If a resource manager is used, the programmer has to do the following:

BITMAP *res1 = (BITMAP *)resourceManager.getResource("res1");
BITMAP *res2 = (BITMAP *)resourceManager.getResource("res2");
BITMAP *res3 = (BITMAP *)resourceManager.getResource("res3");

The above has:

  • performance issues, when done repeatedly in a fast-paced action game.

  • type safety issues. 'Res1' may not be a bitmap.

  • name safety issues. 'Res1' may be erroneously typed 'Resl', for example.

The solution.

So, to avoid the problems, my chosen way to deal with resources is the following:

BITMAP *res1 = load_bitmap("res1");
BITMAP *res2 = load_bitmap("res2");
BITMAP *res3 = load_bitmap("res3");

The advantages of this over the resource manager are:

  • better performance; no lookups are needed during the game.

  • more type safety. The above code will not compile if the resource is not a bitmap.

  • more name safety; if I use the variable 'res1' in my code, instead of the string "res1", there are less chances of a bug because of identifier mistyping.

Requirement 1 - caching individual resources.

Now, if one wants to achieve caching of resources, i.e. to avoid loading resources if already loaded, one can use simple solutions, like this:

shared_ptr<BITMAP> res1, res2, res3;
load_bitmap_if_not_yet_loaded(res1, "res1");
load_bitmap_if_not_yet_loaded(res2, "res2");
load_bitmap_if_not_yet_loaded(res3, "res3");

The above solution achieves the caching effect without sacrificing performance, type and name safety.

Requirement 2 - caching resources of a configuration file.

If one wants to load multiple resources specified in a text file, one can do the following:

shared_ptr<BITMAP> res1, res2, res3;
ALLEGRO_CONFIG *config = load_config("level1.txt");
load_bitmap_if_not_yet_loaded(res1, get_config_value("res1"));
load_bitmap_if_not_yet_loaded(res2, get_config_value("res2"));
load_bitmap_if_not_yet_loaded(res3, get_config_value("res3"));

The above solution achieves the loading resources from files dynamically without sacrificing performance, type and name safety.

Requirement 3 - unloading a whole bunch of resources at once.

For even better design, multiple resources can be divided into structures, like this:

#SelectExpand
1struct BulletBitmaps { 2 shared_ptr<BITMAP> explosion1; 3 shared_ptr<BITMAP> explosion2; 4 shared_ptr<BITMAP> explosion3; 5}; 6 7struct SpaceshipBitmaps { 8 shared_ptr<BITMAP> spaceship1; 9 shared_ptr<BITMAP> spaceship2; 10 shared_ptr<BITMAP> spaceship3; 11}; 12 13struct Resources { 14 shared_ptr<BulletBitmaps> bulletBitmaps; 15 shared_ptr<spaceshipBitmaps> spaceshipBitmaps; 16};

If you want to dispose of a whole bunch of resources at once, you can do this:

Resources res;
...
res.bulletBitmaps.reset(); //get rid of bullet bitmaps
res.spaceshipBitmaps.reset(); //get rid of spaceship bitmaps

Why reference counting is unavoidable.

A resource manager cannot unload resources anyway until it is explicitly told to do so, which requires reference counting of resources, so using reference counting is inevitable.

Since reference counting must be used, the use of shared_ptr is mandated.

Why using maps of identifiers to resources is not required.

The whole debate is based on the observation that if a resource is named by an identifier in a program, then this identifier better be a variable and not a string, in order not to lose performance, type safety and name safety.

In other words, for each time a resource manager is used:

Type var;
var = resourceManager.getResource("resource name");

The same thing can be achieved without a resource manager:

Type var;
var = load_resource_if_not_loaded(var, "resource name");

What about unnamed resources?

Well, if a resource is unnamed (i.e. it has no identifier that it can be addressed by), then the discussion is pointless, because we are talking about named resources. A resource manager cannot be used with unnamed resources.

An unnamed resource, in this case, will be loaded in a different way; for example, by serially reading a file and constructing resources one after the other.

Isn't what you propose a resource manager?

Yes it is! albeit of a different kind!

TL;DR

Each request to a resource manager can be replaced by a variable in order to improve performance, type and name safety.

Audric

Type safety:

BITMAP *ship_north= resourceManager.getBitmap("res1");
SAMPLE *death_sound = resourceManager.getSample("res2");

Oscar Giner
axilmar said:

name safety issues. 'Res1' may be erroneously typed 'Resl', for example.

In your solution the same can happen.

Quote:

more name safety; if I use the variable 'res1' in my code, instead of the string "res1", there are less chances of a bug because of identifier mistyping.

In the resource manager solution you also use the variable res1.

Type safety (or lack of) is the same in both solutions.

Quote:

better performance; no lookups are needed during the game.

Whats the time of that look up, compared to the time of the disk access needed to load the resource? You don't lose any performance, loading the resource takes 99.99% of the time. Trying to optimize the other 0.001% is stupid.

Ans what Audric said about type safety.

So your solution doesn't solve anything.

And BTW, with a resource manager, the load functions won't return you directly a BITMAP *, but a Bitmap class, that holds this bitmap.

SiegeLord
axilmar said:

A resource manager cannot unload resources anyway until it is explicitly told to do so, which requires reference counting of resources, so using reference counting is inevitable.

Since reference counting must be used, the use of shared_ptr is mandated.

I have two issues with this. Firstly, you can structure your game such that the game's logic determines when the resources are guaranteed to be unloaded. In my newest iteration of resource managers, there is a resource manager per level and all objects within a level use that resource manager. When the level is unloaded, all the resources cached within the resource manager are guaranteed to be unused, and thus are freed. This obviously has the issue of increasing individual level load times, but it has the benefit of not having to micromanage resource usage. It also leads to smoother in-level performance (see the discussion in the next paragraph).

This brings me to the second issue. Using a shared_ptr for resources loaded from disk makes no sense. I imagine that when you shared_ptr loses all of its ownership, then the resource if unloaded. Now your magical function load_resource_if_not_loaded will load the resource anew when it is called again. This will lead to a horrible performance for objects which sometimes exist in the level, but often times don't (e.g. player's bullets) because they will have to load the resource every time the first bullet is created! You'd need to create some bizzare secondary cache that shared_ptr 's unload their resources to, and that load_resource_if_not_loaded try to load their resources from first. You'd definitely want to implement your own reference counting that holds on to resources for future reuse, if you must do reference counting.

Quote:

What about unnamed resources?

What about resources that have names only at runtime, like in my example which you still refuse to implement using your method. And no, load_resource_if_not_loaded will not work, and neither will the 'BulletBitmaps' trick.

And I just realized even if they were known at runtime... guess what! Your method fails.

struct BulletBitmaps {
    shared_ptr<BITMAP> explosion1;
    shared_ptr<BITMAP> explosion2;
};

BulletBitmaps resources;

load_bitmap_if_not_yet_loaded(resources.explosion1, "explosion1.bmp");
load_bitmap_if_not_yet_loaded(resources.explosion2, "explosion1.bmp");

or

struct PlayerBitmaps {
    shared_ptr<BITMAP> explosion;
};

struct EnemyBitmaps {
    shared_ptr<BITMAP> explosion;
};

PlayerBitmaps player_resources;
EnemyBitmaps enemy_resources;

load_bitmap_if_not_yet_loaded(player_resources.explosion, "explosion1.bmp");
load_bitmap_if_not_yet_loaded(enemy_resources.explosion, "explosion1.bmp");

In both cases your method will load identical bitmaps multiple times!

axilmar

In your solution the same can happen.
In the resource manager solution you also use the variable res1.
Type safety (or lack of) is the same in both solutions.

Not the same. In my solution, I use the string identifier once, and then use the variable thereafter. In the resource manager solution, the identifier string may be used more than once.

Quote:

Trying to optimize the other 0.001% is stupid.

If the other 0.001% is in reality 0.01% or even worse, 0.1%, and applied 60 times per second, then there might be an issue.

SiegeLord said:

This brings me to the second issue. Using a shared_ptr for resources loaded from disk makes no sense. I imagine that when you shared_ptr loses all of its ownership, then the resource if unloaded. Now your magical function load_resource_if_not_loaded will load the resource anew when it is called again. This will lead to a horrible performance for objects which sometimes exist in the level, but often times don't (e.g. player's bullets) because they will have to load the resource every time the first bullet is created! You'd need to create some bizzare secondary cache that shared_ptr 's unload their resources to, and that load_resource_if_not_loaded try to load their resources from first. You'd definitely want to implement your own reference counting that holds on to resources for future reuse, if you must do reference counting.

You missed the global variable that will keep the object around with reference count = 1.

Quote:

What about resources that have names only at runtime, like in my example which you still refuse to implement using your method. And no, load_resource_if_not_loaded will not work, and neither will the 'BulletBitmaps' trick.

Then you need a map and a few functions, obviously. But that's not what I discuss here.

Quote:

In both cases your method will load identical bitmaps multiple times!

You organized your resources in the wrong way. It's like you used two different resource managers. Each resource that is common should have one variable holding it.

With my method, you can achieve loading a resource twice, if you want one of the resources to be modified at runtime. You cannot do that with a resource manager: you have to duplicate the resource externally.

bamccaig
axilmar said:

[You're absolutely right, and I don't agree with you, but I'm still right.]

::)

Also: anything that you can do without a resource manager you can do with a resource manager. ::)

SiegeLord
axilmar said:

Then you need a map and a few functions, obviously. But that's not what I discuss here.

Yeah... you don't discuss real games, but some kind of fantasy programs. Real games are rife with runtime names for resources.

Quote:

Each resource that is common should have one variable holding it.

You can't know that it is common until runtime!

Quote:

With my method, you can achieve loading a resource twice, if you want one of the resources to be modified at runtime.

With your method you have to load each resource twice, as I've shown. With a resource manager you get sharing by default, and you can get a unique copy, if need be, by cloning the resource.

Quote:

you have to duplicate the resource externally.

Yes. Why is that bad? Also, in my games I've never had to clone a resource to date.

So far it seems that you are trying to solve problems that are not there with a more verbose solution that has severe limitations and requires a lot of thought to get to work. A resource manager is a simple concept that pays dividends with only a few lines of reusable code (I've reused essentially the same bitmap resource manager in 5 games now) and is robust to many use cases.

Oscar Giner
axilmar said:

Not the same. In my solution, I use the string identifier once, and then use the variable thereafter. In the resource manager solution, the identifier string may be used more than once.

Yes the same. You have load_resource_if_not_loaded that you'll call more times. Actually you'll call that function when, if using a resource manager, you'd call the resource manager getter method. So the total number of times a resource is accessed by name is the same in both methods.

Quote:

If the other 0.001% is in reality 0.01% or even worse, 0.1%, and applied 60 times per second, then there might be an issue.

err... like... no? Even if it was 1%. If loading takes 5s, that 1% increases it to 5.05s (this is the load time of all resources, not of a single one). So what? Are you telling my that a level load time of an extra 0.05s is worth the effort of a more unmanageable system? (and lets face, this is not the case, accessing an element in a tree of thousand of elements takes less than a ms, it's just 16 memory accesses).

And then the second part... why applied 60 times per second? You only load resources once :/ But even if you want some kind of texture streaming engine, UE3 engine does it and works on mobile devices and on current consoles.

Thread #608680. Printed from Allegro.cc