状態と状態マネージャ

API ではステート ブロックの概念に対してラッパーが導入されています。DirectX を使用している場合はこの概念にすでに詳しいでしょう。OpenGL を使用している場合、特に古いバージョンのこのインタフェースを使用している場合は、新しいものです。

ステート ブロックは状態変化の論理的なグループです。作成後は、これらのブロックは固定 GPU リソースであると仮定されます。

GPU ステート ブロックは、次のクラスによって API に反映されます。

他の GPU リソースと同様に、これらはリソース管理システムによって管理されます。これらのリソースの API インタフェースは MStateManager です。

ステート ブロックを使用するプロセスは次のとおりです。

  1. 新しいステート ブロックを作成するには:
    1. ステート ブロックの新しい記述が必要です。各ステート ブロック クラスには、次のように対応する記述子クラスがあります。
      1. MBlendState: MBlendStateDesc
      2. MRasterizerState: MRasterizerStateDesc
      3. MDepthStencilState: MDepthStencilStateDesc
      4. MSamplerState: MSamplerStateDesc

      記述子はリソースではなく、プラグインによって所有されます。

    2. 新しいステート ブロック インスタンスを取得するには、状態マネージャを使用します。
  2. 既存の状態を修正するには:
    1. レンダリング コンテキスト内の現在の状態は、状態マネージャで取得できます。
    2. ステート ブロックごとに、その記述子を取得できます。
    3. この記述子を必要に応じて修正し、状態のサブセットを変更できます。
    4. 新しいステート ブロック インスタンスを取得するには、状態マネージャを使用します。
  3. ステート ブロック インスタンス自体はリソースです。有効にするには、状態マネージャでブロックを設定する必要があります。これにより、ステート ブロックによって影響を受ける状態がオーバーライドされ、この状態は別のステート ブロック インスタンスがそれをオーバーライドするまで維持されます。
  4. ブレンド状態(MBlendState)は、出力レンダー ターゲットごとに設定できます。
  5. サンプラ状態(MSamplerState)は、シェーダ ステージおよびサンプラのインデックスの両方によってスコープされます。たとえば、サンプラはピクセル シェーダ ステージの 2 番目のサンプラに対して設定できます。

次の図では、状態とレンダリング コンテキスト全体の間の論理的な接続を示します。ブレンディングは、カラー ターゲットのプロパティです。深度とステンシルの状態は、深度 + ステンシル ターゲット ステージのプロパティです。サンプリングは、シェーダ ステージごとのプロパティです。ラスタライゼーションは、レンダー コンテキスト プロパティです。

図 33: 2 つのブレンド状態が 2 つの異なるカラー ターゲットに対して示され、深度 + ステンシル状態が深度 + ステンシル ターゲットに対して示されています。各シェーダは異なるサンプラ状態を持つ場合があり、コンテキスト ラスタライゼーション状態の関係があります。

次に示すのは、透明度をレンダリングするためによく行われるアルファ ブレンディングを実行するステート ブロックを作成するための簡単なコード例です。ステート ブロックを最初から作成する最初のアプローチを示します。

// 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); 

この API を使用して状態をコントロールする必要はありませんが、レンダリング フレームワークと密接に統合されているので、このインタフェースには多くのメリットがあります。これには、次のものが含まれます。

  1. ステート ブロックのインスタンスは、状態の記述に基づいて固有です。
  2. 各固有インスタンスはキャッシュされるので、再利用できます。
  3. 内部状態管理システムはこれらの変更を追跡し、冗長な変更をできる限り回避します。
  4. 状態インタフェースは描画 API に依存しません。

これらのインスタンスを作成して保持することは簡単ですが、GPU に固有のパフォーマンス コストのため、次のことをお勧めします。

  1. 行われる状態変更の数を最小限に減らします。
  2. その状態インスタンスをできる限り再利用します。

状態を変更するすべてのプラグインは、プラグインが描画を終了した後は前の状態を復元する必要があるというガイドラインに従う必要があります。これは状態が永続的であるためです。

実際の GPU リソースがあるので、DirectX11 を使用してリソース ハンドルを取得できます。これらは読み込み専用のリソースとして使用できます。リソース マネージャは何が変化したかわからないので、これらは変更できません。たとえば、ID3D11RasterizerState は resourceHandle() メソッドを使用して MRasterizerState から取得できます。