2017-03-08 18 views
0

私が実装した単純なDirectXレンダリングコードを改善しようとしています。可能であれば、パイプラインの変更回数を最小限に抑えることが有益だと思うので、私の考えは、絶対に必要なときだけレンダリングパイプラインを更新することです。私はこれで意味次の擬似コードで実証されていますDirectXパイプラインの最適化の理解

ID3D11VertexShader *t_shader = getVertexShader(); 
ID3D11DeviceContext->VSSetShader(t_shader, nullptr, 0); 
// Do some other processing/pipeline setup without modifying t_shader 
ID3D11DeviceContext->VSSetShader(t_shader, nullptr, 0); 
ID3D11DeviceContext->Draw(10, 0); 

シェーダが変更されていないときに我々は二回VSSetShader呼び出しているので、これは非効率的です。これは単純化されていますが、うまくいけば私はどこから来ているのか、私の基本的な理解はこれらのタイプの不要なバインド/呼び出しは非効率的ですか?

この場合、2つの別々のID3D11DeviceContext :: Draw呼び出しの間で以下の最適化を行うことは可能ですか? (再び擬似コードはとても不足している手順を赦し、私たちが描く前に、我々が行うために必要なすべてのトポロジと一緒に頂点&ピクセルシェーダを設定されていると仮定してください):

void Object1::Draw() { 
    ID3D11VertexShader *t_vs = ShaderMgr::vertexShader1(); 
    ID3D11DeviceContext->VSSetShader(t_vs, nullptr, 0); 

    ID3D11PixelShader *t_ps = ShaderMgr::pixelShader1(); 
    ID3D11DeviceContext->PSSetShader(t_ps, nullptr, 0); 

    ID3D11DeviceContext->IASetPrimitiveTopology(ID3D11_PRIMITIVE_TOPOLOGY_LINELIST); 
    ID3D11DeviceContext->Draw(m_vertexCount, 0); 
} 

void Object2::Draw() { 
    ID3D11VertexShader *t_vs = ShaderMgr::vertexShader1(); 
    ID3D11DeviceContext->VSSetShader(t_vs, nullptr, 0); 

    // Use a different pixel shader to Object1 
    ID3D11PixelShader *t_ps = ShaderMgr::pixelShader2(); 
    ID3D11DeviceContext->PSSetShader(t_ps, nullptr, 0); 

    ID3D11DeviceContext->IASetPrimitiveTopology(ID3D11_PRIMITIVE_TOPOLOGY_LINELIST); 
    ID3D11DeviceContext->Draw(m_vertexCount, 0); 
} 

2つの引き分けの唯一の違いは、呼び出しをすることは、使用されます別のピクセルシェーダの次のような最適化が可能か、各描画呼び出しでパイプラインを効果的にリセットしますか?

void Object1::Draw() { 
    // Removed common set code 
    ID3D11PixelShader *t_ps = ShaderMgr::pixelShader1(); 
    ID3D11DeviceContext->PSSetShader(t_ps, nullptr, 0); 
    ID3D11DeviceContext->Draw(m_vertexCount, 0); 
} 

void Object2::Draw() { 
    // Removed common set code 
    ID3D11PixelShader *t_ps = ShaderMgr::pixelShader2(); 
    ID3D11DeviceContext->PSSetShader(t_ps, nullptr, 0); 
    ID3D11DeviceContext->Draw(m_vertexCount, 0); 
} 

void drawObjects() { 
    // Common states amongst object1 and object2 
    ID3D11VertexShader *t_vs = ShaderMgr::vertexShader1(); 
    ID3D11DeviceContext->VSSetShader(t_vs, nullptr, 0); 
    ID3D11DeviceContext->IASetPrimitiveTopology(ID3D11_PRIMITIVE_TOPOLOGY_LINELIST); 

    m_object1->draw(); 

    // Don't bother setting the vs or topology here 

    m_object2->draw(); 
} 

フィードバック/情報は大歓迎です。

+1

冗長性をキャッチするために、状態キャッシングを行うクラス内にデバイスをカプセル化するだけで済みます。ドライバーが通常不要な状態の変更をキャッシュしてテストしているため、これらの呼び出しを取り除くと、実際に悪いことが行われない限り、通常はパフォーマンスが向上しません。 – galop1n

+0

これは私が現時点で実装しようとしている、パイプラインの "状態"があると思われる最初の描画呼び出しの後に、私は分で見ている問題、必要なときだけ状態を変更するPipelineクラスを設計していますリセットされ、すべてをもう一度設定する必要があります。入力トポロジ、頂点バッファなど – TheRarebit

+1

DirectX12がパイプライン状態(パイプライン状態オブジェクト)をカプセル化する方法を見てみることができます。 DirectX 11を熟練したユーザーでない限り、実際にDirectX 12を使用することはお勧めしませんが、デザインには最新のGPUハードウェアの設定が反映されます。 –

答えて

0

問題を混乱させていたテストコードのバグを見つけたので、私自身の質問に答えを投稿してください。うまくいけば、これは他の誰かに役立つでしょう。

私の混乱は、私のテストコードでは、私がレンダリングしていた単一のオブジェクト、つまり単純なプレーンしか持っていなかったということです。唯一のリソースは、頂点バッファ、頂点シェーダ、ピクセルシェーダです。私は、ID3D11DeviceContext呼び出しの数を可能な限り減らすために、上記の最適化を追加しようとしました。この単純なオブジェクトの場合、ID3D11DeviceContextが呼び出すことは賢明だったようです。 VSSetShader、PSSetShaderなどは、唯一のオブジェクトがレンダリングされていたため、一度呼び出す必要があります。しかし、グリッドが一度消えて再びレンダリングされてしまえば、これは当てはまりませんでした。

RenderDocの助けを借りて、私はレンダリングされたフレームをキャプチャすることができ、私が1つだけを期待していたときに2つの描画呼び出しが行われていることに気付きました。私はSprectFontとSpriteBatchクラスをDIrectXTKを使って作成して、デバッグのためにカメラ位置を書き出すのに使用していたことを忘れてしまった。この呼び出しは、私が気付かないうちにパイプラインの状態を修正していました(これらの最適化を制御していた)。これは、グリッドが2度目にレンダリングされたときに、パイプラインが正しくない状態にあることを意味していました。

したがって、これらの最適化が可能であり、描画呼び出しの結果としてパイプラインがクリアされないことが判明しました。したがって、上記の例のようなものがあれば、呼び出しの間にコンテキスト呼び出しを1回呼び出すだけで十分です。私はレンダリングデバッガでビルドされたRenderDocやVisual Studiosのようなデバッグツールが、これらのタイプの問題を追跡するのに不可欠であることも知っています。