D3DResource(Buffer)를 heap에 맵핑해서 사용하기.

몇 번인가 언급한적 있는데 DX12에선 모든 리소스가 64KB얼라인을 사용한다. 이게 문제가 뭐냐면 4Bytes짜리 Vertex Buffer 한개를 만들어도 내부적으로 64KB짜리 버퍼가 할당된다. 처음부터 Vertex Buffer, Index Buffer를 많이 사용하는게 아니라면 별 문제가 아니지만 지금 진행중인 Voxel Horizon처럼 오브젝트를 2만개 이상 할당하는 경우는 크게 문제가 된다.
실제로 크게 문제가 됐다. DX11렌더러가 이미 잘 작동하고 있으므로 DX12렌더러를 그냥 버릴까도 생각했다. 하지만 그간 짠 코드가 아깝기도 하고, 이 문제를 잘 해결하면 결과적으로 다른 API의 렌더러에도 도움이 될것 같아서 이 문제를 해결하기로 했다.

방법은 간단하다.
D3DResource(Buffer)를 생성할때 64KB단위로 만든다. 그리고 이걸 heap에다 맵핑한다. heap이란 흔히 사용하는 malloc/free, new/delete로 사용하는 동적 메모리 할당 구조를 말한다. heap은 기본적으로 큰 메모리 블럭 안에서 최대한 남김없이 메모리를 할당해준다. 물론 메모리를 해제하면 인접 블럭을 체크해서 해제되어있는 경우 병합해서 더 큰 메모리 블럭으로 만든다.
이러한 heap구조를 만들어서64KB 단위로 할당한 D3DResource를 여기에 맵핑할 것이다.
내가 heap을 만든다고 하면 malloc/free보다 잘 만들순 없을것이다. 하지만 그래도 직접 만들어야 한다. 왜냐하면 malloc을 호출하면 실제로 메모리를 할당-소모 하기 때문이다. 필요한건 단지 어드레스 뿐이다. 시스템 메모리를 마구 낭비해도 좋다면 malloc을 이용해도 된다. 하지만 내가 원하는 것은 그것이 아니다.

작업 순서를 보자.

  1. Heap자료구조를 만든다.
    • Alloc(size N)을 호출하면 메모리를 할당해주는게 아니고 base address를 주면 그에 대한 상대주소를 리턴한다.
    • 다양한 사이즈를 할당할 수 있어야한다.
    • 해제시 인접 블록이 해제된 상태라면 병합할 수 있어야 한다.
    • Heap의 어드레스 범위는 0 – D3DResource의 size * 버퍼의 최대 개수
    • 예를 들어 D3DResource(Buffer)를 64KB씩 할당해서 최대 1024개를 사용한다면 heap의 addres범위는 0 – 65536*1024이다.
  2. D3DResource의 최대개수만큼의 ptr 배열을 만든다. 정확히는 포인터와 ref count를 담을 수 있는 구조체 배열이다.
  3. Vertex Buffer or Index Buffer를 할당할때 위에서 만든 Heap->Alloc() 호출
  4. 이렇게 얻은 address – base addr(일반적으로 0)로 상대 address를 얻는다. 상대 address로 맵핑될 D3DResource의 index를 결정한다.
  5. 선택된 D3DResource의 ptr배열이 null인지 체크.  Null이면 D3D API를 사용해서 D3DResource(Buffer)를 만든다.
  6. Null이 아닌 경우 D3DResource의 ref count증가.
  7. VB_DESC에 D3DResource의 ptr을 설정.
  8. 상대 address를 이용해서 VB_DESC에 StartIndex도 설정.
  9. VB_DESC를 리턴.
  10. Draw…()호출할때 VB_DESC의 D3DResource의 ptr과 StartIndex를 파라미터로 넣어준다.
  11. 해제하는 경우 heap으로부터 할당할때와 마찬가지로 D3DResource ptr배열에서 해당하는 D3DResource Index를 찾은 후 ref count를 감소시키고 0이 되면 해당 D3DResource를 Release()한다.

그림으로 보면 다음과 같다.
d3dresource-heap

이렇게 해서 IndexBuffer와 Vertex버퍼로 잡아먹던 2GB이상의 GPU메모리를 300MB이하로 줄였다.
또한 D3DResource생성(CreateCommitedResource)과 해제(Release)에 걸리는 시간이 꽤 길었는데 이 또한 대폭 줄었다.
이 기법은 D3D12 외에 D3D9/11에서도 사용 가능하다. DX11에선 굳이 사용할 필요가 없지만 D3DResource 생성/해제에 걸리는 시간은 줄일 수 있다.


답글 남기기

댓글을 게시하려면 다음의 방법 중 하나를 사용하여 로그인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중