0
\$\begingroup\$

Say I have this Cache<typename Resource> class template, which is a resource cache that contains an std::map<std::string, Resource>, mapping strings to resources.

I can many different kinds of resources: sounds, textures, geometry etc. So I need to have something like a container of all of these caches:

struct ResourceManager {
  Cache<Sound*> _soundCache;
  Cache<Mesh*> _meshCache;
  Cache<Texture2D> _textureCache;
};

A Mesh object might need to query Texture objects from the texture cache, so working with these caches is possible in two ways: Either pass a pointer to _textureCache to every Mesh object, perhaps via its constructor (this approach is pretty clumsy), or define a ResourceManager object globally, so every class can use its caches all the time.

Has this issue ever been brought up for discussion? How should I deal with it?

What do most engines do? Where do they store these caches? Is it viable to do something like this? :

~GameEngine.h~

...
#include "ResourceManager.h"

//global object
resourceManager g_resourceManager;

class GameEngine {
  ...
};
\$\endgroup\$
2

2 Answers 2

1
\$\begingroup\$

Like most things in software development, the answer is "it depends". Only you will be able to decide which is the right approach because you have intimate knowledge of what you need to accomplish and whether it is worth the extra effort to make it right.

Properly designed, you might use both methods. Global anything in code is typically a bad idea. Though it should not and cannot be avoided entirely, anytime you find yourself considering something for being global, think it over carefully because it will implicitly tie things together, which most often leads to greater complications than explicit connections between your code.

It's always a good idea to limit the knowledge of one piece of code to another as much as possible. Even though it means more thought and more work up-front to connect things together and will be a bit more complicated to code your classes, it will simplify the use of those classes. It also helps to avoid problems later when you need to make changes to or reuse either part.

You want your code to be "plug 'n play" as much as possible. Compromises always have to be made though. It's a trade between get it done fast or get it done well.

See Coupling and Cohesion. Always try to aim for low coupling and high cohesion.

Sorry this is a round-about answer and I can't explain this in too much detail for you. It is a rather deep topic and will take quite a bit of experience making your own mistakes before you really get it. Just keep these things in mind.

To be more direct, if you have to ask then just make it work and don't worry about it too much. It seems like you already have a decent idea of how to make it work, so just go with it. Refactor it later to be better when you find there are issues with your current implementation. What most good engines do is evolve as needed. I don't expect you want to spend several years developing your engine to perfection before you produce your first game.

\$\endgroup\$
1
\$\begingroup\$

The simplest way is to pass STL shared pointers of resources around to your objects, it's fairly painless but hard to catch errors in and it's not particularly obvious what system/object owns the particular resource.

Personally I prefer the method of the resource system giving out handles, a handle (in this case) just something comprised an integer value that some other system can use to bind a texture, play a sound or bind a vertex buffer.

This blog post from Molecular Musings has some pretty good insight: https://molecularmusings.wordpress.com/2013/05/02/adventures-in-data-oriented-design-part-3a-ownership/

\$\endgroup\$
3
  • \$\begingroup\$ How does passing pointers make it hard catching errors? \$\endgroup\$
    – McLovin
    Commented Aug 3, 2015 at 8:15
  • \$\begingroup\$ Also, why does it matter if I pass shared pointers and not regular pointers? \$\endgroup\$
    – McLovin
    Commented Aug 3, 2015 at 15:13
  • \$\begingroup\$ Passing pointers make it difficult to catch stale data access and, looking at it from the interface level, to tell what's responsible for actually clearing up the resource. Shared pointers clear this up a little bit by having reference counted garbage collection. Where as if you have a handle structure to reference a resource you have somewhere to put debug data and the opaqueness of the handle means that no outside systems have any way of destroying the resource. \$\endgroup\$
    – cfehunter
    Commented Aug 6, 2015 at 12:17

You must log in to answer this question.

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