4.1 기본지식
4.1.1 Direct3D 12의 개요
Direct3D는 응용 프로그램에서 GPU를 제어하고 프로그래밍하는 데 쓰이는 저수준 그래픽 API이다. 이를 통해 3차원 세계를 렌더링 할 수 있게 된다. Direct3D라는 간접층과 하드웨어 드라이버가 Direct3D 명령들을 시스템의 GPU가 직접 이해하는 고유한 기계어 명령들로 번역해 주므로, 개발자는 GPU의 세부사항까지 걱정할 필요가 없다.
Direct3D 12가 이전 버전들에 비한 주된 개선점은 CPU 부담을 크게 줄이고 다중 스레드 지원을 개선하기 위해 설계를 다시 했다는 점이다. 이를 위해 훨씬 낮은 수준의 API가 되었다. 이전보다 추상화가 줄었고, 개발자가 손수 관리해야 할 사항들이 늘어났으며, 현세대 GPU 구조들을 좀 더 밀접하게 반영한다.
4.1.2 COM
COM(Component Ojbect Model)은 DirectX의 프로그래밍 언어 독립성과 하위 호환성을 가능하게 하는 기술이다. COM의 세부사항들은 프로그래머에게 드러나지 않는다. 알아야 할 것은 필요한 COM 인터페이스를 가리키는 포인터를 특별한 함수들을 이용해서, 또는 다른 COM 인터페이스의 메서드를 이용해서 얻는 방법뿐이다. COM 인터페이스를 다 사용하고 난 후에는 그 인터페이스의 Release 메서드를 호출해 주어야 한다. COM 객체는 참조 횟수가 0이 되면 메모리에서 해제된다.
COM 객체의 수명 관리를 돕기 위해, Microsoft::WRL::ComPtr이라는 클래스(스마트 포인터처럼)를 제공한다. 범위를 벗어난 ComPtr 인스턴스는 바탕 COM 객체에 대해 자동으로 Release를 호출한다. 우리가 직접 Release를 호출할 필요가 없다.
4.1.3 텍스처 형식
2차원 텍스처는 자료 원소들의 행렬(2차원 배열)이다. 2차원 텍스처의 용도 하나는 2차원 이미지 자료를 저장하는 것인데, 이때 텍스처의 각 원소는 픽셀 하나의 색상을 담는다. 이것이 텍스처의 유일한 용도는 아니고 훨씬 범용적이다. 3차원 벡터를 담거나 1차원 배열을 다는 경우도 있다. 텍스처가 단순한 자료 배열인 것만은 아니다. 밉맵 수준들이 존재할 수 있으며, GPU는 필터링, 다중 표본화 등의 특별한 연산을 텍스처에 적용할 수 있다. 더 나아가서, 텍스처에 아무 자료나 담을 수 있는 것은 아니라는 점도 중요하다. 텍스처에는 특정 형식의 자료 원소들만 담을 수 있는데, 구체적인 형식은 DXGI_FORMAT이라는 열거형으로 지정한다.
4.1.4 교환 사슬과 페이지 전환
애니메이션이 껌벅이는 현상을 피하려면 다음과 같은 방법을 사용하는 것이 최선이다.
- 애니메이션의 한 프레임 전체를 화면 바깥의(off-screen) 텍스처에 그린다. 그런 텍스처를 후면 버퍼라고 부른다.
- 그 후면 버퍼를 하나의 완전한 프레임으로서 화면에 표시한다. 표시하는 텍스처는 전면 버퍼이다.
- 전면 버퍼가 화면에 표시된 동안 애니메이션의 다음 프레임을 후면 버퍼에 그리고, 다 그려지면 전면 버퍼와 후면 버퍼의 역할을 맞바꾼다.
즉, 후면 버퍼가 새 전면 버퍼가 되어서 그 내용이 화면에 표시되고, 전면 버퍼가 새 후면 버퍼가 되어서 다음 프레임이 그려진다. 이러한 기법을 이중 버퍼링이라고 부른다. 페이지가 전환되는데 필요한 연산은 버퍼간의 포인터만 맞바꾸면 되기에 아주 효율적이다.
전면 버퍼와 후면 버퍼는 하나의 교환 사슬(swap chain)을 형성한다. 대표적인 인터페이스는 IDXGISwapChain이다.
4.1.5 깊이 버퍼링
깊이 버퍼는 이미지 자료를 담지 않는 텍스처의 한 예이다. 깊이 버퍼는 각 픽셀의 깊이 정보를 담는다. 깊이는 0.0에서 1.0까지의 값으로 작을수록 관찰자에 가깝다. 깊이 버퍼의 원소들과 후면 버퍼의 픽셀들은 일대일로 대응된다. 후면 버퍼의 해상도가 1280 X 1024라면 깊이 버퍼도 1280 X 1024개의 원소들로 구성된다.
한 물체의 픽셀들이 다른 물체보다 앞에 있는지 판정하기 위해 깊이 버퍼링 또는 z-버퍼링이라는 기법을 사용한다. 렌더링을 수행하기 전에 먼저 후면 버퍼를 기본 색상으로 지운다. 이때 깊이 버퍼도 기본값 1.0으로 지워진다. 렌더링하면서 픽셀과 해당 깊이 값이 깊이 버퍼에 이미 들어 있던 값보다 작은 경우에만 후면 버퍼와 깊이 버퍼에 기록된다. 결과적으로 가장 가까운 픽셀이 승자가 되어서 후면 버퍼에 기록된다.
깊이 버퍼는 하나의 텍스처이다.
4.1.6 자원과 서술자
GPU는 자원들에 자료를 기록하거나 자원들에서 자료를 읽어 들인다. 그리기 명령을 제출하기 전에, 먼저 해당 그리기 호출이 참조할 자원들을 렌더링 파이프라인에 묶어야 한다. GPU자원들이 직접 묶이는 것은 아니다. 실제로 묶이는 것은 자원을 참조하는 서술자 객체이다. GPU는 자원 서술자를 통해서 실제 자원에 접근한다.
서술자를 두는 요인은 GPU 자원이라는 것은 사실 범용적인 메모리 조각이기 때문이다. 자원은 범용적이므로, 같은 자원을 렌더링 파이프라인의 서로 다른 단계들에서 사용할 수 있다. 또한 일부 영역만 묶고 싶을 때도 사용된다.
서술자는 자원의 사용법에 따라 여러 죵류가 있다.
서술자 힙은 서술자들의 배열이다. 응용 프로그램이 사용하는 서술자들이 저장되는 곳이 바로 서술자 힙이다. 서술자 종류마다 개별적인 서술자 힙이 필요하다. 같은 종류의 서술자들은 같은 서술자 힙에 저장된다. 또한, 한 종류의 서술자에 대해 여러 개의 힙을 둘 수도 있다.
서술자들은 응용 프로그램의 초기화 시점에서 생성해야 한다.
4.1.7 다중표본화의 이론
계단 현상을 해결하는 방법으로 초과표본화와 다중표본화가 있다. 초과표본화는 후면 버퍼와 깊이 버퍼를 화면 해상도보다 4배 크게 잡고, 3차원 장면을 4배 크기의 해상도에 서 후면 버퍼에 렌더링하고 이미지를 화면에 제시할 때가 되면 후면 버퍼를 원래 크기의 버퍼로 환원하여, 4픽셀 블록의 네 색상의 평균을 최종 색상으로 선택하는 방법이다. 비용이 높다.
Direct3D는 다중표본화라는 절충적인 앨리어싱 제거 기법을 지원한다. 일부 계산 결과를 부분픽셀들 사이에서 공유하기 때문에 초과표본화보다 비용이 낮다. 마찬가지로 해상도의 4배인 후면 버퍼와 깊이 버퍼를 사용한다. 그러나 부분 픽셀마다 계산하는 것이 아니라 픽셀당 한 번만 계산하고, 그 색상과 부분픽셀들의 가시성과 포괄도를 이용해서 최종 색상을 결정한다.
4.1.8 Direct3D의 다중표본화
다중표본화를 위해서는 DXGI_SAMPLE_DESC라는 구조체를 적절히 채워야 한다.
typedef struct DXGI_SAMPLE_DESC
{
UINT Count;
UINT Quality;
} DXGI_SAMPLE_DESC;
Count 멤버는 픽셀당 추출할 표본의 개수를 지정하고, Quality 멤버는 원하는 품질 수준을 지정한다. 표본 개수가 많을 수록, 그리고 품질 수준이 높을수록 렌더링 비용도 증가하므로, 절충선을 잘 잡아야 한다. 품질 수준들의 범위는 텍스처 형식과 픽셀당 표본 개수에 의존한다. 다중표본화를 사용하고 싶지 않을 때는 Count를 1로 하고 Quality를 0으로 설정한다.
4.1.9 기능 수준
기능 수준은 D3D_FEATURE_LEVEL이라는 enum으로 대표된다.
enum D3D_FEATURE_LEVEL
{
D3D_FEATURE_LEVEL_9_1 = 0x9100,
D3D_FEATURE_LEVEL_9_2 = 0x9200,
D3D_FEATURE_LEVEL_9_3 = 0x9300,
D3D_FEATURE_LEVEL_10_0 = 0xa000,
D3D_FEATURE_LEVEL_10_1 = 0xa100,
D3D_FEATURE_LEVEL_11_0 = 0xb000,
D3D_FEATURE_LEVEL_11_1 = 0xb100
}D3D_FEATURE_LEVEL;
기능 수준들은 GPU가 지원하는 기능들의 엄격한 집합을 정의한다. 현재 GPU의 기능 수준을 파악하기만 하면, 구체적으로 어떤 기능을 사용할 수 있는지를 확실히 알 수 있다.
4.1.10 DXGI(DirectX Graphics Infrastructure)
공통된 그래픽 작업과 하드웨어가 어떤 그래픽 시스템을 제공하는지 알려준다. DXGI에 깔린 기본 착안은, 여러 그래픽 API들에 공통된 그래픽 관련 작업들이 존재한다는 것이다. 그 밖에도 여러 가지 공통적인 그래픽 기능성을 처리한다. 전체 화면 모드 전환, 디스플레이 어댑터나 모니터, 지원되는 디스플레이 모드 같은 그래픽 시스템 정보의 열거 등의 기능은 DXGI가 제공한다.
- IDXGIFactory: 디스플레이 어댑터 열거에 사용
- IDXGIOutput: 디스플레이 출력 목록 연관
- DXGI_MODE_DESC: 디스플레이 모드, 전체 화면 모드로 갈때 특히 중요하다. 전체 화면의 성능을 극대화하려면, 지정된 디스플레이 모드가 반드시 모니터가 지원하는 한 디스플레이 모드와 정확히 일치해야 한다.
4.1.11 기능 지원 점검
ID3D12Device::CheckFeatureSupport메서드의 서명은 다음과 같다.
HRESULT ID3D12Device::CheckFeatureSupport(
D3D12_FEATURE Feature,
void *pFeatureSupportData,
UINT FeatureSupportDataSize);
- Feature: 이 메서드로 지원 여부를 점검할 기능들의 종류를 나타낸다.
- pFeatureSupportData: 기능 지원 정보가 설정된 구조체를 가리키는 포인터
- FeatureSupportDataSize: 매개변수로 전달한 구조체의 크기
4.1.12 상주성
많은 양의 자원들 중 항상 GPU에 필요한 것은 많지 않다. 숲에 있는 플레이어가 동굴에 쓰이는 자원을 필요하지 않다.
Direct3D 12에서 응용 프로그램은 자원을 GPU 메모리로부터 내림으로써('퇴거'), 그리고 필요하면 다시 GPU에 올림으로써('입주') 자원의 상주성(resiency)를 관리한다. 간단히 말해서 자원이 GPU 메모리에 들어 있는지 여부를 뜻한다. 상주성 관리의 핵심은 사용하는 GPU 메모리의 양을 최소화하는 것이다. 성능의 측면에서 주의해야 할 점은, 같은 자원을 짧은 시간에 GPU 메모리에 넣었다 뺐다 하는 상황을 피해야 한다. 비용이 따르기 때문이다. 한동안 사용하지 않을 자원들만 GPU 메모리에서 내리는게 이상적이다.
기본적으로, 자원을 생성하면 자원이 GPU 메모리에 입주하며, 파괴되면 메모리에서 나간다. 그러나 메서드들을 이용해서 응용 프로그램이 상주성을 직접 제어할 수도 있다.
HRESULT ID3D12Device::MakeResident(
UINT NumObjects,
ID3D12Pageable *const *ppObjects);
HRESULT ID3D12Device::Evict(
UINT NumObjects,
ID3D12Pageable *const *ppObjects);
'그래픽스 > DirectX 12' 카테고리의 다른 글
[DirectX 12 3D게임 입문] Chapter 5.5: 렌더링 파이프라인 - 입력 조립기 단계 (1) | 2023.11.11 |
---|---|
[DirectX 12 3D게임 입문] Chapter 4.4: Direct3D의 초기화 - 시간 측정과 애니메이션 (0) | 2023.11.08 |
[DirectX 12 3D게임 입문] Chapter 4.3: Direct3D의 초기화 - Direct3D의 초기화 (0) | 2023.11.07 |
[DirectX 12 3D게임 입문] Chapter 4.2: Direct3D의 초기화 - CPU와 GPU의 상호작용 (1) | 2023.11.06 |
[DirectX 12 3D게임 입문] Chapter 3: 변환 (1) | 2023.11.03 |