Resource Management

Started by
21 comments, last by Satharis 9 years, 1 month ago


So do you claim that singleton patterns are bad habit?

For the most part, yes. Though as with any rule, there may be specific exceptions.


I've seen them used many times while viewing engines' source codes.

Open-source engines are not necessarily a good place to look for examples of correct software design.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Advertisement


So do you claim that singleton patterns are bad habit? I've seen them used many times while viewing engines' source codes.
That's bad, and they should feel bad.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

In an effort to steer sharply away from a Singleton flame war...

I also find "manager" too generic.

I prefer a combination of a loader, a cache, a proxy that implements the full resource interface, and a store. Each has a clear responsibility. You request an object from the store and immediately receive a proxy. If the object is already in the cache the proxy is filled in with the live value, otherwise the proxy gets a well-established placeholder. In the latter case, the loader pulls the object from the disk/network/other and loads it into the cache, then notifies the user of the proxy. When the engine decides the resource is no longer needed it can remove the object from the cache and update the proxy back to the placeholder data.

Edit: There are different classes for each class of resource because they really are different. A story script or game object script can coordinate with the scripting engine. A graphics texture store needs to coordinate with images kept on a graphics card, an audio clip store needs to work with items kept in wwise or another audio system. An animation store needs to interface with the animation system. And so on. While there are many things in common, the details are unique to each.

Edit again: Also this system works well with another frequently mentioned aspect of game development; it easily supports hot reloading of resources. The engine can use file system monitoring to detect that a model or texture or audio clip or animation or whatever else has changed in the file tree, then load and swap the modified resource.

Also, many people reach to singleton pattern for such managers, but I think this is wrong-headed. Do not allow your design to assume that only one manager of a single resource type (abstract or concrete) exists -- If you design such that many can co-exist, you can choose to use only one and get all the same traits of a system which only allows for one, but you cannot choose to create many if your design enforces only one.


So do you claim that singleton patterns are bad habit? I've seen them used many times while viewing engines' source codes.
It doesn't matter how you call it; as long its functionality it only one or related to the class itself...

A singleton can be replaced most of the time with a instance based class; I would like to re-iterate: every singleton architecture can be replaced with a instance based one, most of the programmers don't change from a global class architecture to a instance one because sometimes it adds some complexity to your current scheme; the number of classes to wrap the existent architecture can increase if not designed very well.

Calling a manager can be a bad thing, because leads the programmer to think abstractly too much instead of first re-thinking in what needs to be implemented in a specialized class.

A lot of engines uses the manager name and the singleton pattern, but today what matters is do the right thing with code as well many other fields in game development.

I don't have experience in a game company yet, but I believe that with the number of employers in a company today, things should be organized in the best way possible (mainetenable; actually maybe doesn't need to be experienced to note that).

Also, remember that you should organize your engine using modules; thats the only way to NOT do it wrong (at least 60%). If you want an simple example using a intermediary code base you can go into my blog above this quote and search there. Using Visual Studio that can be done using static libraries and solutions to separate the modules responsabilites and it is for sure the best thing to do for a begginner (I'm not saying you're a beginner, just commenting).

Once you have a module make sure to work inside its black box, calling X manager whenever you want without having to make libraries that doesn't depend of another library, also it helps to get most of the things instance based.


I've seen them used many times while viewing engines' source codes.

Open-source engines are not necessarily a good place to look for examples of correct software design.

Then can you link a good article or book about this subject? (besides Game Engine Architecture, which I have already read)

Thanks for the comments but that was too much theory lol. I don't know what to begin with...


I have many different kind of resources: images, opengl textures, vbo's, ebo's, vao's, rbo's, shaders, shader programs. (did I miss something?)

Please notice that different kinds of resources requires different handling as well. The usually meant kind is that of assets like meshes, animation data, sound clips, texture images, scripts, and so on. This stuff is designed, resides on mass storage and need to be loaded into memory. On the other hand, texture objects, VBOs, IBOs, RBOs, and such are resources in the sense of production means. They are not loaded from mass storage but requested from the graphics API (OpenGL in your case). Furthermore, when thinking of VBOs and IBOs and similar buffer objects, the memory behind it may also need to be seen as a resource to be managed, for example if you want to batch meshes. Similarly there may be a management of texture areas in the sense of atlases or levels in a texture array object. And at the end of the spectrum there are resources like texture units that are available in limited amount, depending on the running platform, and hence may need to be managed, too.

Where to start ... well, that is a good question. In fact you cannot render anything if not both assets and production resources are available. What do you have already? Is loading of assets already implemented? Is a basic renderer already implemented?

What do you have already? Is loading of assets already implemented?

Yes. I encapsulated Assimp in classes. However, the Mesh class I wrote, for example, uses opengl commands, which I think isn't correct design. (this doesn't obey the Single responsibility Principle)

