Description Helpers For Direct3D 10, 10.1 and 11

In Direct3D 10, a number of device render states were aggregated into immutable objects. These immutable objects simplify state management on the device for the runtime and the driver, making it quicker to switch between state configurations on the device. These immutable state objects are created by filling in a description structure and creating the object. This mechanism is present in Direct3D 10.1 and Direct3D 11 as well. In this post, I will discuss some helper classes for constructing these state objects in a manner that more clearly reveals your intention.

Download D3DHelpers.zip

Rasterizer State

Consider the construction of a rasterizer state object where we desire to configure the rasterizer for antialiased wireframe rendering, while keeping all the other state the same as the default.

ID3D10RasterizerState *CreateWireframeState(ID3D10Device *device)
{
    D3D10_RASTERIZER_DESC desc;
    desc.FillMode = D3D10_FILL_WIREFRAME;
    desc.CullMode = D3D10_CULL_BACK;
    desc.FrontCounterClockwise = FALSE;
    desc.DepthBias = 0;
    desc.DepthBiasClamp = 0.0f;
    desc.SlopeScaledDepthBias = 0.0f;
    desc.DepthClipEnable = TRUE;
    desc.ScissorEnable = FALSE;
    desc.MultisampleEnable = FALSE;
    desc.AntialiasedLineEnable = TRUE;
    ID3D10RasterizerState *state = 0;
    THR(device->CreateRasterizerState(&desc, &state));
    return state;
}

This code doesn’t do a particularly good job of communicating that we want to keep the defaults and only change a few values from their defaults. To recognize the defaults, we need to look up the D3D10_RASTERIZER_DESC structure in the documentation and compare the values used one by one. An alternative approach would be to use an aggregate initializer for the structure:

ID3D10RasterizerState *CreateWireframeState(ID3D10Device *device)
{
    D3D10_RASTERIZER_DESC desc =
    {
        D3D10_FILL_WIREFRAME, D3D10_CULL_BACK, FALSE,
        0, 0.0f, 0.0f,
        TRUE, FALSE, FALSE, TRUE
    };
    ID3D10RasterizerState *state = 0;
    THR(device->CreateRasterizerState(&desc, &state));
    return state;
}

This code is a little shorter, but still doesn’t reveal which values are different from the default. We have further obscured what we are doing because we don’t even have the structure member names to help us understand what part of the state object is being set to what value.

Is there a better way of communicating our intent?

Builder Classes

We can build ourself a little helper class to build the description structure using intention revealing methods. A common idiom for such a helper class is to provide a method that sets a particular piece of the structure to its argument and then returns a reference to the structure. Since each method returns a reference to the structure, the methods can be daisy chained in any order by applying the next method to the result of the previous method. A small example clarifies the pattern:

ID3D10RasterizerState *CreateWireframeState(ID3D10Device *device)
{
    rt::D3D10::RasterizerDescription desc;
    desc.FillMode_(D3D10_FILL_WIREFRAME).AntialiasedLineEnable_(TRUE);
    ID3D10RasterizerState *state = 0;
    THR(device->CreateRasterizerState(&desc, &state));
    return state;
}

When we look at this code, we immediately know which values differ from their defaults: fill mode and the antialiased line enable. Here, RasterizerDescription is a class in namespace rt::D3D10 derived from D3D10_RASTERIZER_DESC. The constructor initializes all the members of the structure to their defaults, as described by the Direct3D documentation. The builder methods FillMode_ and AntialiasedLineEnable_ are used to set the non-default values. To distinguish the methods from the structure members, an underscore is appended to the name. Because the helper derives from the description structure, we can use its address anywhere we need the address of the structure.

Making It Shorter

The code above is good because its now revealing our intentions explicitly. However, we still have this noise of the desc variable and the state variable. Can we enhance the builder class to eliminate this repetitive code that doesn’t reveal much? How about putting that creation business in a Create method on the builder class like this:

ID3D10RasterizerState *CreateWireframeState(ID3D10Device *device)
{
    return rt::D3D10::RasterizerDescription()
        .FillMode_(D3D10_FILL_WIREFRAME).AntialiasedLineEnable_(TRUE)
        .Create(device);
}

