Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » OOP Design and Resource Management

This thread is locked; no one can reply to it. rss feed Print
 1   2   3 
OOP Design and Resource Management
Edgar Reynaldo
Major Reynaldo
May 2007
avatar

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
Member #2,207
April 2002
avatar

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
Member #1,204
April 2001

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
Member #2,207
April 2002
avatar

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
Major Reynaldo
May 2007
avatar

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
Member #1,204
April 2001

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
Member #7,827
October 2006
avatar

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.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

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
Member #7,536
July 2006
avatar

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
Member #2,207
April 2002
avatar

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
Member #1,204
April 2001

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
Member #907
January 2001

Type safety:

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

Oscar Giner
Member #2,207
April 2002
avatar

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
Member #7,827
October 2006
avatar

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!

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

axilmar
Member #1,204
April 2001

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
Member #7,536
July 2006
avatar

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
Member #7,827
October 2006
avatar

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.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Oscar Giner
Member #2,207
April 2002
avatar

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.

 1   2   3 


Go to: