Contents

[hide]

Concept

Much like state blocks, shader constant buffers are new tech introduced in TGEA 1.8 which help reduce the amount of rendering code you have to write. This concept is quite similar to DiretX 10's version of constant buffers, which share a similar purpose:


Quoted concepts from DirectX HLSL MSDN:

A constant buffer, or shader constant buffer, is a buffer that contains shader constants. 
Conceptually, it looks just like a single-element vertex buffer. 
A constant buffer is a specialized buffer resource that is accessed like a buffer. 
A buffer resource is designed to minimize the overhead of setting shader constants.

Technical Description

A shader constant buffer is just a block of memory (or an object) that contains the constants that a shader needs to have bound. Specifically, the buffer a collection of string/value pairs that are sent to a shader. They are allocated by the shader itself because the shader may have information about layout that no other part of the system will know about.


You can look up handles by shader constant name. This removes the need for a static shader constant to slot mapping and also allows us to do some CPU side optimizations. Under the hood, the string value pair is mapped to a block of memory that can be handed to a shader with one call.

Source Code

We are going to take a light tour of the engine code that makes up the GFXShaderConstBuffer system. Let's start by opening engine/source/gfx/gfxShader.h. Toward the top of this header, you will see a struct and two base classes that make up this system:

/// Instances of this struct are returned GFXShaderConstBuffer
struct GFXShaderConstDesc {...};


/// This is an opaque handle used by GFXShaderConstBuffer clients to set individual shader constants.
/// Derived classes can put whatever info they need into here, these handles are owned by the shader constant buffer
/// (or shader).  Client code should not free these.
class GFXShaderConstHandle {...};


/// GFXShaderConstBuffer is a collection of string/value pairs that are sent to a shader.
/// Under the hood, the string value pair is mapped to a block of memory that can
/// be blasted to a shader with one call (ideally)
class GFXShaderConstBuffer {...}


It should be obvious by the amount of pure virtual function declarations that GFXShaderConstHandle and GFXShaderConstBuffer are meant to have children. Both GFXD3D9 and GFXGL will get their own versions of shader constant buffers. In this file, there is not much you can learn from GFXShaderConstHandle. However, you might want to scan GFXShaderConstBuffer.


It is derived from StrongRefBase and GFXResource. Being a StrongRefBase object, the stateblock will exist as long as the reference exists. When all of the strong references to the stateblock go away, it is permanently deleted. It is treated as a GFXResource since our GFX system needs to track its resources owned by a particular device.


The virtual function declarations should give you insight on how we are setting up the future child functionality. We have almost twenty overloaded functions named set(...). Each one takes in a GFXShaderConstHandle* and a constant. The handles contains the name of the constant, which should be a name contained in the array returned in getShaderConstDesc.


It is very important that you remember what GFXShaderConstBufferRef is:


In engine/source/gfx/gfxShader.h:

typedef StrongRefPtr<GFXShaderConstBuffer> GFXShaderConstBufferRef;


GFXShaderConstBufferRef is like a pointer (reference) to GFXShaderConstBuffer, with a few key differences. A few paragraphs back I mentioned GFX tracking its resources and references, and this is one of the ways it does so. This is a referenced counted, object template pointer class (quite descriptive!). Heavy emphasis on the referenced counted.


If you open engine/source/gfx/gl/gfxGLShader.h, you can see how we have set up our OpenGL version of a constant shader buffer.


We have our child class declaration:

class GFXGLShaderConstBuffer : public GFXShaderConstBuffer{...}


The GL version contains an activation function:

/// Called by GFXGLDevice to activate this buffer.
void activate();


The class even keeps track of the shader that creates the buffer:

GFXGLShader* mShader;

/// Return the shader that created this buffer
virtual GFXShader* getShader() { return mShader; }


Of course, the multiple virtual void set(...) functions get defined as well, but in the gfxGLShader.cpp file. The base relationship you should remember is a GFX shader creates and uses a shader constant buffer, while the constant buffer keeps track of its owner and sets the actual constants.

Engine Example

As for an engine example, I'll start with a generic chunk of code:


Instead of:

GFX->setShaderVertexConstF(14, myConst);


We will set up our constant buffer and handle once

// Setup code
GFXShaderConstBuffer* myBuff = shader->allocConstBuffer();
GFXShaderConstHandle* myHandle = shader->getShaderConstHandle("$diffuseColor");


Now you can set the constant buffer

// Render code
myBuff->set(myHandle, myConst);
GFX->setShaderConstBuffer(myBuff);


Now that you see the basic code concept, let's see an actual working constant buffer. Most gamers and developers know what bloom is by now, so we are going to focus on code related to rendering the blur for a bloom effect. Open engine/source/lightingSystem/synapsegaming/sgDynamicRangeLighting.h/.cpp.


The sgDRLSurfaceChain class contains the two variables we want to follow for this example:

ShaderData *sgBloomBlur;
GFXShaderConstBufferRef sgBloomBlurConstBuffer;


Next, the setup begins...


In sgDynamicRangeLighting.cpp, sgDRLSurfaceChain::sgPrepChain(...):

sgBloomBlurConstBuffer = sgBloomBlur->mShader->allocConstBuffer();


The blur shader (sgBloomBlur) initializes our shader constant buffer. When we get into the render chain function (sgRenderChain), we get to the two big function calls for our system


Just after we set our stateblock in sgRenderChain(...):

GFX->setShaderConstBuffer(sgBloomBlurConstBuffer);

// MP Documentation Note - Slightly further down in code

Point4F temp(sgLightManager::sgBloomSeedAmount, sgLightManager::sgBloomSeedAmount,
			sgLightManager::sgBloomSeedAmount, sgLightManager::sgBloomSeedAmount);

sgLightManager::sgSafeSet(sgBloomBlurConstBuffer, sgDRLSurfaceChain::sgBloomSeedAmountShaderVar, temp);


Not the easiest concept to pick up and master, but the core functionality makes sense after you get used to reading through the rendering code.

Conclusion

The intent of this document was to provide you with a strong introduction to GFX shader constant buffers. There are various examples scattered throughout the engine, so you might want to spend some more time browsing through the code and comments.


Should you decide to create your own custom classes with renderable objects, remember to check this doc again and see how you can use constant buffers in your code. The optimization is well worth the learning curve.