class Mesh
{
public:
	Mesh(aiMesh *mesh) : _mesh(mesh)
	{
		_hasNormals = _mesh->HasNormals();
		_hasTextureCoords = (_mesh->mTextureCoords[0] != 0);

		for(unsigned int i = 0; i < _mesh->mNumVertices; i++)
			_vertices.push_back(glm::vec3(_mesh->mVertices[i].x, _mesh->mVertices[i].y, _mesh->mVertices[i].z));

		if(_hasNormals)
			for(unsigned int i = 0; i < _mesh->mNumVertices; i++)
				_normals.push_back(glm::vec3(_mesh->mNormals[i].x, _mesh->mNormals[i].y, _mesh->mNormals[i].z));

		if(_hasTextureCoords)
			for(unsigned int i = 0; i < _mesh->mNumVertices; i++)
				_textureCoords.push_back(glm::vec2(_mesh->mTextureCoords[0][i].x, _mesh->mTextureCoords[0][i].y));

		for(unsigned int i = 0; i < _mesh->mNumFaces; i++)
		{
			aiFace face = _mesh->mFaces[i];
			for(unsigned int j = 0; j < face.mNumIndices; j++)
				_indices.push_back(face.mIndices[j]);
		}

		glGenVertexArrays(1, &_vao);
		glGenBuffers(1, &_vbo);
		glGenBuffers(1, &_ebo);

		glBindVertexArray(_vao);

		glBindBuffer(GL_ARRAY_BUFFER, _vbo);

		//vertices
		glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * (_vertices.size() + _normals.size()) + sizeof(glm::vec2) * _textureCoords.size(), (const GLvoid*) _vertices.data(), GL_STATIC_DRAW);
		glVertexAttribPointer(0, 3, GL_FLOAT, 0, 0, 0);
		glEnableVertexAttribArray(0);
		if(_hasNormals) { //normals
			glBufferSubData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * _vertices.size(), sizeof(glm::vec3) * _normals.size(), (const GLvoid*) _normals.data());
			glVertexAttribPointer(1, 3, GL_FLOAT, 0, 0, (const GLvoid*)(sizeof(glm::vec3) * _vertices.size()));
			glEnableVertexAttribArray(1);
		}
		if(_hasTextureCoords) { //uvs
			glBufferSubData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * (_vertices.size() + _normals.size()), sizeof(glm::vec2) * _textureCoords.size(), (const GLvoid*) _textureCoords.data());
			glVertexAttribPointer(2, 3, GL_FLOAT, 0, 0, (const GLvoid*)(sizeof(glm::vec3) * (_vertices.size() + _normals.size())));
			glEnableVertexAttribArray(2);
		}

		glBindBuffer(GL_ARRAY_BUFFER, 0);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * _indices.size(), (const GLvoid*) _indices.data(), GL_STATIC_DRAW);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

		glBindVertexArray(0);
	}

This is bad. First I think I should encapsulate vertex buffers in a class called VBO or something. Accessing direct opengl commands like that is ugly.

Secondly I think that I should not call opengl commands in this class at all. It's only meaning should be to store mesh data (again, the Single responsibility Principle).

I should access this data in a higher level class and then mix it with opengl functions (can anyone approve this approach?). Anyway, like I said, I don't know where to begin. I don't want to write code that will turn out to be bad. That's why I tried viewing other engines' source codes, but they are very hard to understand (for me at least..).

Is a basic renderer already implemented?

Nope. My intention was to build a resource management system before implementing the renderer, that should rely on it.

I really want to start coding but I'm stuck too much time on the design. This is really depressing =[

Nope. My intention was to build a resource management system before implementing the renderer, that should rely on it.

I really want to start coding but I'm stuck too much time on the design. This is really depressing =[

You can't design systems like these in isolation (at least not until you've built one 2-3x before).

The best case here is that you finish building the resource system, start on the renderer, and then have to rebuild it all. Instead, start building the renderer right away, and you'll find it places explicit demands on the design of your resource system.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Is a basic renderer already implemented?


Nope. My intention was to build a resource management system before implementing the renderer, that should rely on it.


I really want to start coding but I'm stuck too much time on the design. This is really depressing =[


The most important thing you can do right now is to start coding on your current design.

It won't be "perfect", and you'll almost certainly have to rewrite large portions as you find your design needs to change to match "real world" demands, but that's part of the process. But you won't be able to find the holes or flaws in your design until you're actually working with it.

Design paralysis kills a ton of games (and developers) all the time - don't let it happen to you smile.png

As said by previous posters: Start with the mesh class you have, and implement a simple renderer using it. This is the only way to constantly check that things are working. Then begin to refactor your code.

This topic is closed to new replies.

Advertisement