4.1 기본기
4.1.1 Direct3D 12 개요
- 정의 : 응용프로그램에서 GPU를 제어하고 프로그래밍 하는데 쓰는 저수준 그래픽 API
- 책에서 "렌더 대상을 지운다" = 대상의 모든 원소를 특정한 하나의 값으로 설정한다. (delete, remove가 아님)
- ID3D12CommandList::ClearRenderTargetView 메서드 호출
- 응용 프로그램 - 그래픽 하드웨어 사이, Direct3D라는 간접층과 하드웨어 드라이버가 Direct3D 명령들을 기계어 명령들로 번역.
- 응용 프로그램 개발자는 GPU 세부사항 걱정x
- NIVIDIA, Intel, AMD 제조사들이 Direct3D의 명세를 준수하는 드라이버 제공 필요
- D11과 차이점:
- 새로운 렌더링 기능 추가
- CPU 부담 감소 및 다중 스레드 지원
- GPU에 더 가까운 수준의 API
- 추상화 감소 및 개발자가 관리해야 할 사항 증가
- 현세대 GPU 구조 밀접 반영
- API 사용 난이도 증가 및 성능 개선
- 여담 :
- D12는 하드웨어 관리가 늘어나면서, D13 개발보다는 기능 추가가 되고 있음.
4.1.2 COM
- COM (Component Object Model)
- DirectX 프로그래밍 언어 독립성과 하위 호환성을 지원
- c++로 DirectX 응용 프로그램을 프로그래밍 할 때, 'COM'의 세부 사항은 대부분 드러나지 않음
- 'COM 인터페이스'를
new
키워드로 직접 생성할 일 없음- 프로그래머는 필요한 'COM 인터페이스'를 가리키는 포인터를 특별한 함수들을 이용 or
- 다른 'COM'인터페이스의 메서드를 얻는 방법
- 'COM 인터페이스'를
- 'COM 인터페이스'의 삭제는
delete
가 아니라 인터페이스의Release
메서드 호출- 모든 'COM 인터페이스'는
IUnknown
인터페이스 기능 상속, 해당 인터페이스는Release
메서드 제공 - 참조 횟수가 0이 되면 메모리에서 해제
- 모든 'COM 인터페이스'는
- 참고 : 'COM 인터페이스'의 이름은 대문자
I
로 시작.
- Microsoft::WRL::ComPtr <- "wrl.h" 필요
- Com객체의 수명 관리를 돕는, Smart Pointer
- 범위를 벗어난 ComPtr 인스턴스는 바탕 Com 객체에 대해 자동으로 Release 호출
- (책에서 사용하는 메서드)
- Get : 바탕 COM 인터페이스를 가리키는 포인터 반환.
ComPtr<ID3D12RootSignature> mRootSignature; ... // SetGraphicsRootSignature는 ID3D12RootSignature* 형식의 인수를 받음 mCommandList->SetGraphicsRootSignature(mRootSignature.Get());
해당 COM 인터페이스 포인터 형식의 인수를 받는 함수를 호출할 떄 사용
- GetAddressOf : 바탕 COM 인터페이스를 가리키는 포인터의 주소를 반환.
함수의 매개변수를 통해 포인터를 돌려받을 떄 흔히 사용 ComPtr<ID3D12CommandAllocator> mDirectCmdListAlloc; ... ThrowIfFailed(md3dDevice->CreateCommandAllocator( D3D12_COMMAND_LISTTYPE_DIRECT, mDirectCmdListAlloc.GetAddressOf()));
- Reset: ComPtr : 인스터를 nullptr로 설정하고 바탕 COM 인터페이스의 참조 횟수를 1 감소
- 직접 ComPtr 인스턴스에 nullptr 배정해도 됨
ComPtr<ID3D12Device> device = nullptr;
4.1.3 텍스쳐 형식
- n차원 텍스쳐 = n차원 배열
- 텍스쳐는 특정 형식의 자료 원소들만 담을 수 있음
- DXGI_FORMAT 이라는 열거형으로 지정
- DXGI_FORMAT_R32G32B32_FLAOT: 각 원소는 32bit 부동 소수점 성분 3 개로 구성
- DXGI_FORMAT_R16G16B16A16_UNORM: 각 원소는 [0, 1] 구간으로 사상되는 16bit 성분 4 개로 구성
- DXGI_FORMAT_R32G32_UINT: 각 원소는 부호없는 32bit 정수 성분 2 개로 구성
- DXGI_FORMAT_R8G8B8A8_UNORM: 각 원소는 [0, 1] 구간으로 사상되는 부호 없는 8bit 성분 4 개로 구성
- DXGI_FORMAT_R8G8B8A8_SNORM: 각 원소는 [-1, 1] 구간으로 사상되는 부호 있는 8bit 성분 4 개로 구성
- DXGI_FORMAT_R8G8B8A8_SINT: 각 원소는 [-127, 127] 구간으로 사상되는 부호 있는 8bit 정수 성분 4 개로 구성
- DXGI_FORMAT_R8G8B8A8_UINT: 각 원소는 [0, 255] 구간으로 사상되는 부호 없는 8bit 정수 성분 4 개로 구성
- DXGI_FORMAT_R16G16B16A16_TYPELESS: 각 비트는 설정하지만, 타입을 지정하지 않음 <- 메모리만 확보, 추후 텍스처를 파이프라인에 묶을 때 지정하는 용도로 사용('c++'의 'reinterpret_cast'와 유사
- reinterpret_cast : c++에서 다양한 타입 값 변환할 때 사용되는 연산자
- -> DXGI_FORMAT_{RGBA_bit 표시}_{타입} 형태의 직관적 이름
- -> [-127,1277], [0, 255]의 구간 차이로 인해, 색깔 값의 표현이 다소 달라짐
- DXGI_FORMAT 이라는 열거형으로 지정
4.1.4 교환 사슬과 페이지 전환
- 이중 버퍼링(Double Buffering) :
- 후면 버퍼에서 그림을 그리고 전면 버퍼로 완성된 그림을 송출
- 사용자는 완성된 그림 만을 보게 됨.
- 버퍼 스왑(Buffer Swap) :
- 후면 버퍼가 완전히 렌더링 되면, 후면 버퍼와 전면 버퍼의 역할이 서로 바뀜
- 전면 버퍼와 후면 버퍼는 하나의 교환 사슬(swap chain)을 형성
- IDXGISwapChain :
- 전면 버퍼 픽셀 데이터와 후면 버퍼 텍스처를 담음
- IDXGISwapChain::ResizeBuffers : 버퍼 크기 변경
- IDXGISwapChain::Present : 버퍼의 *"제시"*
- IDXGISwapChain :
D3D
에서는 제시(presenting) 라고 부름
- 전면 버퍼(Front Buffer) :
- 저장된 내용 표시
- 픽셀(Pixel) 단위 데이터 처리
- 후면 버퍼 (Back Buffer) :
- 그래픽 데이터가 먼저 렌더링 되는 곳
- 그래픽 연산 수행
- 현재 화면에 표시x
- 텍셀(Texel) 단위 데이터 처리
- 하지만, 픽셀 데이터 저장이 주된 역할
- 장점 :
- 화면 깜빡임 방지: 그래픽 데이터를 렌더링하는 동안 깜박임 현상 방지
- 티어링 방지: 화면이 잘못된 위치에서 찢어지는 티어링 방지
-> 프레임 버퍼와 모니터 동기화에서 발생하는 문제, 이전 프레임과 섞이는 현상으로 수직, 수평 방향으로 어긋남 발생; (정점 버퍼와 관계 x)
4.1.5 깊이 버퍼링
- 깊이 버퍼(Detph Buffer) :
- 이미지 자료를 담지 않는 텍스처
- 각 픽셀의 깊이 정보 보관
- 깊이는 0..0 ~ 1.0
- 0.0 : 시야 절두체 안에서 관찰자와 최대한 가까운 물체
- 1.0 : 시야 절두체 안에서 관찰자와 최대한 먼 물체
- 시야 절두체(view frustum)
- 3D 그래픽스에서 카메라가 볼 수 있는 공간을 정의
- 구성
- 근평면 (Near Plane): 카메라와 가장 가까운 절두체의 면으로, 화면에 표시되는 최소 거리입니다.
- 원평면 (Far Plane): 카메라와 가장 먼 절두체의 면으로, 화면에 표시되는 최대 거리입니다.
- 상면 (Top Plane): 절두체의 윗면으로, 카메라의 시야 각을 정의합니다.
- 하면 (Bottom Plane): 절두체의 아랫면으로, 카메라의 시야 각을 정의합니다.
- 좌면 (Left Plane): 절두체의 왼쪽 면으로, 카메라의 시야 각을 정의합니다.
- 우면 (Right Plane): 절두체의 오른쪽 면으로, 카메라의 시야 각을 정의합니다.
- 깊이는 0..0 ~ 1.0
- 깊이 버퍼의 원소들과 후면 버퍼의 픽셀들은 일대일 대응
- ex) 1080 x 1024 해상도는 1280 x 1024개 원소 깊이 버퍼로 구성
- 깊이 버퍼는 하나의 텍스처
- 생성 시 특정한 자료 원소 형식 지정 필요
- ex)
- DXGI_FORMAT_D32_FLOAT_S8X24_UINT : 각 텍셀은 32비트 부동소수점 깊이 값과 [0,255] 구간으로 사상되는 부호 없는 8bit 정수 스텐실 값, 그리고 다른 용도 없이 채움(padding)용 24bit 구성
- DXGI_FORMAT_D32_FLOAT : 각 텍셀은 32비트 부동소수점 깊이 값
- DXGI_FORMAT_D24_FLOAT_S8_UINT : 각 텍셀은 [0,1]구간으로 사상되는 부호 없는 24bit 깊이 값 1개, [0,255]구간 사상되는 부호 없는 8bit 정수 스텐실 값
- DXGI_FORMAT_D16_UNORM : 각 텍셀은 [0,1] 구간으로 사상되는 부호 없는 16bit 깊이 값
- -> 응용 프로그램이 스텐실 버퍼를 반드시 사용해야 하는 것은 아니나, 사용한다면 스텐실 버퍼는 항상 깊이 버퍼와 같은 텍스처에 포함
- "D3D"는 깊이 버퍼링 또는 z-버퍼링 사용
- 깊이 버퍼링 : 그리는 순서 무관하게 물체들이 제대로 가려짐
- (z-버퍼링과 깊이 버퍼링은 같은 개념 지칭)
- 깊이 버퍼링 : 그리는 순서 무관하게 물체들이 제대로 가려짐
- 깊이 판정 실패 시, 버퍼 갱신 x
- 가장 앞에 있는 물체보다 뒤에 물체가 추가 될 떄, 버퍼 갱신을 하지않음
- 깊이 판정 실패 시, 버퍼 갱신 x
4.1.6 자원과 서술자
- 그리기 호출
- 호출 전에 필요한 자원을 바인딩 시킬 필요가 있다
- 필요하다면 호출마다 바인딩 갱신
- GPU 자원들이 파이프 라인에 직접 묶이는 것 X
- 자원 서술자를 통해 실제 자료 접근
- 호출이 참조할 서술자 명시 -> 해당 자원들이 렌더링 파이프라인에 바인딩
- 서술자(Descriptor) : { <- "View"는 서술자와 동의어 }
- 자원을 GPU에게 서술해 주는 경량 자료구조
- 서술자는 간접층(level of indirection)
- GPU 자원이 사실상 범용적인 메모리 조각이기 때문
- 응용프로그램 초기화 시점에 생성해야 함
- 해당 시점에 일정 정도의 형식 점검과 유효성 검증이 일어나기 떄문
- 초기화 시점에서 생성하는 것이 실행 시점보다 나음
- 최적화, 안정성(디버깅), 반복 재사용, 메모리관리, 서술자 힙 구조 적합성 등으로 인해
- 서술자 종류 일부 (책에 사용하는 것들)
- Shader Resource View(SRV) : 텍스처의 특정 MIP 레벨이나 배열의 특정 슬라이스를 참조
- Constant Buffer View (CBV) : 상수 버퍼의 특정 범위를 참조
- Unordered Access View (UAV) : 버퍼나 텍스처의 특정 영역에 쓰기 작업 수행
- 표본추출기 서술자 : 텍스처 적용에 쓰이는 표본추출기(sampler) 자원 서술
- Render Target View(RTV) : 렌더 대상 자원 서술
- Depth Stencil View(DSV) : 깊이/스텐실 자원 서술
- 자원
- 범용적
- 같은 자원을 렌더링 파이프라인의 서로 다른 단계(stage)에 사용 가능
- ex) 텍스처 : 렌더 대상 사용 -> 셰이더 자원으로도 사용
- 단계마다 개별적인 서술자 필요
- 사용처에 대한 명시 x
- 서술자가 지정 및 GPU에 서술해줄 필요가 있음
- 부분 영역 정보 x
- 자원의 일부 영역만 렌더링 파이프라인에 묶이는게 불가
- 서술자가 부분 영역을 지정해줄 수 있음
- SRV, CBV, UAV
- 무형식으로 생성 가능
- GPU는 자원 형식 파악 불가
- 해당 자원을 참조하는 서술자를 생성할 떄, 구체적인 형식 명시 가능
- 런탐임에 자원에 대한 접근 최적화를 위해, 가능한 지양
- 서술자 힙(Descriptor heap)
- 서술자 배열
- 서술자 종류마다 개별적인 힙 필요
- 같은 종류는 같은 힙에 저장 ( 한 종류에 여러개 힙도 가능 )
4.1.7 다중표본화의 이론
- 앨리어싱 제거(antialiasing) 기법
- 초과표본화(supersampling) :
- 후면 버퍼와 깊이 버퍼를 화면 해상도보다 4배 (가로 2, 세로2) 크게 잡고, 렌더링. -> 제시할 떄, 버퍼를 원래 크기로 환원(resolving)
- 하양표본화(downsampling) :
- 4pixel 블록의 네 생상의 평균을 최종 색상으로 지정하는 환원 공정
- 소프트웨어에서 해상도를 늘려 해결하는 방식
- 하양표본화(downsampling) :
- 4배로 늘렸기에 비용이 높음
- 후면 버퍼와 깊이 버퍼를 화면 해상도보다 4배 (가로 2, 세로2) 크게 잡고, 렌더링. -> 제시할 떄, 버퍼를 원래 크기로 환원(resolving)
- 다중표본화(multisampling)
- 절충적인 앨리어싱 제거 기법 (D3D에서 사용)
- 일부 계산 결과를 부분픽셀(subpixel)들 사이서 공유 -> 비용 저렴
- supersampling 처럼 x배인 후면 버퍼와 깊이 버퍼 사용
- 픽셀당 한 번만 색상 계산 -> 해당 색상과 부분픽셀들의 가시성, 포괄도를 이용해 최종 결정
- 초과표본화(supersampling) :
4.1.8 Direct3D의 Multisampling
- DXGI_SAMPLE_DESC 구조체를 적절히 채워야 함
-
typedef struct DXGI_SAMPLE_DESC { UINT Count; // 추출할 표본 개수 UINT Quality // 원하는 품질 수준 } DXGI_SAMPLE_DESC;
- 표본_개수&품질_수준은 "렌더링 비용"과 정비례
- 교환 사슬 버퍼, 깊이 버퍼 모두 필요
- 후면 버퍼와 깊이 버퍼를 생성할 떄, 동일한 multisampling 설정 적용
-
텍스처 형식과 표본 개수이 조합에 대한 품질 수준 개수 파악 메서드 사용 예
typedef struct D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS { DXGI_FORMAT Format; UINT SampleCount; D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG Flags; UINT NumQualityLevels; } D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS; D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels; msQualityLevels.Format = mBackBufferFormat; msQualityLevels.SampleCount = 4; msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE; msQualityLevels.NumQualityLevels = 0; ThrowIfFailed(md3dDevice->CheckFeatureSupport( D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &msQualityLevels, sizeof(msQualityLevels))); ```</div></details>
- 실제 응용에서 multisampling 표본을 4~8개만 추출
- D3D12 대응 장치는 모든 렌더 대상 형식에 대해 4x multisampling 지원
- Count 1, Quality 0 설정하면 multisampling 사용 x
4.1.9 기능수준
- D3D_FEATURE_LEVEL 열거형
- GPU가 지원하는 기능들의 엄격한 집합
4.1.10 DXGI (DirectX Graphics Infrastructure)
- D3D와 함께 쓰이는 API
- IDXGISwapChain
- 화면 전환 등등
- <- IDXGI{Interface}{Ver} 을 쓰기 위해서는 `<dxgi{Ver}>` 헤더 필요 <- 헤더 링크 설정 필요 `#pragma comment(lib, "dxgi.lib"`>
- IDXGIFactory : DXGI를 사용하기 위해 필요한 각종 인터페이스 생성
- IDXGIAdapter : 그래픽카드 열거
- IDXGIOutput : 그래픽 카드에 연결된 디스플레이 기능 제어
- IDXGISwapChain : double buffering, triple buffering 에 사용
시스템 모든 디스플레이 어댑터 열거
//시스템의 모든 디스플레이 어댑터를 열거한다. void D3DApp::LogAdapters() { // DXGI Factory 생성 ComPtr<IDXGIFactory4> mdxgiFactory; if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory)))) return -1; UINT i = 0; IDXGIAdapter* adapter = nullptr; std::vector<IDXGIAdapter*> adapterList; while (mdxgiFactory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND) { DXGI_ADAPTER_DESC desc; adapter->GetDesc(&desc); std::wstring text = L"***Adapter"; text += desc.Description; text+= L'\n'; OutputDebugString(text.c_str()); adapterList.push_back(adapter); ++i; } for (size_t i = 0; i < adapterList.size(); ++i) { LogAdapterOutputs(adapterList[i]); } } ```</div></details>
어댑터 관련 모든 출력 열거
//주어진 어댑터와 연관된 모든 출력을 열거한다. void D3DApp::LogAdapterOutputs(IDXGIAdapter* adapter) { UINT i = 0; IDXGIOutput* output = nullptr; while (adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND) { DXGI_OUTPUT_DESC desc; output->GetDesc(&desc); std::wstring text = L"***Output : "; text += desc.DeviceName; text += L"\n"; OutputDebugString(text.c_str()); LogOutputDisplayModes(output, mBackBufferFormat); ReleaseCom(output); ++i; } } ```</div></details>
- DXGI_MODE_DESC 해당 형태 구조체들은 디스플레이 관련 정보를 담는다.
tytypedef struct DXGI_MODE_DESC { UINT Width; // 가로 해상도 UINT Height; // 세로 해상도 DXGI_RATIONAL RefreshRate; // 주사율 DXGI_FORMAT Format; // 디스플레이 형식 DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; //스캔 방식 : 순차 주사(프로그레시브) or 비월 주사(인터레이스) DXGI_MODE_SCALING Scaling; // 영상 모니터 크기에 맞게 스케일 값 } ```
주어진 출력과 디스플레이 형식을 지원하는 모든 디스플레이 모드 목록 출력
```
주어진 출력과 픽셀 형식의 조합이 지원하는 모든 디스플레이 모드를 나열한다.
void D3DApp::LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format)
{UINT count = 0; UINT flags = 0; //nullpter을 인수로 해서 호출하면 목록의 크기(모드 개수)를 얻게 된다. output->GetDisplayModeList(format, flags, &count, nullptr); std::vector<DXGI_MODE_DESC> modeList(count); output->GetDisplayModeList(format, flags, &count, &modeList[0]); for (auto& x : modeList) { UINT n = x.RefreshRate.Numerator; UINT d = x.RefreshRate.Denominator; std::wstring text = L"Width = " + std::to_wstring(x.Width) + L" " + L"Height = " + std::to_wstring(x.Height) + L" " + L"Refresh = " + std::to_wstring(n) + L"/" + std::to_wstring(d) + L"\n"; ::OutputDebugString(text.c_str()); }
}
```- 전체 화면 성능 최적화를 위해 지원 모니터 지정에 유용
위의 예제 코드 실행 시, 필요 전처리문
#pragma comment(lib, "dxgi.lib") #include <dxgi1_4.h>// IDXGI_ #include <wrl.h> // Microsoft::WRL::ComPtr #include <vector> #include <iostream> #include <string> // std::to_string #ifndef ReleaseCom #define ReleaseCom(x) { if(x){ x->Release(); x = 0; } } #endif ```</div></details>
4.1.11 기능지원점검
CheckFeatureSupport
HRHRESULT ID3D12Device::CheckFeatureSupport( D3D12_FEATURE Feature, void *pFeatureSupportData, UINT FeatureSupportDataSize );
Feature
: 지원 여부 점검할 기능 종류D3D12_FEATURE_D3D12_OPTIONS
: D3D 12의 여러 기능D3D12_FEATURE_ARCHITECTURE
: 하드웨어 아키텍처 기능들D3D12_FEATURE_FEATURE_LEVELS
: 기능 수준들D3D12_FEATURE_FORMAT_SUPPORT
: 텍스처 형식에 대한 기능D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS
: 다중 표본화 기능
pFeatureSupportData
: 기능 지원 정보 설정될 구조체 포인터Feature
의 타입과 동일해야 함
FeatureSupportDataSize
:pFeatureSupportData
에 전달한 구조체 크기다중 기능 지원 점검 예제
typedef struct D3D12_FEATURE_DATA_FEATURE_LEVELS { UINT NumFeatureLevels; const D3D_FEATURE_LEVEL *pFeatureLevelsRequested; D3D_FEATURE_LEVEL MaxSupportedFeatureLevel; } D3D12_FEATURE_DATA_FEATURE_LEVELS; D3D_FEATURE_LEVEL featureLevels[3] = { D3D_FEATURE_LEVEL_11_0, // First check D3D 11 support D3D_FEATURE_LEVEL_10_0, // Next, check D3D 10 support D3D_FEATURE_LEVEL_9_3 // Finally, check D3D 9.3 support }; D3D12_FEATURE_DATA_FEATURE_LEVELS featureLevelsInfo; featureLevelsInfo.NumFeatureLevels = 3; featureLevelsInfo.pFeatureLevelsRequested = featureLevels; md3dDevice->CheckFeatureSupport( D3D12_FEATURE_FEATURE_LEVELS, &featureLevelsInfo, sizeof(featureLevelsInfo)); ```</div></details>
4.1.12 상주성
- 상주성(regsidency) :
- 응용 프로그램 자원을 GPU메모리에 내리('퇴거')거나 올림('입주')으로 자원의 상주성을 관리
- GPU 메모리 양 최소화가 핵심
- 상주성 변경의 적합한 시점 예 : "게임 레벨", "게임 지역" 변화 시점
- 기본적으로 자원 생성 -> 입주, 파괴 -> 퇴거
MakeResident
: 자원을 GPU 메모리에 유지HRESULT MakeResident( UINT NumObjects, ID3D12Pageable *const *ppObjects ));
Evict
: 자원을 GPU 메모리에서 내보냄HRESULT Evit( UINT NumObjects, ID3D12Pageable *const *ppObjects );
'프레임워크 > DirectX' 카테고리의 다른 글
Direct3D의 초기화_게임프레임워크&타이머 코드 의문점 (0) | 2025.03.07 |
---|---|
04.Direct3D의_초기화03 (1) | 2025.03.04 |
04.Direct3D의_초기화02 (0) | 2025.03.04 |
02.Matrix (0) | 2025.02.25 |
01.Vector (0) | 2025.02.25 |