Skip to main content
GameDev.net gamedev.net
🔒 Locked

Rendering pipeline architectures and design

Started by Althar Aug 30, 2011 at 5:17 PM 2 replies 5.1k views
Original Post
Althar
Althar
Hey everyone,

I've been working on a personal 3D "game engine" for several months now, for learning purposes mainly.
My engine is split into two separate libraries : core and game.

Core is the low level wrappers and classes which abstract specifics APIs (such as directX for graphics, FMod for sound, etc.) into a unified system to use for high level components. This part is pretty much complete, and I have something I am happy to work with, and which offers all the components required for rendering ( primitive drawing, texturing, shader handling, etc.), gathering input, as well as managing memory and resources.

Now lately, I have been focusing on the game part, which is supposed to feature high level components, such as scene management components (octrees, scene graphs, whatever you like), state management (game states), and the rendering pipeline.

I have made several attempts at designing my rendering pipeline, from the dirty/naive rendering system within scene nodes (with simple "render()" calls), to attempting to have a unified system ( specific render managers for meshes, particles, etc, which receive basic data prior to rendering, and treat it later on) which I then turned into a deferred rendering pipeline. Unfortunately, I haven't been able to find a way which I found elegant and modular/flexible enough.

I have been reading through the forums, and gathered a few techniques and designs on providing the data, such as cbuffers, or render managers which expect pre-defined structures (similar to what I attempted with my deferred renderer), but I have found very little information on what the code looks like and how it links up with the pipeline (with shading, shadowing, post-processing effects, etc.).

---------------

Would you happen to know where I could find resources on an actual pipeline? I understand the principle of the techniques mentioned above, however, I do not understand how they actually relate to the complete process.

In my deferred rendering design, I essentially had several render managers which would take a list of pre-defined structures, and were able to output a render something. Basically my main RenderManager would allocate RenderInstances on demand (which could be of type RenderMeshInstances for meshes, expecting a mesh, matrices, and materials), and register it, so that during the actual rendering of the scene, the instance can be dispatched to its corresponding manager (in that case RenderMeshManager), which actually performs the job.

Now this method worked pretty well for a bit, since I could centralize rendering, however, as soon as I attempt to add an extra stage (say shadowing), I am lost. I thought I could potentially add a new RenderShadowManager, and its corresponding RenderShadowInstance which would require the mesh to be registered twice (once for default rendering, and once for shadowing). In effect that could work, but I hardly imagine it being viable once I start to add more effects and having to explicitely re-register instances for particular effects.

How did you guys design your rendering pipeline ? Is it monolithic ? Has someone of you managed to design it in a somewhat modular and dynamic fashion (e.g. allowing post-effects to be stacked on the fly at runtime, or new stages to be injected, without further changes, based on existing info : for instance a mesh might still register for an AO pass)?

Does it sound wrong to have to hardcode parts of the pipeline like I did? I have had a look at several engines/frameworks, such as Torque3D, Irrlicht, Ogre (which I know the less about), and they all seem to have to do it to some level. Am I delusionial to think a pipeline can be modular? Should I just drop it and design a fixed pipeline revolving around my game's needs?

Thanks in advance for your input and advice !!
smasherprog
smasherprog
IMO it is best to leave as much flexibility as possible. The more you have managers taking too much responsibility, the less control you have overall. It is really what you want your target audience to be. If your target audience are people new to programming, the more structured you will have to be in creating a specific rendering pipeline. Personally, I like the way Humus has his project setup. I based my project largely off the way he has his framework. Basically, it is a graphics API, with all redundant code in a a central file. There are no complex managers, and IMO, it makes for a clean, easy to code project.

This is nothing compared to what you wrote, but if you create a rendering pipline, you are creating a method of rendering. So, adding features later can be incredibly difficult. But, if you leave the actual pipline up to the programmer, adding later is a breeze.
Wisdom is knowing when to shut up, so try it.
--Game Development http://nolimitsdesigns.com: Reliable UDP library, Threading library, Math Library, UI Library. Take a look, its all free.
dblack
dblack

Hey everyone,

I've been working on a personal 3D "game engine" for several months now, for learning purposes mainly.
My engine is split into two separate libraries : core and game.

Core is the low level wrappers and classes which abstract specifics APIs (such as directX for graphics, FMod for sound, etc.) into a unified system to use for high level components. This part is pretty much complete, and I have something I am happy to work with, and which offers all the components required for rendering ( primitive drawing, texturing, shader handling, etc.), gathering input, as well as managing memory and resources.

Now lately, I have been focusing on the game part, which is supposed to feature high level components, such as scene management components (octrees, scene graphs, whatever you like), state management (game states), and the rendering pipeline.

I have made several attempts at designing my rendering pipeline, from the dirty/naive rendering system within scene nodes (with simple "render()" calls), to attempting to have a unified system ( specific render managers for meshes, particles, etc, which receive basic data prior to rendering, and treat it later on) which I then turned into a deferred rendering pipeline. Unfortunately, I haven't been able to find a way which I found elegant and modular/flexible enough.

I have been reading through the forums, and gathered a few techniques and designs on providing the data, such as cbuffers, or render managers which expect pre-defined structures (similar to what I attempted with my deferred renderer), but I have found very little information on what the code looks like and how it links up with the pipeline (with shading, shadowing, post-processing effects, etc.).

---------------

Would you happen to know where I could find resources on an actual pipeline? I understand the principle of the techniques mentioned above, however, I do not understand how they actually relate to the complete process.

