<table border="0" cellpadding="0" cellspacing="0" width="700">
<tbody>
<tr>
<td width="700"><table id="toc" summary="Contents">
<tbody>
<tr>
<td><div id="toctitle">
<h2>Contents</h2>
<ul>
<li class="toclevel-1"><a href="#Concept"><span class="tocnumber">1</span> <span class="toctext">Concept</span></a></li>
<li class="toclevel-1"><a href="#Technical_Description"><span class="tocnumber">2</span> <span class="toctext">Technical Description</span></a></li>
<li class="toclevel-1"><a href="#Source_Code"><span class="tocnumber">3</span> <span class="toctext">Source Code</span></a></li>
<li class="toclevel-1"><a href="#GFXSamplerStateDesc"><span class="tocnumber">4</span> <span class="toctext">GFXSamplerStateDesc</span></a></li>
<li class="toclevel-1"><a href="#GFXStateBlockDesc"><span class="tocnumber">5</span> <span class="toctext">GFXStateBlockDesc</span></a></li>
<li class="toclevel-1"><a href="#GFXStateBlock"><span class="tocnumber">6</span> <span class="toctext">GFXStateBlock</span></a></li>
<li class="toclevel-1"><a href="#GFXStateBlockData"><span class="tocnumber">7</span> <span class="toctext">GFXStateBlockData</span></a></li>
<li class="toclevel-1"><a href="#Engine_Example"><span class="tocnumber">8</span> <span class="toctext">Engine Example</span></a></li>
<li class="toclevel-1"><a href="#Script_Example"><span class="tocnumber">9</span> <span class="toctext">Script Example</span></a></li>
<li class="toclevel-1"><a href="#Conclusion"><span class="tocnumber">10</span> <span class="toctext">Conclusion</span></a></li>
</ul></td>
</tr>
</tbody>
</table>
<a name="Concept" id="Concept"></a>
<h2> <span class="mw-headline">Concept</span></h2>
<p>The purpose of GFXStateblocks is quite simple: an entire rendering
state is contained in one object, and you are able to set the rendering
state with one call. If you've written rendering code before, you may
have written code similar to this before: </p>
<pre>// Enable Blending
glEnable (GL_BLEND);
// Set The Blend Mode
glBlendFunc (GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA)
What if you need to setup that kind of rendering state more than
just once? GFXStateblocks allow you to wrap that up into the following:
GFX->setStateBlock(myState);
There's a little more tech and setup involved, but the above
example should be enough to encourage you to read on and use this
concept in your own project.
Using GFXStateblocks allows you to pre-generate your rendering
states, which is going to save you time, cleanup your code, apply
modern rendering techniques, and give you more control over your game's
rendering from the engine as well as script. When combined with CustomMaterials, the material definitions in script need less custom engine support.
Stateblocks are created by filling out a GFXStateBlockDesc structure
and calling GFX->createStateBlock on it. This returns a
GFXStateBlockRef, which is a reference-counted GFXStateBlock. This is
essentially a GFXResource, just like a texture. Then you use a
stateblock by calling GFX->setStateBlock and passing the state block
in.
Using a stateblock fully defines a render state, which
completely does away with all concepts canonical. This is actually a
good thing as this will reduce bugs introduced because a state was not
properly cleaned up before exiting.
It might help if we tour the engine code, so you can get some concrete code samples in front of your eyes. Start by opening gfxStateBlock.h and gfxStateBlock.cpp, both of which are found in engine/source/gfx. Let's browse the header (.h) first. All four major declarations are important here:
struct GFXSamplerStateDesc {...};
struct GFXStateBlockDesc {...};
class GFXStateBlock {...};
typedef StrongRefPtr<GFXStateBlock> GFXStateBlockRef;
The GFXSamplerStateDesc struct contains variables and methods
to identify the texture object used for each texture lookup. You'll
find texture arguments for color, alpha, min/mag/mip filter, and
address modes. A solid example of how this structure is used can be
found in the gfxD3D9StateBlock::activate(...) function.
Inside this method, a GFXSamplerStateDesc variable (ssd) is
set to one of the stateblocks internal samplers. The function performs
a check to see how OpenGL handles the GL_TEXTURE_2D variable:
Abbreviated code from GFXGLStateBlock::activate(...), found in engine/source/gfx/gl/gfxGLStateBlock.cpp
:
const GFXSamplerStateDesc ssd = mDesc.samplers[i];
switch (ssd.textureColorOp)
{
case GFXTOPDisable:
if(!tex)
break;
glDisable(GL_TEXTURE_2D);
updateTexParam = false;
break;
}
GFXStateBlockDesc defines a render state, which is then used
to create a GFXStateBlock instance. If you look through the structure,
you will read through some common render state ops and references:
blending (source/destination/operation), depth buffer (z buffer
enable/bias/definition), color writes (write red/blue/green/alpha), and
so on.
A nifty concept applied through GFXStateBlockDescr is the
combining state block descriptions. This is done so through
the::addDesc(...) function:
/// Adds data from desc to this description, uses *defined
parameters in desc to figure out what blocks of
state to actually copy from desc.
void addDesc(const GFXStateBlockDesc& desc);
<a name="GFXStateBlock" id="GFXStateBlock"></a>
<h2> <span class="mw-headline">GFXStateBlock</span></h2>
<p>The base GFXStateBlock class looks very bare. 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. </p>
<p><br />
There are only 4 functions in this base class, and they are
all virtual. This is due to the fact that we have multiple graphical
APIs to support, which means we are going to have GFXStateBlocks for
DirectX and OpenGL: </p>
<ul>
<li>GFXD3D9StateBlock </li>
</ul>
<ul>
<li>GFXGLStateBlock </li>
</ul>
<p><br />
Of course, we have our null device stateblock
(GFXNullStateBlock), but you will not actually need to use that for any
rendering. The other GFX devices will have their own implementation of
stateblocks. In fact, let's take a look at the GL implementation. Open <i>engine/source/gfx/gl/gfxGLStateBlock.h</i>. The class we are looking at is <b>GFXGLStateBlock</b>. </p>
<p><br />
Within this function we have a working interface (constructor and
destructor), a function called by OpenGL to activate the stateblock
(::activate), and a tangible GFXStateBlockDesc member variable (mDesc).
The activate function definition can be found in <b>gfxGLStateBlock.cpp</b> </p>
<pre>GFXGLStateBlock::activate(const GFXGLStateBlock* oldState)
{
// Internal code not shown
}
It is heavily commented and has a very important warning at the
beginning. It is highly recommended you read through the comments and
code to get a better understanding of how the stateblock is set up.
Two very important classes we need to take a look at is the GFXStateBlockData class and GFXSamplerStateData
class, found in engine/source/gfx/sim/gfxStateBlockData.h/.cpp. The class definitions and their comments are quite descriptive:
/// Allows definition of render state via script,
basically wraps a GFXStateBlockDesc
class GFXStateBlockData: public SimObject
/// Allows definition of sampler state via script,
basically wraps a GFXSamplerStateDesc
class GFXSamplerStateData: public SimObject
As you read, these classes allow us to create GFXStateBlock and
GFXSamplerState descriptions in TorqueScript. This means you are able
to utilize a lot more custom shader and rendering work without being as
reliant on custom engine code. You should definitely look through
the::initPersistFields() functions for each class. You will
notice the various references and ops that make up a render state are
exposed.
We are going to use a very simple engine example for showing how GFXStateBlocks are created and used. Start by opening engine/source/interior/interior.h/.cpp.
When we use debug rendering, we are able to see certain aspects of an
interior that are normally invisible to a player. One such aspect would
be the rendering of portals.
In the Interior class, we have defined multiple stateblock references. Let's look at one that affects our portal rendering:
GFXStateBlockRef mInteriorDebugPortalSB;
Ok! We have our stateblock reference, but it still needs some
information. Go into Interior.cpp, and scroll down to the
prepForRendering function around line 343:
bool Interior::prepForRendering(const char* path)
{
// Previous and remaining code not shown
mInteriorDebugPortalSB = GFX->createStateBlock(sh);
}
This line of code shows we have initialized our stateblock
reference, but what device does so? We haven't seen any OpenGL or
DirectX code anywhere, so how can we know if we are using a
GFXGLStateBlock or not? This is where GFX takes control. GFX->createStateBlock(...) takes in a fully initialized GFXStateBlockDesc reference.
GFX then proceeds to set up the hash value and search for existing
stateblocks. If the stateblock we need does not exist, it calls createStateBlockInternal.
This function drills into our platform's device, so we have a
GFXGLDevice::createStateBlockInternal and a
GFXD3D9Device::createStateBlockInternal.
When creating a GFXStateBlock in script, quite a few things
happen for you automatically. However, when you are in the engine there
are a few "Gotchas" you have to remember. When you create a GFXStateBlock in the engine, you must call setStateBlock before you use it!
Called in Interior::debugRenderPortals():
GFX->setStateBlock(mInteriorDebugPortalSB);
A simple, more generic engine example would be this:
// Setup code, done once
GFXStateBlockDesc desc;
desc.setBlend(true, GFXSrcAlpha, GFXInvSrcAlpha);
GFXStateBlockRef myState = GFX->createStateBlock(desc);
// Render time code
GFX->setStateBlock(myState);
Earlier, I mentioned using shader data and GFXStateBlocks together
in script. We have provided you with a couple of examples. Open
Examples/FPS Example/game/core/scripts/client/postFX.cs. This file
contains multiple GFXStateBlockData and ShaderData definitions. First,
we focus on the default stateblock:
singleton GFXStateBlockData( PFX_DefaultStateBlock )
{
zDefined = true;
zEnable = false;
zWriteEnable = false;
samplersDefined = true;
samplerStates[0] = SamplerClampLinear;
};
The above code demonstrates how to declare a GFXStateBlock in TorqueScript using the exposed Console Object, GFXStateBlockData.
In this example, we define depth sorting and handle linear clamping. We
can then use this stateblock in a PostEffect definition:
singleton PostEffect( BL_ShadowFilterPostFx )
{
requirements = "";
shader = BL_ShadowFilterShader;
stateBlock = PFX_DefaultStateBlock;
targetClear = "PFXTargetClear_OnDraw";
targetClearColor = "0 0 0 0";
texture[0] = "$inTex";
target = "$outTex";
};
A GFXStateBlockData definition can also be used in the creation of a separate stateblock, as shown below:
singleton GFXStateBlockData( LightRayStateBlock: PFX_DefaultStateBlock )
{
samplersDefined = true;
samplerStates[0] = SamplerClampLinear;
samplerStates[1] = SamplerClampLinear;
};
Essentially, we wanted the exact same rendering state for our two
post processing effects. Instead of having to modify our engine code
(twice), we can define our stateblock in script (once) and use it
multiple times.
There is a lot to learn about GFXStateBlocks. The intent of this article
was to give you a strong introduction on the purpose, engine structure,
and examples of how to use this new system. As you develop new shaders,
look for ways you can save yourself time and headaches by using
GFXStateBlocks.
</td>
</tr>
</tbody>
</table>