Constant Buffers and Shaders

Started by
2 comments, last by Samith 10 years, 4 months ago

Hi,

I tried to find some information about how I should create constant buffers. I know that I should create constant buffers per frame or per object...

But every tutorials which I found, use Effects for rendering(which are deprecated, I'm try to move on new approach) Nvidia SDK, DirectX SDK even Books.They create one huge buffer and used it throughout all shaders in Effect(I don't know if this is good approach, but what I read -> it isn't).

I insert every shader into its own file. My question is, should I create constant buffer for every shader, for example: I used in VS ViewProj and CameraPosition, but in DS I used CameraPosition too and I don't know, maybe TextureTilingFactor (both Buffers are updated per Frame). I Should create one CB for VS and one for DS, or should I create one buffer which has this three elements and follows this approach as shaders will be more and more?

My second question is about shaders. Now I'm using small numbers of shaders(max 8) but if I want to create a game(in the future) and this shaders I'll have about 100(I don't know how many shaders uses game) which approach is good to manage them, some flexible and powerful solution?

Thanks for answers.

Advertisement

Here's what I've done in the past which seemed to work fairly well (disclaimer: the technique I'm about to describe was never used in a shipped title, so it may not hold up under the stress of a full game, but it worked well enough during the early stages of development)

Any resource (constant buffer, texture, sampler) that the shader had access to I split into two categories: "system" resources and "material" resources. System resources were things that the game engine would create, manage and provide to the shader, and material resources were things that the material would provide (a material, in case you're wondering, is essentially just a collection of constant buffers/textures and GPU state settings, it's authored by an artist and stored on disk as data). For example, a system constant buffer might contain the model, view and projection matrices, whereas a material constant buffer might contain a specular power field, model diffuse color, or some other constant, artist authored value.

I denoted any system resource by naming it with a special prefix ("sys" in my case). So the constant buffer that contains the transformation matrices might look like this:


cbuffer sys_Transform {  // sys prefix indicates this is a "system" constant buffer
    float4x4 ModelMatrix;
    float4x4 ViewMatrix;
    float4x4 ProjectionMatrix;
};

I would put the system constant buffer declarations in a header file, and any shader that wanted that particular system constant buffer's data could include the header file to get that constant buffer. Then, when I loaded the shader at runtime, I would look through all the constant buffers used in the shader and determine which ones were system constant buffers (by seeing if they had the "sys" prefix) and keep track of which system constant buffers the shader used. When I bound the shader to the pipeline, I would make sure the system constant buffers that shader used were up to date and bound to the correct location. That way any shader could use any "system" constant buffer and no programmer would have to worry about getting the correct constants bound to the pipeline, it would all happen automatically.

I could have as many shaders as I wanted in the game, and it wouldn't require me to duplicate constant buffers. I would only have one copy of each system constant buffer, and it would get bound whenever a shader that used it got bound.

The other category of constant buffers is the material constant buffers. They are much simpler. When our data got built, the material builder would build these constant buffers directly into the material file. Then at runtime when the material was loaded, that data would be used to create a constant buffer, and when the material was selected the constant buffers it contained would get bound as well.

I've glossed over some stuff because this post is feeling kinda long, but if you want me to explain anything in more detail just let me know biggrin.png

Thanks for quick answer. Your system looks pretty good. If I understand it, it works follows:

1. You decompose buffers inside two category.

2. You create header shader file for both category, and include it into every shader which want to use them.

3. In C++ code you ensure that buffers are set.

If you have two shaders headers files and insert both of them inside one shader, how do you handle this:

You must(or should, or don't have to?) register every buffer inside some slot(register b(0), register b(1) etc.)

You use slots 0-8 for systems buffers and 9 - 15 for materials?.

But you don't answer me on my primary question:

We assume that I want use this variables in VS:


float4x4 gViewProj;
float3 gCameraPosition;

and this in HS :


float4 gFrustumPlanes[6];
float gMinTessDistance;
float gMaxTessDistance;
float gMinTessFactor;
float gMaxTessFactor;

We assume that this buffers are updated per frame.

My primary question was: Should I create two buffers for this example or should I insert this variables inside one big CB?

My primary question was: Should I create two buffers for this example or should I insert this variables inside one big CB?

Sorry, didn't quite understand your first question originally. I've got it now, though. I would have one CB for the gViewProj and gCameraPosition which gets bound in the VS and wherever else it's needed, then I would put the tessellation stuff in a separate CB that only gets bound to the tessellation stages. In addition to being a conceptually separate set of parameters, the tessellation factors seem like something that would probably get updated more frequently than the view/proj matrices, so it would be best to put them in their own CB.

If you have two shaders headers files and insert both of them inside one shader, how do you handle this:

You must(or should, or don't have to?) register every buffer inside some slot(register b(0), register b(1) etc.)

You use slots 0-8 for systems buffers and 9 - 15 for materials?.

My setup will actually let the compiler put the constant buffers wherever it wants, and the runtime will make sure everything is bound to the correct location. This is a tiny bit complicated, and it might actually be better to do it the way you suggested, with system buffers using bind points 0-7 and material buffers using 8-15. But either way works!

This topic is closed to new replies.

Advertisement