Wow, that’s pretty concise. We can read this code out loud and know what it does: default rasterizer description, with fill mode wireframe, with antialiased line enabled, create state for the device. Now it feels like I don’t even need this method with an intention revealing name because I could just as easily write that single statement. (You might still want the intention revealing method if you’re doing this operation in multiple places, but with the nature of immutable state objects in Direct3D 10/10.1/11, you should be creating only one state object with this particular state and reusing it.)

Mechanics of a Builder Class

OK, so what do these builder classes look like on the inside? Here’s an example from the RasterizerDescription class.

struct RasterizerDescription : public D3D10_RASTERIZER_DESC
{
    explicit RasterizerDescription() : D3D10_RASTERIZER_DESC()
    {
        FillMode = D3D10_FILL_SOLID;
        CullMode = D3D10_CULL_BACK;
        FrontCounterClockwise = FALSE;
        DepthBias = 0;
        DepthBiasClamp = 0.0f;
        SlopeScaledDepthBias = 0.0f;
        DepthClipEnable = TRUE;
        ScissorEnable = FALSE;
        MultisampleEnable = FALSE;
        AntialiasedLineEnable = FALSE;
    }
    RasterizerDescription &FillMode_(D3D10_FILL_MODE value)
    {
        FillMode = value;
        return *this;
    }
    // other similar builder methods...

    ID3D10RasterizerState *Create(ID3D10Device *device)
    {
        ID3D10RasterizerState *result = 0;
        THR(device->CreateRasterizerState(this, &result));
        return result;
    }
};

The constructor is straightforward: it makes sure that all the members are explicitly initialized to their defaults. The members are explicitly initialized because D3D10_RASTERIZER_DESC is a plain old datastructure with no explicit constructor. The builder methods are very straightforward and are all similar to the one shown for FillMode. The Create method is similar to the code we had in our earlier examples.

Using the Builders

To use the provided code, add the D3DHelpers library to your path and include the appropriate file:

Direct3D Version Header File
10 <rt/D3D10Descriptions.h>
10.1 <rt/D3D10_1Descriptions.h>
11 <rt/D3D11Descriptions.h>

All classes are located within the rt namespace, with nested namespaces D3D10, D3D10_1 or D3D11 depending on the description file. The builders are completely contained within the header file, so there is nothing else you need to include or compile. Due to the very repetitive nature of the implementation, macros are used to eliminate the duplication in the header.

Unit Testing the Builders

Included with the builders are suites of C++ unit tests written with Boost.Test for testing all the functionality of the builders. You will need a boost distribution if you want to compile and execute the unit tests.

kick it on GameDevKicks.com

4 Responses to “Description Helpers For Direct3D 10, 10.1 and 11”

  1. DieterVW Says:

    You might also consider looking at the CD3D11_XXX_DESC classes added as helpers for a similar reason. These aren’t documented on MSDN, but they are in the headers. A version can be constructed with all default values. For instance CD3D11_RASTERIZER_DESC desc( D3D11_DEFAULT ); will do just that. They’re most useful when making the texture descriptions.

    Like

  2. legalize Says:

    When these classes are available, my description helper classes derive from them, mostly for the constructor that creates a structure filled with default values.

    However, I find their usage similar to the structure aggregate approach because they end up with the long parameter list code smell and if you’re using constant values and not variables with intention revealing names, its not clear exactly what you’re setting with those classes.

    I provide one class for each variant of the discriminated unions so that only the appropriate builder methods are exposed. The Direct3D 11 classes handle this with an overloaded parameter list that I find more confusing and less revealing of my intention when reading source code.

    These classes are not uniformly available for Direct3D 10, 10.1 and 11. There are some helper classes in the headers for 10 and these are carried over to 11, but the description classes are present only in 11.

    If you use the builders I’ve provided, you can use the same mechanism for all three versions.

    Like

  3. Direct3D Graphics Pipeline Sample Code « Legalize Adulthood! Says:

    […] related articles that I write on this blog. Initially you will find the code from my article on description helpers, but I will be uploading the sample code from the book there in the next few days. Posted in […]

    Like

  4. “Domain Specific Languages” by Martin Fowler « Legalize Adulthood! Says:

    […] the syntactical constructs of some other language, called the host language. In my blog post on description helpers for Direct3D, I showed how to create Direct3D resources using method chaining. You could think of this technique […]

    Like


Leave a comment