了解DirectX管道优化

Understanding DirectX pipeline optimisations

本文关键字:优化 管道 DirectX 了解      更新时间:2023-10-16

我正在尝试改进我实施的一些简单的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。这是一个过度简化的,但希望您能得到我的来源,我的基本理解是这些类型的不必要的绑定/呼叫效率低下?

如果是这种情况,那么可以在两个单独的ID3D11deviceContext :: draw呼叫之间进行以下优化吗?(再次,伪码,请原谅丢失的步骤,并假设我们要做的就是在我们绘制之前设置一个顶点& amp; amp; amp; amp; amp;

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

两个抽奖调用之间的唯一区别是使用其他像素着色器。因此,以下可能的优化是否有效地重置管道?

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

任何反馈/信息都将不胜感激。

只是在我自己的问题上发布答案,因为我在测试代码中发现了一个错误,该错误蒙上了问题,希望这会帮助看到其他任何人。

我的困惑是因为在我的测试代码中,我只有一个我渲染的一个对象,一个简单的平面。它使用的唯一资源是顶点缓冲区,顶点着色器和像素着色器。我尝试添加上面提到的优化,以尝试尽可能减少ID3D11D11DeviceContext调用的数量。对于这个简单的对象,对我来说似乎很明智,即ID3D11D11deviceContext呼叫,例如VSSETSHADER,PSSETSHADER等只需要一次调用一次,因为这是唯一呈现的对象。但是,事实并非如此,因为一旦网格消失了,并且再也不会渲染。

在Renderdoc的帮助下,我能够捕获一个渲染的框架,并注意到当我只期待一个时,有两个打电话。我忘记了我的SpriteFont和SpriteBatch类是通过Directxtk创建的,用于写出我的相机位置进行调试。这个呼叫正在修改管道状态,同时绕过我的管道类(控制这些优化)而没有我意识到。这意味着当第二次渲染网格时,管道处于不正确的状态。

因此,事实证明这些优化是可能的,并且由于拉动呼叫的结果未清除管道。因此,如果您有上述示例类似的内容,那么它足以在呼叫之间一次调用上下文呼叫。我还了解到,诸如Renderdoc或Visual Studios内置的调试工具渲染调试器对于跟踪这些类型的问题至关重要。