2017-03-08 2 views
0

J'essaie d'améliorer un simple code de rendu DirectX que j'ai implémenté. Mon idée est de ne mettre à jour le pipeline de rendu que lorsque cela est absolument nécessaire car, d'après ce que je comprends, il est avantageux de minimiser autant que possible le nombre de modifications du pipeline. Ce que je veux dire par là est démontré dans le pseudo-code suivant:Comprendre les optimisations de pipeline 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); 

Ceci est inefficace parce que nous appelons VSSetShader deux fois lorsque le shader n'a pas changé. C'est une simplification excessive, mais j'espère que vous obtenez d'où je viens, ma compréhension de base est ce type de liens inutiles/appels sont inefficaces?

Si tel est le cas, alors est-il possible d'effectuer l'optimisation ci-dessous entre deux appels ID3D11DeviceContext :: Draw distincts? (Encore une fois pseudocode donc s'il vous plaît pardonnez les étapes manquantes et d'assumer tout ce que nous devons faire est de définir avec une topologie d'un pixel shader sommet & le long avant de tirer):

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

La seule différence entre les deux tirage au sort appelle est l'utilisation d'un pixel shader différent. Est-ce que la suivante est une optimisation possible ou est-ce que chaque appel de tirage réinitialise efficacement le pipeline?

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(); 
} 

Une rétroaction/information serait grandement appréciée.

+1

Vous pouvez simplement encapsuler le périphérique dans une classe qui effectue la mise en cache d'état pour intercepter la redondance. Habituellement, le gain de perf, à moins que quelque chose de vraiment mauvais ne soit fait, est minime lorsque vous vous débarrassez de ces appels de toute façon car le pilote cache et teste d'habitude les changements d'état inutiles déjà. – galop1n

+0

C'est ce que j'essaie d'implémenter en ce moment, en concevant une classe Pipeline qui ne change d'état qu'en cas de besoin, le problème que je vois à la minute est après un appel draw initial, l'état du pipeline semble avoir été réinitialisé et je dois tout remettre en place, par exemple InputTopology, tampons de vertex etc. – TheRarebit

+1

Vous pouvez voir comment DirectX12 encapsule l'état du pipeline (Pipeline State Objects). Je ne suggère pas que vous utilisez réellement DirectX 12 à moins que vous soyez déjà un utilisateur expert de DirectX 11, mais la conception reflète les préférences du matériel GPU moderne. –

Répondre

0

juste poster une réponse à ma propre question car j'ai repéré un bogue dans mon code de test qui obscurcissait le problème et j'espère que cela aidera quelqu'un d'autre qui voit. Ma confusion était due au fait que dans mon code de test je n'avais qu'un seul objet que je rendais, un simple plan. Les seules ressources utilisées étaient un vertex buffer, un vertex shader et un pixel shader. J'ai essayé d'ajouter les optimisations mentionnées ci-dessus pour essayer de réduire autant que possible le nombre d'appels ID3D11DeviceContext. Pour cet objet simple, il m'a semblé logique que les appels ID3D11DeviceContext, par ex. VSSetShader, PSSetShader etc., ne doivent être appelés qu'une seule fois car c'est le seul objet qui a été rendu. Cependant, ce n'était pas le cas, une fois que la grille a été rendue une fois qu'elle a disparu et n'a jamais été restituée. Avec l'aide de RenderDoc, j'ai été capable de capturer un cadre rendu et j'ai remarqué qu'il y avait deux appels de tirage au sort quand je n'en attendais qu'un. J'avais oublié que j'avais une classe SpriteFont et SpriteBatch créée par DIrectXTK en train d'écrire ma position de caméra pour le débogage. Cet appel modifiait l'état du pipeline en contournant ma classe de pipeline (qui contrôlait ces optimisations) sans que je m'en rende compte. Cela signifiait que lorsque la grille était restituée une seconde fois, le pipeline était dans un état incorrect.

Il s'avère donc que ces optimisations sont possibles et que le pipeline n'est pas effacé à la suite d'un appel de tirage. Donc, si vous avez quelque chose comme l'exemple ci-dessus, il suffit d'appeler les appels de contexte une fois entre les appels. J'ai également appris qu'un outil de débogage comme RenderDoc ou Visual Studios intégré dans le débogueur de rendu est essentiel pour suivre ces types de problèmes.