D3D9 vs D3D10: Constant buffers

Started by
5 comments, last by Demirug 15 years ago
Im working on little render engine supporting both D3D9/D3D10 using direved classes. So far the engine is near ready for some first testing but im strugling with 1 part of it, the shader constant buffers. So far i implemented: - IRenderDevice - IVertexLayout - IVertexBuffer - IIndexBuffer - IVertexShader - IPixelShader - ITexture - IFont both render dll's have an implementation for each interface class. Now the main issue is the difference of settings the shader variables in D3D9 and D3D10. As u can see in the 'HLSLWithoutFX10' sample of the DXSDK, in D3D10 u map/unmap the constant buffer and can just copy some structure containing the variable values into the constant buffer. In D3D9 its a total different story as u must set each value separated into the constant table or call ID3DDevice9::SetVertexShaderConstantF and so one ... i can't figure out a good way to make some interface that can be impelented by both render dll's to use D3D9/D3D10 the same way. Once i have this issue solved i have pretty much the low lvl render engine ready and dont have to bother about differences in D3D9/D3D10 for the rest of the project. so anyone have idea or samples on how to implement an interface for settings constants. p.s. Currently im using a small work-around by using effect system instead of separated shaders wich work fine in both D3D9/D3D10, but as i want to keep the render dll's low lvl api i prefer to use separeted shaders instead. This causes less issues regarding compiling the same shader file for both D3D9 & D3D10 (fx seem to require a D3D9 & D3D10 technique, currently solved by passing some constants to the shader when compiling)
Advertisement
You should do it in your interfaces like D3D10 does it. And emulate constant buffer functionality on D3D9.
Personally (but that's D3D11 and OGL, don't remember how it worked in D3D9) I keep the data "locally" in the constant buffer (that is, in a buffer that doesn't have anything to do with the GPU) and flag the buffer as dirty.

Then when I start using a technique, I check which buffers are needed and if they're dirty, "reload" them (that is, rewrite all the data).

Anyway if you target D3D9 and 10 (and nothing else), wouldn't it be simpler to just use the fx files support provided by D3DX9 and D3D10?
You just mimick D3D10 behavior in D3D9, it's trivial.
-* So many things to do, so little time to spend. *-
I think you're approaching the problem in a fundamentally wrong way, actually :)

Rather than write a thin, rather brittle wrapper around a rendering API, go for the proverbial jugular and do things at a *really* high level, i.e. have a RenderScene functionality that takes a custom container with all the objects you want to draw. This solves several problems very elegantly:

-Since you're dynamically loading your renderer, you avoid the overhead associated with calling functions in an external library. (similar to lots of virtual function calls; by itself it isn't too bad, but if you call it thousands of times per frame it has an impact)

-Semantic problems like the one you're describing vanish. If you make a few classes for materials, geometry containers and bone transforms, the renderer itself can decide what do do with them.

-Rendering approaches are no longer a problem. Want to make a deferred renderer? Easy! Just goof with your renderer class to output g-buffers and do the required postprocessing. No app-side changes required!

-Culling is automatically abstracted away. You can just build a visible set in your update function, put that list in the container, ship it off and forget about it. Some more advanced stuff, like occlusion culling, could conceivably be done inside the renderer; that being said a simple boolean value or flag could be set to enable/disable this feature.

-Fixes/debugging are much simpler, too. You don't have to track down every single instance of a renderer call in order to update it. Everything you need is (metaphorically) right there in front of you.

So, granted, resource management can be a bit more involved, (i.e. how do we tell our renderer where to look for geometry/texture info, etc.) but with some creative thinking it's not much more difficult. Again, abstraction is key, and calling a CreateResource()-esque function that returns an index into an internal array is pretty simple to integrate.
clb: At the end of 2012, the positions of jupiter, saturn, mercury, and deimos are aligned so as to cause a denormalized flush-to-zero bug when computing earth's gravitational force, slinging it to the sun.
Rewdew's approach seems quite reasonable to me.

Also, have a look at the FX9/FX10 API's as they don't explicitly mention registers (D3D9) or constand buffers (D3D10) and are quite intuitive from a coding perspective.

It should be fairly trivial to implement the common interface that allows access to individual parameters/types, and to set them on the device should be reasonably straight forward as well.

D3D9
foreach(constant in _constants)   switch(type)      case FLOAT: device.SetFloatConstant(constant)      case INT: device.SetIntConstant(constant)      ...


D3D10
struct cbuffer { float fVal; int iVal; };cbuffer cb = { your_float, your_int };Buffer.Map();memcpy(buffer, cb)Buffer.UnMap();Device.??SetConstants(cb)



Just remember that D3D9 can be quite performance-sensitive to this sort of operation, so a naive/trivial implementation like my one above may work fine for D3D10 but suck for D3D9. Be sure to profile it.


hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

For DX9 it’s better to write all constants to a memory block and then set them with a single call instead of using multiple calls. The reason for this is quite simple. There is a constant overhead per SetConstant call and an additional overhead depending on the amount of constants.

While the reducing the draw calls rule is well know there is a second one that tolds you to use as less other Direct3D calls per draw call as possible.

While Direct3D 10 is not that critical on the CPU side you need to be very carefully with the size of a constant buffer. Using large general buffers that contains many elements that are not used by a shader can easily kill your performances. There is some kind of constant buffer transfer limit that can easily become your bottleneck.

This topic is closed to new replies.

Advertisement