Voxel Horizon데모를 D3D12엔진으로 돌아가게 만들었다.
데모에서 바닥과 블럭 렌더링은 Dynamic VertexBuffer를 사용한다. D3D11에선 Map(), Unmap()을 사용해서 하나의 VertexBuffer에 Update하고 Draw..(), 또 Update하고 Draw..()하면 되었다. D3D11이라고 해서 Draw..()한다고 즉시 렌더링이 이루어지진 않는데 Resource Renaming이란 방법으로 해결하고 있다.
D3D12에선 이런거 없다. 게다가 CommandList를 이욯하기 때문에 Execute()하는 시점까진 Draw…()에 사용될 VertexBuffer의 내용이 그대로 유지되어야한다.
우선 Vertex Buffer는 다음과 같이 만든다. Dynamic이기 때문에, 즉 CPU에서 write해야하기 때문에 D3D12_HEAP_PROPERTIES로 CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD)를 사용한다.
HRESULT hr = S_OK; D3D12_VERTEX_BUFFER_VIEW VertexBufferView = {}; ID3D12Resource* pVertexBuffer = NULL; UINT VertexBufferSize = SizePerVertex*dwVertexNum; // create vertexbuffer for Dynamic Accessing hr = m_pD3DDevice->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(VertexBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pVertexBuffer)); // Initialize the vertex buffer view. VertexBufferView.BufferLocation = pVertexBuffer->GetGPUVirtualAddress(); VertexBufferView.StrideInBytes = SizePerVertex; VertexBufferView.SizeInBytes = VertexBufferSize;
그리고 다음과 같이 Write흘 시스템 메모리 어드레스를 확보한다. Unmap()은 호출할 필요없다. 이 어드레스에 써넣을 것이고 Draw..()시점..아니 정확히는 Execute()를 통해서 실제로 Draw..()가 실행될 시점에서 D3D런타임이 시스템메모리로부터 GPU메모리로 버텍스 데이타를 카피한다.
// CPU address CD3DX12_RANGE readRange(0, 0); UINT8* pVetexDataBegin = NULL; pVertexBuffer->Map(0,&readRange,(void**)&pVetexDataBegin); // Write Vertex Data to pVertexDataBegin dynamically
렌더링할때는 일반적인 VertexBuffer를 사용하듯 똑같이 사용한다.
// Draw D3D12_VERTEX_BUFFER_VIEW VBView[] = {VertexBufferView}; pCommandList->IASetVertexBuffers(0, _countof(VBView), VBView); pCommandList->DrawInstanced(VertexCount,1,VertexStartIndex,0);
다시 한번 강조하지만 프레임 갱신 전에, 즉 Execute()가 호출되기 전에는 VertexBuffer를 재활용할 수 없다. 간단한 메모리 풀 구조를 만들어서 Execute()시점까지 write한 VertexBuffer를 모두 유지해야한다.