2017-03-08 4 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를 호출하고 있기 때문에 이것은 비효율적이다. 이것은 지나치게 단순화되었지만 잘하면 내가 어디에서 왔는지 이해할 수 있습니다. 기본적인 이해는 이러한 유형의 불필요한 바인드/호출이 비효율적입니까?

그런 경우 두 개의 개별 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); 
} 

이 사용입니다 다른 픽셀 쉐이더의 다음은 가능한 최적화입니까 아니면 각 그리기 호출이 파이프 라인을 효과적으로 재설정합니까?

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의 도움을 받아 렌더링 된 프레임을 캡처 할 수 있었고 하나만 예상했을 때 두 번의 그리기 호출이 있음을 알았습니다. 디버깅을 위해 카메라 위치를 작성하는 데 사용되는 DIrectXTK를 통해 생성 된 SpriteFont 및 SpriteBatch 클래스가 있음을 잊어 버렸습니다. 이 호출은 실현하지 않고 파이프 라인 클래스 (이 최적화를 제어하는)를 우회하면서 파이프 라인 상태를 수정했습니다. 즉, 그리드가 두 번째 렌더링 될 때 파이프 라인이 잘못된 상태였습니다.

따라서 이러한 최적화가 가능하며 그리기 호출의 결과로 파이프 라인이 지워지지 않습니다. 그래서 위의 예제와 비슷한 것을 가지고 있다면 호출간에 컨텍스트 호출을 충분히 호출 할 수 있습니다. 또한 렌더링 디버거에 내장 된 RenderDoc 또는 Visual Studios 같은 디버깅 도구가 이러한 유형의 문제를 추적하는 데 필수적이라는 사실을 알게되었습니다.