D3D12 예제를 보면 Constant Buffer를 system memory에 만들어 사용하고 있다. (CPU write, GPU read).
D3D11에선 Constant Buffer를 GPU메모리에 두느냐, 시스템 메모리에 두느냐에 따라 성능차이가 꽤 난다. 20-30%정도는 차이가 나는것 같다.
D3D12에서 테스트해보면 대략 10%정도 성능 차이가 난다. 당연히 GPU메모리에 두는 쪽이 빠르다. 크다면 크고 작다면 작은 수치다. 그럼 왜 D3D12예제에선 시스템 메모리에 Constant Buffer를 잡았을까.
D3D12에선 시스템메모리에 Constant Buffer를 잡으면(Dynamic Buffer로) Map()해놓은 후 Unmap()할 필요가 없다. 즉 한번 써넣을 어드레스를 받아놓고 그 위치에 계속 써넣으면 별도의 API호출없이 렌더링시에 그대로 반영된다. 후술할 이유 때문에 이것은 의도된 API 설계로 보인다.
반면에 Constant Buffer를 GPU메모리에 두면 API를 통해서 시스템 메모리-> GPU 메모리로 업데이트를 해야한다. 업데이트를 하려면 Command List에 기록을 하고 Command Queue를 통해서 Execute()를 호출해야한다. Command Queue를 여러개 동시 사용하는 경우 Fence를 걸고 작업이 끝날때까지 wait해야할 수 있다.
그리고 D3D12에선 copy를 하거나 draw를 하거나 상황에 따라서 리소스의 상태를 수동으로 바꿔줘야한다.
따라서 Constant Buffer로 사용할 GPU메모리에 뭔가를 써넣으려면 다음과 같은 코드가 된다.
1) 한번에 갱신할 경우
pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pResourceGPU, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, D3D12_RESOURCE_STATE_COPY_DEST)); pCommandList->CopyResource(m_pResourceGPU, m_pResourceCPU); pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pResourceGPU, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER));
불필요하게 큰 용량을 PCI BUS를 통해서 전송해야한다. 이 메모리 영역의 극히 일부만 갱신이 된다해도 PCI BUS를 통해서 전송되는 양은 버퍼 전체 크기이다.
2) 부분적으로 갱신할 경우
for (DWORD i = 0; i ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pResourceGPU, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, D3D12_RESOURCE_STATE_COPY_DEST)); pCommandList->CopyBufferRegion(m_pResourceGPU, DestOffset, m_pResourceCPU, SrcOffset, size); pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pResourceGPU, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER)); }
ResourceBarrier()를 호출해서 리소스의 상태를 수시로 바꿔야한다. 상당한 오버헤드가 걸린다.
절충안으로 후자의 방법을 사용하되 ConstantBuffer로 사용할 GPU메모리를 일정 사이즈 단위로 잘라서 관리하면 오버헤드를 줄일 수 있다. 안그래도 번거로운데 더 번거로워 진다.
[결론]
그러니까 D3D12샘플에서 Constant Buffer를 GPU메모리에 둔 이유는 다음과 같이 추측할 수 있다.
하려면 할 수는 있지만…이런 엄청 비대해진 코드를 만들지 않기 위해 그냥 쉽게 system memory에 Constant Buffer를 잡은걸로 보인다.
지금 두가지 버전을 다 만들어놨지만 결국 system memory에 잡아두는걸로 되돌릴 생각이다. 번거로워서 안되겠다.