Monday, June 20, 2011

Program Design, Managing Resources

So I started describing how I wanted to hold my information. My first idea was vectors of each type:

Vectors to hold shaders, entities and models

A vector, for those not familiar with the C++ standard library, is a form of storage consisting of as many adjacent 'boxes' as you need. Each of the boxes in a given vector can contain one type of data. 

You can access any of the boxes directly by their index. In the example above, asking for shader[0] gives you one value of type GLuint, while asking for entity[0] gives you an Entity object and asking for model[0] gives you a GLTriangleBatch object.

This presents a few problems. For example, I have to keep one kind of calling code for each, and ready a different kind of object to hold the information. This kind of duplication will make things difficult in the long run. As I was saying last time, a solution for this was to create a Resource class, from which every other kind of resource would inherit.
While I can make Entity inherit from Resource easily, GLuint is a primitive type defined in the OpenGL standard, and GLTriangleBatch belongs to the GLTools library in the OpenGL SuperBible. They can't inherit from Resource. Instead, I can create wrapper classes, Shader and Model, which simply contain elements of their types, and inherit from Resource.

 Resource inheritance graph

Then, I can create vectors of pointers to Resource types. Each caller would then be responsible for asking the right manager for the information, and handling it correctly, but the 'pipes' moving the information across can all be the same.

Resource pointer vectors for shaders, entities and models

Giving the engine the resource it needs, then, would mean giving it a pointer to where in memory the resource is stored. This way the engine can know everything it needs to know about the resource, without worrying about its storage or lifetime. Looking back at the interface graph, however, we notice that our interface needs to know about every manager. Managers belong in the Resource layer, and the interface belongs in the Engine layer. Giving one an insight into the inner workings of the other is troublesome.

We can add an additional layer of insulation, and create an Engine interface in the resource layer. The Resource Interface would just need to know how to call the Engine interface, then, and it would be this component that knows about all the managers, and who holds what bit of information. The Resource interface would then just ask for 'model #0', and the engine interface would provide the Resource pointer to 'model #0', without the engine interface needing to know anything about managers or the resource managers knowing what engine asked for it.

Interfaces insulating the Resource and Engine Layers

So we have now fleshed out a bit the design for the Engine and Interface layers, and worked out how to keep the details of each local to themselves. We could create more engines and resource types, and would not need to change much to accommodate them.

No comments:

Post a Comment