• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
robert.leblanc

Decoupling the Graphics System (Render Engine)

6 posts in this topic

I'm fairly new to 3D programming but I've got a grasp on some of the basics. My last attempt at it fizzled out mainly because my ability to manage assets and the code in general was compromised due to poor program architecture. I've been reading about various design patterns and it has spawned a few good ideas (nothing overly concrete though). I'm programming with c++/Direct3D11 API (Win 8 sdk) and I've created a class that I'm calling a RenderEngine (I'm not entirely convinced that I actually know what I render engine is but you'll see soon enough what I'm leaning toward). The idea of having a 'game object' that consists of components like 'graphics', 'physics', 'control', etc seems appealing for its flexibility. In my mind at least part of the idea behind a RenderEngine is that the engine could do things like optimization for one, but also allow you to program objects without worrying about putting a bunch of rendering calls in the game object itself. 

 

So for example, say you have a game object that is a ball (or whatever). I'm interested in what would be considered its graphical part. Also I'm interested in how the RenderEngine would interpret the graphics part. For example, would you have some member variables for say, 

texture, 

verticies, 

color, 

sprite, 

etc (what other useful things would go here ?)

 

Then somewhere in a game loop you'd have some type of GameObjectManager that would give a game object to the renderEngine and ask it to render it. Presumably the GraphicsComponent would be a predefined interface so that when the renderEngine looks at the GraphicsComponent, it "knows" it can call something like getVerticies(), getColor(), .... Some of these things could be null obviously because not every GraphicsComponent would be the same. That's at least part of how I thought a rendering engine would work (probably a very small part--but I'm only going for simple game at this point). 

 

However, I don't see how the object could be truly decoupled from the Direct3D API because there are so many specific things that need to be set up in to bind the buffers, and layouts, to the pipeline. For example, vertex descriptions, layout descriptions, multisampling, etc, etc. So that makes me wonder, does it make sense to include that in the graphicsComponent as well? So along with texturs, verticies, colors, etc, you'd have DESC_ type structures that would get passed along to the RenderEngine? I don't see how the engine could be programmed to figure that stuff out. 

 

Or does it make sense to have a initialize class that would have all these specific needs coded for the gameObject in question. The initializingComponent could pass this data along to the RenderEngine and the Engine could store it in some kind of STL container. Perhaps an objectID would also be passed along so that the next time the game asks the render engine to render that gameObject, it would use the gameID to locate the stored data in the container and fire that into the pipeline. 

 

The question for me is this: in a initializing class as described above, am I maintaining a decoupled state even though I'd have a ton of highly specific commands to create buffer descriptions and the like? You certainly couldn't write the initializeComponent and GraphicsComponent separately. Well you could write the GraphicsComponent probably but someone (me) would have to make sure the stuff in the initializeComponent made sense for what was in the graphicsComponent (which seems to me is not decoupled)

 

Also, suppose I went the initialization route, when things like vertex buffer is created, would it make more sense for the render engine to store that? Or should that be fired back into the GraphicsComponent? If in the renderEngine, you'd need to tell the renderEngine if an object get's destroyed so you could release that memory? If in the GraphicsComponent you'd need to add many place holder variables in the interface for things that could possibly get initialized. I have no idea if either of those options are good, or if they even make much sense. 

 

So what parts of anything I just wrote made sense? What am I out to lunch on? And where might I find examples of this type of architecture that I could learn from?

 

 

 

0

Share this post


Link to post
Share on other sites

In my code when a level loads or the game starts I load all models in a list into graphics memory. Each level might have different models so to save VRAM I only load used models for that level and unload the rest. My models are stored in a list in a data file. Each model has a reference (file name, ID, etc.) to a material data file, vertex data file, all the stuff you want exported from your modeling software could go here. I have a map that maps the filenames to an ID for easy reference. This creates a library where you could ask for the correct model at render time and draw it using only an integer ID. The ID is a pointer of sorts to the graphics engine. I use an entity component system so one of my components is GraphicsComponent and simply stores an integer ID to the graphics engine. At draw time, the physics code is asked for the model position and orientation and then the graphics engine transforms it and renders the ID stored in the GraphicsComponent. I send a message to the graphics engine containing the ID, and the graphics engine looks up the ID in the model library and draws the assosiated data in the proper way. You will need to make something to load the different types of models and store the data properly, but that's easy enough. The way you render the model doesn't matter to the rest of the engine, as the rest of the engine sees it only as an integer ID. You could store each model as a vertex buffer internally, or you could store the model as a vertex array, the engine will never know and will never need to know.

 

Valve does something similar in the Source Engine. When you create a prop which is basically a model attached to a bounding box, you assign it a model by file name which I assume internally is linked to an integer ID. The model is a special type of file which has the vertex data and material data encoded either by reference or by raw data, I'm not sure but I'm assuming the latter. When you go to assign the model itself to the prop, you get to scroll through a list of available models which is their model library. You could even store some physics data with the material for things like bullet penetration, just as long as you delegate the proper data to the proper subsystem at initialization.

Edited by KeyGames
0

Share this post


Link to post
Share on other sites

However, I don't see how the object could be truly decoupled from the Direct3D API because there are so many specific things that need to be set up in to bind the buffers, and layouts, to the pipeline. For example, vertex descriptions, layout descriptions, multisampling, etc, etc.

The graphics module and model module are separate things.
The graphics module contains CVertexBuffer, CIndexBuffer, CShader, CTexture2D, etc., and a bunch of wrappers around Direct3D/OpenGL/Maple calls.

A model never touches Direct3D/Maple/OpenGL/Glide/whatever directly.

A CVertexBuffer handles layouts etc.
A CTexture2D, CSampler, CShader, CRenderTarget, etc., all know how to bind themselves to the device. A model never needs to worry about this.
Multi-sampling has nothing to do with models at all. This is done at a higher level when the user selects the properties of the back buffer. Also done through wrappers provided by the engine.



A model is a collection of meshes.
A mesh is a collection of mesh subsets.
A mesh subset holds its own CVertexBuffer/CIndexBuffer pair and some kind of structure containing render states that need to be set to render the mesh subset. Materials, texture pointers, culling modes, etc. There is one of each of these structures for each pass of rendering needed to render the mesh subset.
None of the values in this structure are actual Direct3D/OpenGL/OpenGL ES 2.0 enumerated values; they are engine constants that are translated (at compile-time or sometimes run-time) into values understood by Direct3D/OpenGL/etc.


And now the model has no idea what Direct3D or OpenGL or Glide or Maple or GR is. It has been completely separated from the underlying graphics API.


Or does it make sense to have a initialize class that would have all these specific needs coded for the gameObject in question.

Objects need to initialize all the structures they own that hold all the state settings, textures, shaders, vertex buffers, and index buffers, but why would that be a class other than the model itself?
These settings all come from a model data file. Why would any other class be messing with it?


The initializingComponent could pass this data along to the RenderEngine and the Engine could store it in some kind of STL container. Perhaps an objectID would also be passed along so that the next time the game asks the render engine to render that gameObject, it would use the gameID to locate the stored data in the container and fire that into the pipeline.

Extremely inefficient. Models can do just fine by holding their own structures with all their render settings.


The question for me is this: in a initializing class as described above, am I maintaining a decoupled state even though I'd have a ton of highly specific commands to create buffer descriptions and the like?

No, you are making a headache for yourself while not having decoupled anything from anything else.


Also, suppose I went the initialization route, when things like vertex buffer is created, would it make more sense for the render engine to store that?

Why?
The vertex buffers and index buffers used by models are part of their model file formats. They belong to the models no matter how you look at it.
You keep them separate from Direct3D by using a CVertexBuffer and CIndexBuffer class provided by the graphics module.


What am I out to lunch on?

A pepperoni pizza with extra cheese.




You have misunderstood decoupling graphics.
In a normal hierarchy a graphics module is not “coupled” to anything but a math module, thread module, memory module, template module, and a foundation module (provides basic types such as u32 as well as system macros such as E_WIN32 or E_IOS, plus generic functions such as ByteSwap()).
That’s what a graphics module needs to survive and that is all to which it links.

Anything that needs graphics (the building module, model module, terrain module, etc.) link to the graphics module and use the features it exposes.
This doesn’t mean they necessarily do their own drawing.
The engine module sits on top of everything. It provides a scene manager in which models, buildings, terrain, cameras, lights, etc. can live.
As the highest-level module it is responsible for high-level rendering.
Gathering objects that are in view, preparing render targets, creating shadow maps, picking LOD levels, deciding if and when to billboard an object, etc.
Part of rendering means the scene manager, after culling non-visible objects, gathers those structures that each model has (one for each pass that tells what states, textures, shaders, vertex buffers, etc., to use), passes them off to a render queue for sorting, and at some point at its own convenience renders them in sorted order.

And thus the graphics module is not coupled to models, the scene, terrain, vegetation, water, particles, etc.


L. Spiro
2

Share this post


Link to post
Share on other sites
 

None of the values in this structure are actual Direct3D/OpenGL/OpenGL ES 2.0 enumerated values; they are engine constants that are translated (at compile-time or sometimes run-time) into values understood by Direct3D/OpenGL/etc.

 

 

Can we just dissect a little bit pseudocode of extremely simple code to see if I can get this a little more.. it will likely help others who are new like me...

 

MyGameObject( .. ) : GameObject{

...
init( .. )
inputModule
physicsModule
modelModule
graphicsModule
otherModules
...
}

I don't think this was exactly what you were describing so you can clear up my misconceptions hopefully. It seems there is some degree of coupling between the graphics and model modules as you'd have to get the vertices or the mesh or whatever from the model and pass it to the graphics to be turned into buffers of various sorts. I guess this makes sense. Currently I'm working with the simplest thing possible a few vertices in the shape of a cube so I guess that would be the model. In the future I imagine I'll have create models using software like Blender (I'm too poor for the like of 3D Studio Max). So then you'd be parsing a file (I used Assimp or whatever it's called in the past... but back then I wasn't any decoupling whatsoever so it was messy just like this run on sentence with poor use of commas ;-) The model I suppose would also contain references to materials, texture files, and other stuff that would be the starting point of what would eventually get put on the screen. Most of the periods used in this paragraphs could be replaced with questions marks, except for this one.

modelModule{

init( .. path-to-model-file/ID-of-Model-to-look-up-/Something-that-lets-this-class-load-model) {

do the loading stuff by parsing... or whatever this implementation decided on
}

verticies
Mesh
textures
materials
general-model-data-variables
etc
}

How the graphicsModule gets its hands on this I'm not quite sure.. Maybe this is a implementation of an interface and the graphics module gets a handle to this during the MyGameObjects initialization? Then the graphics can call things like getMesh(), getWhateverItNeeds() knowing those functions will be there because they were virtually defined in the modelModule interface... In any case... some black magic happens and the graphicsModule maybe could look like....

graphicsModule : someInterfaceThatMakesSenseForTheRenderEngine{
init( handle-to-modelModule, whatever-else-it-needs) {
//This is what I'm wondering... does the following sort of stuff go in here ...
//for example

//Build some shaders based on the data from the model (presumably models define their own shaders )-- The engine can deal
//with the result
D3DCompileFromFile( szFileName, nullptr, nullptr, szEntryPoint, szShaderModel, dwShaderFlags, 0, ppBlobOut, &pErrorBlob );



// Define the input layout
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
UINT numElements = ARRAYSIZE( layout );

//Vertext Buffer Description
D3D11_BUFFER_DESC bd;
ZeroMemory( &bd, sizeof(bd) );
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth = sizeof( SimpleVertex ) * 3;
    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
    D3D11_SUBRESOURCE_DATA InitData;
ZeroMemory( &InitData, sizeof(InitData) );
    InitData.pSysMem = vertices;
   
//description ready to be passed along to the engine
 
...
...
}
}
 
To make an actual vertex buffer requires a handle to the D3DDevice. I guess creating a VB maybe isn't considered rendering?? So does it make sense to just get a handle to the engine and give it the buffer desc and get back a VB? Then just hang on to it in this class?
You had several classes mentioned CVertexBuffer, CInputLayout, .... Are these classes within the graphicsModule? Are the initialized during the parsing of the model data?
 
If any of what I have written is even close to on the right track (and I doubt it is) I picture the Engine using the interface when a GameObjectManager calls RenderEngine.render(MyGameObject) to access all the predefined methods like, getVB, getInputLayouts, getShaders, getWhateverStates, getWhateverElseIsDefinedByTheInterface. BTW is there a difference in the use of the word component vs. module? Also I'm mostly interested in simple ideas here that allow me to decouple for organization. I'm currently (although I recognize its importance) not terribly interested in optimizing speed or fancy dancy scene management. I will do that when I get a bunch of simpler objects interacting and doing the basic things I want them to do. I figure even get there I need at least a semblance of structure to my code. That's why I'm going this route. Ex: I'm not building a commercial rendering engine here
0

Share this post


Link to post
Share on other sites


… It seems there is some degree of coupling between the graphics and model modules as you'd have to get the vertices or the mesh or whatever from the model and pass it to the graphics to be turned into buffers of various sorts. ...

I rephrase the second last paragraph of L. Spiro's above answer a bit. As ever, details and naming things may differ a bit from case to case...

 

The usual way (as long as we speak of modern engine architecture) is to have 2 levels of rendering, neglecting here that a mesh generator level may be placed on top of it. Let us name the upper level "graphics rendering" and the lower level "graphics device".

 

The graphics rendering iterates through the scene and uses some mechanism on each of the found renderable objects to determine whether it could be visible (e.g. it does frustum culling or the like). For each object passing this, the graphics rendering creates a graphics rendering job. This job is an instance of a structure that becomes filled with the draw call (what kind of drawing routine is to be used, but in an engine manner, not using graphics API specific enums or codes) and perhaps which ranges are to be used, which index buffer and vertex buffers and constant buffers and so on, and which drawing state (an abbreviation for blending and other modes, as well as textures and so on). For each visible sub-mesh an own job is created (perhaps more than one; think of L. Spiro's "one for each pass"), and put into a queue.

 

The graphics device then works on the queue, sorts the jobs as necessary, and invokes the underlaying D3D/OpenGL/... library first to set the modes as prescribed by the current job's drawing state, and then the API's routine matching the current job's draw call.

 

Notice that the graphics device gets told what to do by the graphics rendering. Neither does the graphics rendering need to know anything about D3D/OpenGL/..., nor does the graphics device know anything about models. Of course, both levels must agree how index buffer, vertex buffer, and so on are built up, and for sure will their structure be borrowed from what D3D/OpenGL/... use. But that is just a convention due to performance reasons ;)

0

Share this post


Link to post
Share on other sites

A CVertexBuffer handles layouts etc.
A CTexture2D, CSampler, CShader, CRenderTarget, etc., all know how to bind themselves to the device. A model never needs to worry about this.

 

 

So do they take a device type parameter or a render type class? Here's an example I found of a CVertextBuffer class. Is this similar to what you meant? It's D3D9 but I'm assuming the ideas are the same. I'm less interested in having the flexibility to change 3D API's although I know that is a big advantage of this approach. I'm more interested in how it helps structure programming itself. Maybe that's the wrong reason to go this route. 

 

Also this VertexBuffer Class

Edited by R. LeBlanc
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0