메모리 관리 전략
Direct3D 12용 메모리 관리자는 UMA 또는 개별(비UMA) 어댑터에 대한 다양한 지원 계층이 포함되고 GPU 어댑터 간의 상당한 아키텍처 차이로 인해 매우 복잡해질 수 있습니다.
이 섹션에 설명된 Direct3D 12 메모리 관리에 대한 권장 전략은 “분류, 예산 및 스트림”입니다.
리소스 종류
"커밋된 리소스"(관리되는 실제 메모리에서 초기화된 가상 주소 공간과 물리적 주소 공간을 둘 다 만드는)라는 기본 개념은 Direct3D 9 이후부터 있어왔지만, Direct3D 12에서는 앱이 실제 메모리를 신중하게 관리하기 위해 VA(가상 주소 지정)와 물리적 주소 지정을 분리할 수 있습니다.
커밋된 리소스 외에, Direct3D 12의 힙 구문을 통해 "배치된 리소스"와 "예약된 리소스"라는 두 가지 다른 유형의 리소스를 사용할 수 있습니다. Direct3D 11에서 "예약된" 리소스는 "타일식 리소스"라고도 알려져 있습니다.
예약된 리소스에는 고유한 자체 GPU 가상 주소 공간이 있다는 점에서 배치된 리소스와 다릅니다. 이렇게 하면 VA 공간을 미리 많이 할당했다가 나중에 VA 페이지를 힙의 특정 섹션에 매핑하여 애플리케이션이 배치를 즉시 재구성할 수 있습니다. VA 공간 인접해 있고 부분적으로 매핑될 수 있습니다.
예약된 리소스는 UpdateTileMappings와 같은 API 호출로 힙의 참조 영역을 만들 수 있고 즉시 페이지 테이블을 업데이트하여 앱에 상주할 수 있습니다. VA 범위가 NULL 또는 비상주 힙으로 매핑되면 리소스의 해당 부분이 상주하지 않는 것으로 간주됩니다. VA 범위가 상주 힙에 매핑되면 리소스의 해당 부분이 상주하는 것으로 간주됩니다. 힙은 만들면 상주합니다.
배치된 리소스는 매우 간단하며 단순히 힙의 특정 영역을 가리키는 포인터입니다(예: 5MB 힙의 텍스처에 대한 1MB 영역). 앨리어싱 장벽을 사용하면 중첩 배치된 리소스를 사용할 수 있습니다(CreatePlacedResource 및 ResourceBarrier 참조).
일부 Direct3D 12 하드웨어에서는 예약된 리소스를 사용할 수 없으며, 배치되는 리소스는 적절한 대체 리소스입니다. 단, 배치된 리소스는 인접해야 하고 부분적으로 상주할 수 없습니다.
메모리 예산
Direct3D 12에서 힙을 할당하면 커밋된 리소스의 실제 메모리 측면이 생성됩니다. 보다 명시적인 메모리 세그먼트 선택은 Direct3D 12에서 가능합니다(비디오와 시스템 메모리 중에서 선택). UMA 어댑터에는 단일 메모리 세그먼트 즉, 시스템 메모리만 있습니다.
GPU는 페이지 폴트를 지원하지 않습니다. 따라서 개발자는 과도하게 커밋하지 않도록, 특히 시스템 메모리가 1Gb뿐인 경우에는 주의해야 합니다. 앱에서 과도한 커밋을 수행하면 OS는 실제 메모리 수요에 따라 성긴 프로세스 스케줄링을 사용합니다. 스케줄러는 포그라운드 프로세스를 동결하고 그 중 일부를 반드시 페이지 아웃(page-out)합니다. 그래야 실행하려는 백그라운드 프로세스를 페이지 인(page-in)할 수 있습니다. 사용 가능한 실제 메모리는 사용자가 백그라운드에서 수행하는 작업(예: 브라우저 실행 또는 비디오 시청)에 따라 상당히 달라질 수 있습니다.
메모리 예산용 API는 QueryVideoMemoryInfo입니다. 개별 어댑터의 경우 "로컬"은 비디오 메모리이고 "비로컬"은 시스템 메모리입니다. UMA 어댑터의 경우 비로컬은 항상 0입니다. 디자인 관련 한 가지 질문은 엔진이 두 가지 예산을 모두 관리할지 아니면 로컬 예산만 관리할지 여부입니다. 로컬 예산만 관리하면 간단하지만 몇 가지 주의 사항이 있습니다. 예를 들어, 최대 로컬 예산이 1Gb라고 가정하면, 모든 힙이 UMA 시스템의 1Gb에서 발생하고, 시스템 메모리에 오버플로가 발생하지 않습니다(분명히 없습니다).
Direct3D11에서 애플리케이션용 메모리를 관리하기 때문에 사용되지 않는 리소스는 반드시 페이지 아웃됩니다.
가장 적합한 리소스 크기를 선택해야 합니다. 리소스의 크기가 애플리케이션이 실제 실행되는 상황에 적합한지 고려해야 합니다. 창에서 또는 800x600의 화면 해상도로 애플리케이션을 실행하는 사용자도 있을 수 있습니다.
분류 전략
메모리가 제한된 시나리오에서 리소스를 효율적으로 관리하려면 리소스를 다음과 같이 분류하는 것이 좋습니다.
분류 | 예제 | 개체 및 API 기능 | 관리 참고 사항 |
---|---|---|---|
위험 | 게임 UI | 명령 할당자, 명령 큐, 쿼리 힙, 리소스 및 리소스 힙. | 이러한 요소는 페이징할 수 없거나 항상 커밋된 메모리에 있어야 합니다. |
크기 조정/선택적 | 수준별 모델 및 텍스처, 스왑 체인, 스카이 박스, 1인칭 플레이어 캐릭터 모델 | 리소스 및 힙. 커밋된 리소스뿐만 아니라 배치 및 예약된 리소스도 마찬가지로 잘 작동할 수 있습니다. | 메모리 상주 예산을 렌더링 알고리즘으로 통합합니다. 사용 가능한 세부 정보의 적절한 수준을 선택하고 프레임당 1회 미만으로 재평가합니다. 이 기법에는 크기가 가변적인 리소스와 스왑 체인 크기 조정이 포함됩니다. |
다시 사용되는 리소스 | 섀도 버퍼, 지연 렌더링 리소스, 사후 처리 리소스, 라이팅 데이터 캐시 | 리소스 및 힙. 힙에 중첩 배치된 리소스 및 앨리어싱 장벽. | 프레임 내 힙 영역 또는 큰 리소스를 다시 사용하여 전체 프레임에 대한 요구 사항을 줄입니다. 프레임 내 메모리 재사용 기법을 사용합니다. Direct3D 11에서는 동일한 유형의 리소스와 크기가 충분한 리소스만 애플리케이션에서 다시 사용할 수 있었습니다. Direct3D 12 힙은 중첩 리소스를 허용하기 때문에 훨씬 간단하고 재사용이 많습니다. |
스트리밍 리소스 | 지형, 개방형 텍스처 및 기하 도형 | 리소스 및 힙. 자유 스레드 생성, 백그라운드 CPU 스레드 및 백그라운드 복사 명령 큐 및 목록. | 부분 상주, 일반적으로 가시성(시각 절두체 또는 거리 기반 평가 사용)에 기반하며 상주 요건을 재평가할 때 모든 프레임이 필요합니다. GPU 어댑터가 힙 내에 예약된 리소스를 지원하는 경우, 프레임 간 재사용 및 타일 단위 부분 상주 관리를 사용하는 기법을 사용할 수 있습니다. 프레임 간 메모리 재사용을 사용하는 기법을 사용하면, 부분적인 하위 리소스 상주를 구현할 수 있지만 적합성이 떨어집니다. 힙을 사용하여 배치된 리소스는 신속하게 재활용할 수 있어야 하지만 커밋된 리소스를 대체 리소스로 사용할 수 있습니다. |
대부분의 작업에서 스트리밍 리소스로 향하는 애플리케이션이 많을수록 배치된 리소스와 예약된 리소스를 더 많이 활용하게 됩니다. 따라서, 4가지 분류 간의 메모리 재사용을 극대화할 수 있습니다. 애플리케이션 스트리밍이 많을수록 대역폭에 대한 예산과 우선 순위 지정이 많아집니다.
일반적으로 Direct3D 12 그래픽 엔진은 보다 다양하고 동적인 예산을 적용해야 하며 이전보다 훨씬 엄격하게 적용해야 합니다. 최적의 애플리케이션은 프로세스에 부여된 예산에 4가지 범주 모두를 배치하고, 백그라운드 모바일 앱에서 전체 화면 개별 예산까지 게임 플레이를 확장합니다. 하지만 시작할 때 중요 범주 유형의 리소스가 너무 많아서 어려움을 겪는 애플리케이션이 많이 있습니다. Direct3D 11을 사용하면 성능에 영향을 미치지 않고, 리소스가 익명으로 생성되고 중요 상태를 차지할 수 있습니다. 하지만, Direct3D 12의 경우, 개발자가 엔진과 미들웨어 전체에서 무작위로 생성된 리소스를 열심히 찾아서 다른 범주 중 하나에 할당해야 합니다.
다른 문제 영역은 미들웨어 구성 요소, 사용자 컨트롤 및 프레임 간 스트리밍입니다. 미들웨어 구성 요소는 예산에 노출되지 않을 수도 있지만, 긴밀히 협력해야 할 필요도 없습니다. 미들웨어 구성 요소는 기능을 렌더링 기법으로 노출할 수 있으며 애플리케이션은 미들웨어 및 엔진 설정 노출에 의존할 수 있습니다. 개발자는 Direct3D 11에 의존하여 페이징을 수행하고 올바른 프레임 속도를 구현할 수 있습니다. 경우에 따라 Direct3D 11 애플리케이션이 모든 프레임 안팎에서 리소스 콘텐츠를 페이징하여 사용자가 수용할 수 있는 프레임 속도가 될 수도 있습니다. 대부분의 엔진은 백그라운드 작업으로만 리소스 데이터를 스트리밍합니다. 이 경우, 우선 순위가 높은 프레임 간 스트리밍에 대해 정상적인 대체가 없습니다. 엔진에 이것을 구현하도록 요구하면, Direct3D 12로 이동하면서 모색하는 CPU 오버헤드 게인 중 일부가 손실됩니다. 엔진 개발자는 프레임을 단계별로 나눠서 재사용 가능 리소스에 더 많은 기회를 제공할 수 있으며, 프레임 간 메모리 재사용을 위해 배치된 리소스와 힙을 지원하도록 미들웨어 공급업체와 협력할 수 있습니다.