State and State Manager

The API introduces a wrapper for the concept of state blocks. Those using DirectX will be familiar with this concept already, but this could be something new for to those using OpenGL, especially for those using older version of this interface.

State blocks are logical groupings of state changes. Once created, these blocks are assumed to be immutable GPU resources.

GPU state blocks are reflected in the API via the following classes

As with other GPU resources, they are managed by the resource management system. The API interface for these resources is an MStateManager.

The process for using a state block is as follows:

  1. To create a new state block:
    1. A new description of the state block is required. Each state block class has a corresponding descriptor class as follows:
      1. MBlendState: MBlendStateDesc
      2. MRasterizerState: MRasterizerStateDesc
      3. MDepthStencilState: MDepthStencilStateDesc
      4. MSamplerState: MSamplerStateDesc

      Descriptors are not resources and are owned by the plug-in.

    2. Use the state manager to acquire a new state block instance.
  2. To modify an existing state:
    1. The current state in the render context can be returned via the state manager.
    2. For each state block its descriptor can be returned.
    3. This descriptor can be modified as required to change some subset of state.
    4. Use the state manager to acquire a new state block instance.
  3. A state block instance in itself is just a resource. To be enabled, the block must be set via the state manager. This overrides the states affected by the state block and this state persists until another state block instance overrides it.
  4. Blend states (MBlendState) can be set per output render target.
  5. Sampler states (MSamplerState) are scoped by both the shader stage as well as the index for the sampler. For example, a sampler can be set for the 2nd sampler in the pixel shader stage.

The following diagram shows the logical connections between state and the overall rendering context. Blending is a property of a color target; depth and stencil state is a property of a depth+stencil target stage; sampling is a prop­erty per shader stage; and rasterization is a render context property.

Figure 33: Two blend states are shown for two different color targets and a depth+stencil state is shown for a depth+stencil target. Each shader could have a different sampler state and there is a per context rasterization state relationship.

The following is a simple code example to create a state block to perform alpha blending which is commonly done to render transparency. It demonstrates the first approach of creating a state block from scratch.

// Create a new blend state descriptor. Set it to be (alpha, 1-alpha) blending and enable 
// blending. 
MHWRender::MBlendStateDesc desc; 
unsigned int i=0; // Set state for first render target 
desc.targetBlends[i].blendEnable = true; 
desc.targetBlends[i].destinationBlend = MHWRender::MBlendState::kSourceAlpha; 
desc.targetBlends[i].alphaDestinationBlend = MHWRender::MBlendState::kInvSourceAlpha; 
desc.targetBlends[i] blendOperation = MHWRender::MBlendState::kAdd; 
desc.targetBlends[i].alphaSourceBlend = MHWRender::MBlendState::kOne; 
desc.targetBlends[i].alphaDestinationBlend = MHWRender::MBlendState::kInvSourceAlpha; 
desc.targetBlends[i].alphaBlendOperation = MHWRender::MBlendState::kAdd;

// Use the state manager to acquire a new blend state and then enable it. 
MHWRender::MStateManager* stateMgr; 
const MHWRender::MBlendState* blendState = stateMgr->acquireBlendState(desc); 
stateMgr->setBlendState(blendState); 

Though it is not necessary to use this API to control the state, the interface has a number of advantages due to the tight integration with the rendering framework. This includes:

  1. Instances of state blocks are unique based on the state description.
  2. Each unique instance is cached and can thus be reused.
  3. The internal state management system tracks these changes and avoids redundant changes as much as possible.
  4. The state interface is draw API agnostic.

Though these instances are easy to create and hold on to, it is recommended that due to the inherent performance cost on the GPU:

  1. The number of state changes made is reduced to a minimum.
  2. That state instances be reused as much as possible.

Any plug-ins which change state must follow the guideline that the previous state must be restored after the plug-in is finished drawing. This is due to the fact that state is persistent.

A resource handle can be retrieved when using DirectX11 as real GPU resources exist. They can be used as read-only resources – they should never be modified as the resource manager would not know of what has changed. For example an ID3D11RasterizerState could be retrieved from an MRasterizerState using its resourceHandle() method.