동적 정점 버퍼

WanJu Kim·2022년 12월 29일

Direct3D

목록 보기
21/29

지금까지의 버퍼들은 정점 버퍼들을 사용했는데, 정적 버퍼의 내용은 초기화 시점에서 고정된다. 반면에 동적 버퍼의 내용은 실행 시점에서 얼마든지 변할 수 있다. 그리고, 매 프레임마다 변하는 경우가 많다. 예를 들어 파도를 구현한다고 하자. 평범한 언덕과는 달리 파도는 시시각각 높이가 변한다. 또한 복잡한 물리 계산과 충돌 검출을 수반하는 입자 시스템에도 동적 정점 버퍼가 필요하다. 매 프레임마다 CPU에서 물리 계산과 추돌 검출을 수행해서 각 입자의 새 위치를 구해야 하기 때문이다. 이처럼 시간의 흐름에 따라 갱신되는 메시를 표현하기 위해서는 동적 정점 버퍼가 필요하다.

버퍼를 동적으로 만들기 위해서라면 버퍼의 용도를 반드시 D3D11_USAGE_DYNAMIC으로 지정해야 한다. 또 CPU에서 버퍼에 자료를 기록해야 하므로 CPU 접근 플래그를 반드시 D3D11_CPU_ACCESS_WRITE로 지정해야 한다.

D3D11_BUFFER_DESC vbd;
vbd. Usage = D3D11_USAGE_DYNAMIC ;
vbd.ByteWidth = sizeof(Vertex) * mWaves.VertexCount();
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vbd.MiscFlags = 0;
HR(md3dDevice->CreateBuffer(&vbd, 0, &mWavesVB));

동적 정점 버퍼를 갱신할 때에는 ID3D11DeviceContext::Map 함수로 버퍼의 메모리 블록의 시작을 가리키는 포인터를 얻어서 그 메모리 블록에 자료를 기록하면 된다.

HRESULT ID3DllDeviceContext::Map(
	ID3D11Resource *pResource,
	UINT Subresource,
	D3D11_MAP MapType,
	UINT MapFlags,
	D3D11_MAPPED_SUBRES0URCE *pMappedResource
);
  1. pResource : 읽기, 쓰기 접근을 원하는 자원을 가리키는 포인터.
  2. Subresource : 그 자원에 담긴 부분자원(subresource)의 색인. 지금 나올 예시는 0을 지정.
  3. MapType : 다음 플래그 값들 중 하나이다.
    (a) D3D11_MAP_WRITE_DISCARD : 하드웨어에게 현재 버퍼를 폐기하고 새 버퍼를 할당해서 그 포인터를 돌려달라고 지시한다. 이렇게 하면 새로 할당된 버퍼에 자료를 기록하는 동안 하드웨어가 폐기된 버퍼에 렌더링 작업을 계속 진행할 수 있으므로 하드웨어의 실행이 일시적으로 멈추는 일이 방지된다.
    (b) D3D11_MAP_WRITE_NO_OVERWRITE : 하드웨어에게 응용 프로그램이 버퍼의 초기화되지 않은 부분에만 자료를 기록할 것이라고 알려준다. 이 경우에도, 버퍼의 미초기화 부분에 자료를 기록하는 동안 하드웨어가 이전에 기록된 기하구조의 렌더링을 계속 진행할 수 있으므로 하드웨어의 실행이 멈추는 일이 방지된다.
    (c) D3D11_MAP_READ : GPU 버퍼의 복사본을 시스템 메모리로 읽어 들여야 하는 예비(staging) 버퍼에 쓰이는 값이다.
  4. MapFlags : 잘 사용하지 않는 값. 0을 넣는다.
  5. pMappedResource : D3D11_MAPPED_SUBRESOURCE 구조체를 가리키는 포인터를 돌려준다. 이를 통해서 버퍼에 자료를 기록하거나 읽으면 된다.

D3D11_MAPPED_SUBRESOURCE 구조체의 정의는 다음과 같다.

struct D3D11_MAPPED_SUBRESOURCE {
	void *pData;
    UINT RowPitch;
    UINT DepthPitch;
}
  1. pData : 읽고 쓸 수 있는 자원의 생(raw) 메모리 블록을 가리키는 포인터. 이를 자원에 담긴 자료에 맞는 형식으로 캐스팅할 필요가 있다.
  2. RowPitch : 자원의 자료 한 줄의 바이트 단위 크기. 예를 들어 2차원 텍스처의 경우 이것은 텍스처 한 줄의 바이트 단위 크기이다.
  3. DepthPitch : 자원의 자료 한 페이지의 바이트 단위 크기. 예를 들어 3차원 벡터의 경우 이것은 3차원 텍스처의 2차원 이미지 부분집합 하나의 바이트 단위 크기이다.

다음은 정점 버퍼를 갱신하는 코드의 예시이다.

D3D11_MAPPED_SUBRESOURCE mappedData;
HR(md3dImmediateContext->Map(mWavesVB, 0,
	D3D11_MAP_WRITE_DISCARD, 0, &mappedData));
Vertex* v = reinterpret_cast<Vertex*>(mappedData.pData); 
for(UINT i = 0; i < mWaves.VertexCount(); ++i)
{
	v[i].Pos = mWaves[i];
	v[i].Color = XMFLOAT4(O.Of, O.Of, O.Of, l.Of);
}
md3dImmediateContext->Unmap(mWavesVB, 0);

버퍼를 다 갱신한 후에는 반드시 ID3D11DeviceContext::Unmap 메서드를 호출해야 한다. 동적 버퍼를 사용하면 새 자료를 CPU 메모리에서 GPU 메모리로 전송해야 하므로 약간의 추가 부담이 생긴다. 따라서 정적 버퍼로도 가능한 일이라면 동적 버퍼보다는 정적 버퍼를 사용하는 것이 바람직하다.

profile
Question, Think, Select

0개의 댓글