Programming/DirectX 12

DX12 다중 스레드 렌더링

KingSSSSS 2017. 12. 18. 11:56
voidOnRender_MainThread()
{
    // 각 자식 렌더링 스레드에 알리고 렌더링을 시작합니다.
    forworkerIdinworkerIdList
    {
        SetEvent(BeginRendering_Events[workerId]);
    }

    // Pre 명령 목록은 렌더링을 준비하는 데 사용됩니다.
    // Pre 명령 목록 재설정
    pPreCommandList->Reset(...);

    // 백 버퍼의 표시 상태와 Rendering Target 사이의 장벽을 설정합니다.
    pPreCommandList->ResourceBarrier(1, (..., D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

    // 백 버퍼의 색을 지운다.
    pPreCommandList->ClearRenderTargetView(...);

    // 백 버퍼의 깊이 / 템플릿 지우기
    pPreCommandList->ClearDepthStencilView(...);

    // Pre 명령리스트의 다른 연산들
    // ...

    // Pre 명령 목록 닫기
    pPreCommandList->Close();

    // Post 명령 목록은 렌더링 후에 끝내기 위해 사용됩니다.
    // 백 버퍼의 표시 상태와 Rendering Target 사이의 장벽을 설정합니다.
    pPostCommandList->ResourceBarrier(1, (..., D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

    // Post 명령 목록의 다른 연산들
    // ...

    // Post 명령 목록 닫기
    pPostCommandList->Close();

    // Pre 명령 목록 제출
    pCommandQueue->ExecuteCommandLists(..., pPreCommandList);

    // 모든 작업자 스레드가 완료 될 때까지 대기 Task1
    // Task1 쓰레드가 끝날때까지 모두 대기 한다
    WaitForMultipleObjects(Task1_Events);

    // 모든 작업자 스레드에 대해 Task1의 명령 목록을 제출합니다.
    pCommandQueue->ExecuteCommandLists(..., pCommandListsForTask1);

    // 모든 작업자 스레드가 완료 될 때까지 대기 Task2
    // Task2 쓰레드가 끝날때까지 모두 대기 한다
    WaitForMultipleObjects(Task2_Events);

    // 완료된 렌더링 명령을 제출합니다 (모든 작업자 스레드에 대해 Task2의 명령 목록)
    pCommandQueue->ExecuteCommandLists(..., pCommandListsForTask2);

    // ...
    // 모든 작업자 스레드가 TaskN을 완료 할 때까지 기다립니다.
    WaitForMultipleObjects(TaskN_Events);

    // 완료된 렌더링 명령을 제출합니다 (모든 작업자 스레드에 대해 TaskN의 명령 목록)
    pCommandQueue->ExecuteCommandLists(..., pCommandListsForTaskN);

    // 마지막 명령 목록을 제출합니다 (pPostCommandList).
    pCommandQueue->ExecuteCommandLists(..., pPostCommandList);

    // SwapChain 프리젠 테이션 사용
    pSwapChain->Present(...);
}


voidOnRender_WorkerThread(workerId)
{
    // 각 루프는 자식 스레드의 한 프레임 렌더링을 나타냅니다.
    while (running)
    {
        // 메인 프레임에서 이벤트 알림을 기다리면 한 프레임 렌더링이 시작됩니다.
        WaitForSingleObject(BeginRendering_Events[workerId]);

        // Rendering subtask1
        {
            pCommandList1->SetGraphicsRootSignature(...);
            pCommandList1->IASetVertexBuffers(...);
            pCommandList1->IASetIndexBuffer(...);
            //...
            pCommandList1->DrawIndexedInstanced(...);
            pCommandList1->Close();

            // 현재 작업자 스레드의 렌더링 하위 작업 1이 완료되었음을 주 스레드에 알립니다.
            SetEvent(Task1_Events[workerId]);
        }

        // Rendering subtask2
        {
            pCommandList2->SetGraphicsRootSignature(...);
            pCommandList2->IASetVertexBuffers(...);
            pCommandList2->IASetIndexBuffer(...);
            //...
            pCommandList2->DrawIndexedInstanced(...);
            pCommandList2->Close();

            // 현재 작업자 스레드의 렌더링 하위 작업 2가 완료되었음을 주 스레드에 알립니다.
            SetEvent(Task2_Events[workerId]);
        }

        // 더 많은 렌더링 하위 작업
        //...

        // Rendering subtaskN
        {
            pCommandListN->SetGraphicsRootSignature(...);
            pCommandListN->IASetVertexBuffers(...);
            pCommandListN->IASetIndexBuffer(...);
            //...
            pCommandListN->DrawIndexedInstanced(...);
            pCommandListN->Close();

            // 현재 작업자 스레드의 렌더링 하위 작업 N이 완료되었음을 주 스레드에 알립니다.
            SetEvent(TaskN_Events[workerId]);
        }
    }
}