You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Much like state blocks, shader constant buffers help reduce the
amount of rendering code you have to write. This concept is quite
similar to DirectX 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, they 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:
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:
WeakRefPtr<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:
We will set up our constant buffer and handle once
Now that you see the basic code concept, let's examine an
existing constant buffer in the engine. The CloudLayer class handles
its shader constant buffer internally. Open engine/source/environment/cloudLayer.cpp. Scroll down until you see the following function:
bool CloudLayer::onAdd(){...}
Further into the function, around line 92, you can see where the internal GFXShaderConstBufferRef (mShaderConsts) is allocated:
// Create ShaderConstBuffer and Handles
mShaderConsts = mShader->allocConstBuffer();
The GFXShaderConstHandle pointers are internal members belonging to the CloudLayer class:
The actual setting of the shader data, constant buffer, and stateblock
does not happen until further down in the source file. If you scroll
down to around line 264, you will find the rendering function for
CloudLayer:
On line 276, GFX takes over and performs the "set" code:
GFX->setShader( mShader );
// HERE WE SET THE SHADER CONSTANT BUFFER
GFX->setShaderConstBuffer( mShaderConsts );
GFX->setStateBlock( mStateblock );
Script Example
Using shader constant buffers in TorqueScript is a little different
than in the engine code. At this time, most game developers know about
the SSAO (Screen Space Ambient Occlusion) rendering technique. Torque 3D has a SSAO solution, which is defined in
TorqueScript.
SSAO is a PostEffect, so it must be defined as such. Locate and open Examples/FPS Example/game/core/scripts/client/postFx/ssao.cs. The effect is declared using the following code (reduced to just show the declaration):
singleton PostEffect( SSAOPostFx ){...};
In the CloudLayer example, I mentioned the class contained
internal GFXShaderConstHandle pointers as member variables. In
TorqueScript, SSAO uses scoped global variables:
These variables are used when setting the buffer. As a script
object, SSAOPostFx can have member functions. An important function to
define for PostEffect objects is setShaderConsts(%this). If a PostEffect object, such as SSAOPostFx, has this function defined, it will be called by the engine automatically.
If you scroll down to the function, you can see how it sets the shader constant buffer:
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 code, 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 document again and see how you can use
constant buffers in your code. The optimization is well worth the
learning curve.