In my deferred rendering design, I essentially had several render managers which would take a list of pre-defined structures, and were able to output a render something. Basically my main RenderManager would allocate RenderInstances on demand (which could be of type RenderMeshInstances for meshes, expecting a mesh, matrices, and materials), and register it, so that during the actual rendering of the scene, the instance can be dispatched to its corresponding manager (in that case RenderMeshManager), which actually performs the job.

Now this method worked pretty well for a bit, since I could centralize rendering, however, as soon as I attempt to add an extra stage (say shadowing), I am lost. I thought I could potentially add a new RenderShadowManager, and its corresponding RenderShadowInstance which would require the mesh to be registered twice (once for default rendering, and once for shadowing). In effect that could work, but I hardly imagine it being viable once I start to add more effects and having to explicitely re-register instances for particular effects.

How did you guys design your rendering pipeline ? Is it monolithic ? Has someone of you managed to design it in a somewhat modular and dynamic fashion (e.g. allowing post-effects to be stacked on the fly at runtime, or new stages to be injected, without further changes, based on existing info : for instance a mesh might still register for an AO pass)?

Does it sound wrong to have to hardcode parts of the pipeline like I did? I have had a look at several engines/frameworks, such as Torque3D, Irrlicht, Ogre (which I know the less about), and they all seem to have to do it to some level. Am I delusionial to think a pipeline can be modular? Should I just drop it and design a fixed pipeline revolving around my game's needs?

Thanks in advance for your input and advice !!



You should keep things as simple as possibe,but no simpler.

For the example you give, I think the object(mesh etc) knows best how it should be rendered, so it should have a draw call(or multiple draw calls), from which it draws itself using a lower level API. You can use inheritance and contained classes to factor out common code and higher or lower level code to handle communication(eg state sorting, light binding, occluder fusion etc).


Just start with something simple and add features, spliting things out when they get messy or duplicated.


David

PS I am not a fan of thin API abstraction layers(anymore), I would leave such things until you actually have to port to another API or platform. Otherwise you end up wasting a lot of time on them and they continually grow in scope. (The exception to this is if you have no other option, eg a wrapper from C++ to .NET, in this case you should keep the wrapper as small as possible and mold the API to suit your specific needs).
Althar
Althar
Thanks for your replies!!

If I understand you guys, I should keep it as simple as possible and as relevant to what I need smile.gif.

Now, I have attempted what dblack described in the past, and I must say, I do have issues with renderables rendering themselves :

  • How can I have global effects if the entities are rendered independantly?
    Obviously, the fact that the entities are rendered independantly means I can render them the way I want, but it also means I lose rendering coherency : I can imagine it (actually I have already experienced this issue) being a nightmare to try and get everything to render the "same" if I decide I want to change the game's graphical style halfway through. Additionally (you may have arguments against that), I believe a mesh, or whatever renderable entity you are talking about, is just data, it needn't know what it should look like (I would rather have a dispatch that task to another component, and allow it to be substituted depending on the desired style)
    • How does optimizing draw calls (reducing material and batching) come into this?
      I know early optimization is the root of evil, however, going this route would mean an awful lot of maintenance if I do decide to change my design/pipeline. I could probably have the lower level layers batch things up, but I don't believe it is the render device's role to do so : the low level classes only provide basic operations such as setting textures, render states and rendering primitives.

      ---------------

      I've been brainstorming and expanding on what I have now, and I have come up with a solution I'm quite comfortable with (again, I'm open to suggestions):

      The application provides the user with a RenderPipeline, an abstract class/interface which provides the "method of rendering" as smasherprog described. This particular class is modular and meant to be substituted depending on the desired processing (e.g. a forward pipeline, a deferred pipeline, etc.).

      This class instantiates render entries (which can be of type mesh, billboard, or any custom type you decide to add) on demand (during traversal of the scene, or via a query system). Those entries are fed to registered managers (RenderMeshManager, RenderBillBoardManager, etc.) during rendering according to the pipeline's method of rendering.

      Again, managers can be substituted, and are invoked by the pipeline, when it needs to : the render pipeline might decide that meshes should be both fed to the RenderMeshManager and to the RenderShadowManager if shadows should be featured. The specific managers are never seen beyond the render pipeline, meaning, meshes can be rendered as a whole, but could also be split and dispatched to MaterialRenderers instead. Since the pipeline knows the specifics of rendering passes and techniques, it should be able to find fallbacks and select sets of geometry for each pass (a deferred renderer might exclude translucent objects from the main set while rendering the GBuffer).

      Batching and optimizing draw calls would be handled by the pipeline and its managers, to suit the pipeline's needs ( some techniques might require drawing from front to back, while others might require drawing larger meshes first).

      I am aware of the degree of complexity of this setup (many classes and subsystems), but I believe it will be far easier to maintain, as well as being modular enough (I am not planning on a scriptable pipeline, but this is as close as I can get) if I decide to add more renderables, or decide I want to change my rendering pipeline (data will already be provided and I will just have to process it in a different way).

      Renderable entities should be provided without any regard to the pipeline : a mesh will provide its geometry, materials/shaders and transforms ; the pipeline will decide what to dispatch, and when.

      ----------------

      PS : I have just ordered "3D Game engine architecture" from Dave Eberly (the 2006 version), and "Game engine architecture" from Jason Gregory from amazon, I'll see if I can find anything resourceful in there.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.