Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I often tend to do a lot of premature optimazation when dealing with graphics. There are a few principles I always try to follow:

  • Keep the number of D3D components to a minimum. (Render states, buffers, shaders, etc.)
  • Only bind components if absolutely necessary. (Not bound already, etc.)
  • Specialize the components as much as possible. (Only set necessary BindFlags, etc.)

This lead me to building very elaborate wrappers for managing the created components and the current pipeling state. Not only does this consume a lot of my valuable development time, it also adds another big layer of complexity.

And worst of all: I don't even know if it is all worth the trouble.

Some of my optimization considerations may already be implemented on a lower level and I am just replicating them, additionally wasting time on the CPU. Other considerations may be completely unnecessary, since the effect on performance is negligible.

So my questions are:

  1. Which of the above guidelines are valid and to which extent should I follow them?
  2. How does the GPU handle state changes?
  3. What happens if I change a state that is never used? (No draw call being made while it is active.)
  4. What are the actual performance penalties for binding the various different components?
  5. What other performance considerations should be made?

Please don't just tell me, that I just should not care about performance until I hit actual limits. While that is obviously true from a practical point of view, I am mainly interested in the theory. I somehow need to combat the urge of building the optimal graphics framework and I don't think I can do that with the usualy "premature optimization lecture".

Managing components

I am currently writing DirectX 11 applications in C# using SlimDX as a managed wrapper. It is a very low level wrapper and my current abstraction is built on top of it.

There are some obvious advantages when using a Direct3D abstraction. Setting up the environment, loading shaders, setting constants and drawing a mesh is much simpler and uses a lot less code. Also, since it manages creation and disposing of most components they can be automatically reused everywhere and I almost completely avoid memory leaks.

  1. How do you usually manage all the graphics components and resources?
  2. Can you recommend any managed wrappers doing something similar to my example below?

Here is an example of my current implementation. I am quite happy with the interface. It has enough flexibility for my needs and is very simple to use and understand:

// Init D3D environment
var window = new RenderForm();
var d3d = new Direct3D(window, GraphicsSettings.Default);
var graphics = new GraphicsManager(d3d.Device);

// Load assets
var mesh = GeometryPackage.FromFile(d3d, "teapot.gp");
var texture = Texture.FromFile(d3d, "bricks.dds");

