GameDev General

Pages: 1234567
@Lumpkin
Yes. For PS3 they use MIPS assembler language to try and get speed out of the playstation systems. Learning it doesn't hurt nothing either. I had to learn assembler when I did my degree. I'm rusty on it as I don't use it in my everyday stuff, but still a good thing to learn.
Last edited on by closed account z6A9GNh0
I'm questioning whether I should replace the "shared_ptrs" in my resource manager, I've heard they're quite expensive.

They also can't be casted, and that's a huge problem, because my class looks a little like:

1
2
3
4
5
6
7
8
9
class ResourceManager
{
    // ...
    
    public:
        std::vector<std::shared_ptr<Resource<Mesh>>> meshes;
        std::vector<std::shared_ptr<Resource<Texture>>> textures;
        std::vector<std::shared_ptr<Resource<Model>>> models;
};
I'm questioning whether I should replace the "shared_ptrs" in my resource manager, I've heard they're quite expensive.


They have some overhead, but they're not "quite expensive" by any stretch of the imagination. If you need the reference counting, then they're a good and efficient class to use. If you don't need the reference counting then yeah it might be worthwhile to reconsider.


They also can't be casted, and that's a huge problem, because my class looks a little like:


Yes they can.

Are Resource<Mesh>/Resource<Texture>/Resource<Model> related? ie: Do they have a common parent class?

(note: being of the same template does not mean they are related. vector<int> and vector<float> have zero relationship and are two completely independent classes)
> I've heard they're quite expensive.

It is not primarily a question of expense. Designs involving pervasive shared ownership of resources tend to be brittle, inflexible designs.

Sometime back, there was a lounge thread running into several pages, which complained bitterly and volubly about shared pointer performance. When what one should have been really concerned about was the fundamentally flawed design that required the use of shared pointers anywhere and everywhere.


> They also can't be casted

http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast
Last edited on
Designs involving pervasive shared ownership of resources tend to be brittle, inflexible designs.


I think management of singular, constant resource lifetimes is one of the few cases where shared_ptr is actually a very strong option.

You want shared ownership because you want objects to own the resources they are using (to ensure they have control over their lifetimes), but you also want the manager to also maintain a reference to them so that when multiple entities request the same resource, they don't each load their own copy.


I agree that using shared_ptr for "everything" is a bad idea... but I say don't let fear of overuse prevent you from using it in places where it's warranted.
http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast
You're a life saver.

Are Resource<Mesh>/Resource<Texture>/Resource<Model> related? ie: Do they have a common parent class?
Nah.
Last edited on
If they're unrelated classes, then casting them is of no use to you.
How so? My function to get the resource looks something like so:

1
2
template<class T>
int getResource(std::string &location, enum ResourceType type, std::shared_ptr<Resource<T>> &resource);
Last edited on
> My function to get the resource looks something like so:

You appear to have three different kinds of resources. Can't you just have three different overloads of the function?
std::shared_ptr<Resource<T>> can only cast to std::shared_ptr<Resource<U>> if one is the base of the other. Also why does it return an int? Why not just return the pointer? Wait I get it, cause Resources don't share a base.

Edit: dynamic casting can only go from derived to base, static casting can go both ways.
Last edited on
> My function to get the resource looks something like so:

You appear to have three different kinds of resources. Can't you just have three different overloads of the function?


I don't know why I didn't think of that.

*bonks head* I should've had a V-8.
I'm just curious why you are doing:
1
2
template<class T>
int getResource(std::string &location, enum ResourceType type, std::shared_ptr<Resource<T>> &resource);


Shouldn't the enum be external so you have:
1
2
3
4
enum ResourceType{ /* symbolic constants */ };

template<class T>
int getResource(std::string &location, ResourceType type, std::shared_ptr<Resource<T> > &resource);


I've not used enumerations that much.
@BHXSpecter
IIRC its a C thing. http://ideone.com/ppKljQ
Goes to show you, learning C++ doesn't mean you learn C because only thing I have seen, and this is from other C programmers is typedef struct Name{};.
closed account (3qX21hU5)
@ Lumpkin

Would you mind sharing the implementation of your resource manager? I am curious about your approach.
Here's it all, I did go overboard with comments.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#include <vector>
#include "Texture.h"
#include "Model.h"

#ifndef _RESM_H_
#define _RESM_H_

/******************************************************
Copyright Antoine Henry 2013-2014

Resource Manager.h

 Desc: This header contains the ResourceManager class. Go to Management(.h/.cpp)
 if you'd like the global object that's delcared there.

 INFO ON "class ResourceManager": This resource manager is relatively simple.
 The Resource class template declared in Resource.h is put into a vector (used as dynamic array) of
 shared_ptrs, and they're loaded upon request. When used correctly, the ResourceManager
 polls the shared_ptrs every 5 minutes. If the shared_ptr is not referenced (.unique() == true),
 the resource is unloaded.

 NOTES TO USE CLASS CORRECTLY: If you'd like to use this class correctly,
 .reset() the shared_ptr when you're done with it so the ResourceManager object
 knows the pointer isn't being used when it's polled, and can be unloaded from memory
 without any problems. If you decide not to, the program will keep that resource in memory 
 until the program is shutdown.

 Unload() and UnloadAll() should NEVER be used by a non-management class.
 This can lead to a laggy program. Let the ResourceManager do all the work for you,
 that's why it's here in the first place, to manage your resources.
******************************************************/

class CF::Management::ResourceManager
{
	public:
		////////////////////////////////////////////////////////////////////////////////////////////
		// * void CF::Management::ResourceManager::Resource Manager()
		//
		// Desc: The class constructor, just initializes required variables.
		////////////////////////////////////////////////////////////////////////////////////////////
		ResourceManager()
		{
			// Initialize some variables to avoid errors.
			numResources = 0;
			prevTime     = 0;
			currentTime  = 0;
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		// * void CF::Management::ResourceManager::LoadResource()
		// Desc: Loads requested resource. (If it can find it on the HD, that is.)
		////////////////////////////////////////////////////////////////////////////////////////////
		template<class sParam, class tParam>
		int LoadResource(std::string &location, ResourceType type, sParam secondParam, tParam thirdParam)
		{
			bool resLoaded = isLoaded(location, type);

			if(resLoaded) {return -1;} // Tells the resource was already loaded.

			if(type == R_TEXTURE)
			{
				// Load the texture
				Video::Texture* texture = new Video::Texture(location.c_str());
				if(texture == nullptr) {return 1;} // If the texture failed to load...
					
				// Load the texture into a resource template, then to a shared_ptr object.
				std::shared_ptr<Resource<Video::Texture*>> tmpRes(new Resource<Video::Texture*>(texture, location.c_str()));

				// Add the shared_ptr to the texture database.
				textures.push_back(tmpRes);
			}

			else if(type == R_MESH)
			{
				switch(secondParam)
				{
					case FILE_OBJ:
						try {Mesh* mesh = new Mesh;}

						catch(std::bad_alloc& ba)
						{
							std::cerr << "Failure to allocate memory: " << ba.what() << '\n';
						}

						// Load the file into memory
						mesh->LoadOBJ(location.c_str());
					break;

					case FILE_OBJ_TEXTURED:
						try {Mesh* mesh = new Mesh;}

						catch(std::bad_alloc& ba)
						{
							std::cerr << "Failure to allocate memory: " << ba.what() << '\n';
						}

						mesh->LoadOBJT(location.c_str(), (CF::Video::Texture*)thirdParam);
					break;
				}
			}

			// Now since a resource was loaded, we can safely add 1 to the numResources variable...
			numResources++;

			return 0;
		}
		
		////////////////////////////////////////////////////////////////////////////////////////////
		// * void CF::Management::ResourceManager::PollResources()
		//
		// Desc: Polls the resource database every X minutes for unused resources. If it finds
		// any, we immediately unload it from memory, as it's no longer being used.
		////////////////////////////////////////////////////////////////////////////////////////////
		void PollResources()
		{
			// Every 5 minutes we check if a resource is referenced. If not, we unload it
			if((currentTime - prevTime) > 300000) // There are 300,000 miliseconds in 5 minutes 
			{
				prevTime = currentTime; // Reset

				for(unsigned int r = 0; r < textures.size(); r++)
				{
					if(textures[r].unique()) // If the texture isn't referenced any more...
					{
						// Unload the resource
						UnloadResource(textures[r]->getID(), R_TEXTURE);
					}
				}

				for(unsigned int r = 0; r < meshes.size(); r++)
				{
					if(meshes[r].unique())
					{
						// Unload the resource
						UnloadResource(meshes[r]->getID(), R_MESH);
					}
				}

				for(unsigned int r = 0; r < models.size(); r++)
				{
					if(models[r].unique())
					{
						// Unload the resource
						UnloadResource(models[r]->getID(), R_MODEL);
					}
				}
			}

			currentTime = SDL_GetTicks(); // Update...
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		// * void CF::Management::ResourceManager::UnloadResource()
		//
		// Desc: Unloads the requested resource.
		////////////////////////////////////////////////////////////////////////////////////////////
		int UnloadResource(std::string &location, ResourceType type)
		{
			bool resLoaded = isLoaded(location, type);

			if(!resLoaded) {return -1;} // Tells the resource isn't loaded.

			switch(type)
			{
				case R_TEXTURE:
					long i = -1; // The index of the resource with the requested location

					for(unsigned int r = 0; r < textures.size(); r++)
					{
						if(location == textures[r]->getID())
						{
							i = r; // Set the index
						}
					}

					if(i == -1) {return -1;} // There was no match
					std::cout << textures[i]->getID() << " Unloaded.\n";
					//delete textures[i]->getResource();    // Free the resource.
					textures[i].reset();                  // Free the resource's container
					textures.erase(textures.begin() + i); // Remove the resource from the vector.
				break;
			}

			return 0;
		}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
		////////////////////////////////////////////////////////////////////////////////////////////
		// * void CF::Management::ResourceManager::isLoaded()
		//
		// Desc: Checks if the requested resource is loaded in the database. Often used
		// in acompany with "LoadResource()".
		////////////////////////////////////////////////////////////////////////////////////////////
		const bool isLoaded(std::string &location, ResourceType type)
		{
			// Based on the type, we loop through our
			// resource data base and check if the texture is loaded.
			// (Look at each resource's HD location, and based on that we check if it matches the
			// resource we'd like to load's HD location. If it does, we return true)
			switch(type)
			{
				case R_TEXTURE:
					for(unsigned int r = 0; r < textures.size(); r++)
					{
						if(location == textures[r]->getID())
						{
							return true;
						}
					}
				break;

				case R_MESH:
					for(unsigned int r = 0; r < meshes.size(); r++)
					{
						if(location == meshes[r]->getID())
						{
							return true;
						}
					}
				break;

				case R_MODEL:
					for(unsigned int r = 0; r < models.size(); r++)
					{
						if(location == models[r]->getID())
						{
							return true;
						}
					}
				break;
			}

			// If there wasn't a match
			return false;
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		// * void CF::Management::ResourceManager::UnloadAll() [overloads]
		//
		// Desc: Get the requested resource from the database.
		////////////////////////////////////////////////////////////////////////////////////////////
		int getResource(std::string &location, std::shared_ptr<Resource<Video::Texture*>> &resource)
		{
			bool  isLoaded    = false;

			// Go through our "resource database" and look to see if our resource is loaded.
			// if it is, we set the resource to the one required.
			for(unsigned int r = 0; r < textures.size(); r++)
			{
				if(location == textures[r]->getID())
				{
					isLoaded = true;
					resource = textures[r];
				}
			}
			// If the resource isn't in the database, we return 1. We can't return a resource
			// that isn't loaded
			if(!isLoaded) {resource = nullptr; return 1;}

			return 0;
		}
		
		int getResource(std::string &location, std::shared_ptr<Resource<Mesh*>> &resource)
		{
			bool  isLoaded    = false;

			// Go through our "resource database" and look to see if our resource is loaded.
			// if it is, we set the resource to the one required.
			for(unsigned int r = 0; r < meshes.size(); r++)
			{
				if(location == meshes[r]->getID())
				{
					isLoaded = true;
					resource = meshes[r];
				}
			}
			// If the resource isn't in the database, we return 1. We can't return a resource
			// that isn't loaded
			if(!isLoaded) {resource = nullptr; return 1;}

			return 0;
		}
		
		int getResource(std::string &location, std::shared_ptr<Resource<Model*>> &resource)
		{
			bool  isLoaded    = false;

			// Go through our "resource database" and look to see if our resource is loaded.
			// if it is, we set the resource to the one required.
			for(unsigned int r = 0; r < models.size(); r++)
			{
				if(location == models[r]->getID())
				{
					isLoaded = true;
					resource = models[r];
				}
			}
			// If the resource isn't in the database, we return 1. We can't return a resource
			// that isn't loaded
			if(!isLoaded) {resource = nullptr; return 1;}

			return 0;
		}

		unsigned int getNumResources() const{return numResources;}
		
		////////////////////////////////////////////////////////////////////////////////////////////
		// * void CF::Management::ResourceManager::UnloadAll()
		//
		// Desc: Relatively simple function, it forces the unload of all resources in the database.
		// You should only call this at the end of the program, but it's already called in the
		// destructor. Only use this when you must.
		////////////////////////////////////////////////////////////////////////////////////////////
		void UnloadAll()
		{
			// Free our "resource database"
			for(unsigned int r = 0; r < textures.size(); r++)
			{
				UnloadResource(textures[r]->getID(), R_TEXTURE);
			}

			for(unsigned int r = 0; r < meshes.size(); r++)
			{
				UnloadResource(meshes[r]->getID(), R_MESH);
			}

			for(unsigned int r = 0; r < models.size(); r++)
			{
				UnloadResource(models[r]->getID(), R_MODEL);
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////
		// * void CF::Management::ResourceManager::~ResourceManager()
		//
		// Desc: The destructor of the manager. Just unloads everything.
		////////////////////////////////////////////////////////////////////////////////////////////
		~ResourceManager()
		{
			UnloadAll();
		}

	private:
		// "Resource database"... is exactly what it sounds like,
		// these vectors have all resources that are wanted loaded in
		// memory.
		std::vector<std::shared_ptr<Resource<Video::Texture*>>> textures; 
		std::vector<std::shared_ptr<Resource<Mesh*   >>> meshes;
		std::vector<std::shared_ptr<Resource<Model*  >>> models;
		//////////////////////////

		// Number of total resources left in the database.
		unsigned int numResources;

		// Time variables
		unsigned int prevTime;
		unsigned int currentTime;
};

#endif//_RESM_H_ 


Notice a few parts aren't complete, still changing a lot of it as I find new information on optimization on the internet.
Last edited on
1
2
3
4
5
6
7
8
						try {Mesh* mesh = new Mesh;}

						catch(std::bad_alloc& ba)
						{
							std::cerr << "Failure to allocate memory: " << ba.what() << '\n';
						}

						mesh->LoadOBJT(location.c_str(), (CF::Video::Texture*)thirdParam);


That's... interesting.
@cire
You mean you don't try to use the object even after it possibly fails to allocate memory for it?
1
2
3
void PollResources()
		{
			// Every 5 minutes we check if a resource is referenced. If not, we unload it 


What's all this? Why aren't you using a weak pointer if unreferenced resources are to be automatically unloaded? That is canonical, and will avoid the race condition that exists in your code.

Something like what is outlined below, perhaps? (Assuming that the resource manager is not going to be concurrently accessed from multiple threads).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <memory>
#include <map>
#include <string>
#include <stdexcept>

struct mesh { void load_obj( const std::string& location ) ; /* ... */ };
struct texture { /* ... */ };
struct model { /* ... */ };

struct resource_manager
{
    std::shared_ptr<mesh> load_mesh( const std::string& location /* , ... */ )
    {
        try
        {
            auto sh_ptr = std::make_shared<mesh>( /* ... */ ) ;
            sh_ptr->load_obj(location) ;
            if( meshes.emplace( location, sh_ptr ).second ) return sh_ptr ;
        }
        catch( const std::exception& ) { return {} ; } // failed to create/load/emplace
    }

    // likewise for texture and mesh
    std::shared_ptr<texture> load_texture( const std::string& location /* , ... */ ) ;
    std::shared_ptr<model> load_model( const std::string& location /* , ... */ ) ;


    std::shared_ptr<mesh> get_mesh( const std::string& location )
    {
        auto iter = meshes.find(location) ;

        if( iter != meshes.end() )
        {
            // try to get a shared_ptr from the weak_ptr
            if( auto sh_ptr = iter->second.lock() ) return sh_ptr ; // ok, not expired
            else meshes.erase(iter) ; // expired, remove from the map
        }

        return {} ; // not found
    }

    // likewise for texture and mesh
    std::shared_ptr<texture> get_texture( const std::string& location ) ;
    std::shared_ptr<model> get_model( const std::string& location ) ;

    private:
         std::map< std::string, std::weak_ptr<mesh> > meshes ;
         std::map< std::string, std::weak_ptr<texture> > textures ;
         std::map< std::string, std::weak_ptr<model> > models ;
};
Pages: 1234567