// Render states
graphics.SetViewports(new Viewport(0, 0, 800, 600);
graphics.SetRasterizer(wireFrame: false, culling: CullMode.Back);
graphics.SetDepthState(depthEnabled: true, depthWriteEnabled: true);
graphics.SetBlendState(BlendMethod.Transparency);

// Input layout
graphics.SetLayout("effect.fx", "VS", "vs_4_0",
    new InputElement("POSITION", 0, Format.R32G32B32_Float, 0),
    new InputElement("TEXCOORD", 0, Format.R32G32_Float, 0)
);

// Vertex shader
graphics.SetShader(Shader.Vertex, "effect.fx", "VS", "vs_4_0");
graphics.SetConstants(Shader.Vertex, 0, 4, stream => stream.Write(wvpMatrix));

// Pixel shader
graphics.SetShader(Shader.Pixel, "effect.fx", "PS", "ps_4_0");
graphics.SetTexture(Shader.Pixel, 0, texture);
graphics.SetSampler(Shader.Pixel, 0, Sampler.AnisotropicWrap);
graphics.SetConstants(Shader.Pixel, 0, 1, stream => stream.Write(new Color4(1, 0, 1, 0);

d3d.Run(() =>
{
    // Draw and present
    d3d.BackBuffer.Clear(new Color4(1, 0, 0.5f, 1));
    graphics.SetOutput(d3d.BackBuffer);
    graphics.Draw(mesh);
    d3d.Present();
}
share|improve this question
8  
For this kind of question I wouldn't give the "premature optimization" lecture, I'd give you the "profile changes so you can see for yourself" lecture. –  Tetrad Mar 1 '13 at 12:00
    
@Tetrad I am almost ashamed to admit that this is some pretty decent advice. I should definitely do more profiling. –  Lucius Mar 1 '13 at 12:05
1  
Profiling numbers are the gamedev version of "pics or it didn't happen" for sure =) –  Patrick Hughes Mar 1 '13 at 15:45
add comment

2 Answers

I like the abstraction approach outlined by Hodgman in these threads on gamedev.net:

He describes a three-tier rendering system:

  1. Low-level rendering API that accepts "commands", abstracting no more than the differences across different graphics API's, such as Direct3D 9, Direct3D 11, and OpenGL. Each "command" maps to a different state or draw-call, such as binding a vertex stream or texture, or drawing the primitives.
  2. API that accepts "render items", which group together all the states and a single draw-call needed to render a certain object, and sorts and translates them into commands which are sent to the first level. A render state contains a draw-call and a stack of "state groups", which logically group state-changes. For example, you'd have a state group for the render pass, a state group for the material, a state group for the geometry, a state group for the instance, and so on. This level is responsible for sorting these render items to reduce redundant state changes and culling any state changes which are, in fact, redundant.
  3. High-level systems like a scene graph or GUI renderer which send off render items to the second level. The important thing to notice is that these systems don't know about either the state sorting algorithms or the specific rendering API, making them completely platform-agnostic. They're also easy to use once the lower-level API's have been implemented.

In conclusion, this model solves both of your problems at once.

share|improve this answer
add comment

Since I was looking for an answer at a lower level than the solution proposed by Boreal Games (no graphics API abstraction needed yet), here is the architecture I finally went with.


Profiling showed that the creation and repeated binding of components negatively affects performance. Some components (e.g. BlendStates) had less impact than others (e.g. shaders). But luckily the system can be modified to consider this. I chose to split my original system into two parts. I pass both of them to the effect constructor:

GraphicsPipeline

This class represents a copy of the graphics pipeline state. When a component is bound to the pipeline object it is first compared with the current state. Only if it is not already bound it is actually sent to the graphics device.

PixelShader ps1;

pipeline.Reset();
pipeline.Set(ps1); // Shader is bound to the device
pipeline.Set(ps1); // The pipeline remembers that the shader is still bound and it is not passed to D3D at all

The only cost from repeatedly binding the same effect would be the (per reference) comparison for each component, and that should be negligible.

Only bind components if absolutely necessary.

GraphicsPool

To further increase the usefulness of the GraphicsPipeline class I now use a pool for all graphics components. It can create components from a description and stores description and resource in a dictionary. Next time the resource is requested it is fetched from the dictionary and not recreated.

var ps1 = pool.GetPixelShader("effect.fx", "PS", "ps_4_0");
var ps2 = pool.GetPixelShader("effect.fx", "PS", "ps_4_0");

ps1 == ps2; // TRUE

As you can see, this works great for shaders. Instead of compiling the same code twice for two different effects it is simply refetched from the pool. The overhead for the string comparison should not be too bad.

Though, the overhead might be bigger than the actual creation cost for some components. For example, accurately comparing BlendStateDescriptions is quite a hassle and creating a new BlendState is rather cheap. But since the creation and fetching is hidden in the pool class I can decide on a per component type what I actually pool.

It is again negligible on a per frame basis, since the components only need to be fetched at effect creation. After that they are referenced in the individual effect objects. So there is no performance cost per frame. But still:

Keep the number of D3D components to a minimum.

Since resources are created using their original Direct3D descriptions the system is quite flexible:

Specialize the components as much as possible.

Through this setup the risk of memory leaks (caused by shaders or render states) is also reduced.

Conclusion

I think this architecture works really nicely, especially since both components can at this point just be a shell. All the performance optimization is hidden and can be added as required (what I will determine through further profiling). Textual feedback is appreciated (as far as the QA format allows it).

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.