✨ DirectX를 공부하는 자세

DirectX는 "전체적인 흐름을 먼저 보고 숲을 이해하는 것"이 중요합니다. 처음부터 모든 코드를 이해하려 하면 금방 포기하게 됩니다. 그렇기 때문에 처음에는 각 객체가 어떤 역할을 하고, 어떤 흐름으로 상호작용하는지만 파악해도 충분합니다.


🚀 장치 초기화란?

장치 초기화는 GPU에게 외주를 맡기기 위한 준비 작업입니다. 이 과정을 통해 CPU는 반복적이고 계산량 많은 그래픽 처리를 GPU에게 위임하고, 자신은 게임 로직과 같은 고차원적인 연산에 집중할 수 있습니다.


🔧 DirectX 장치 초기화 핵심 구성요소

DirectX12에서 장치 초기화는 다음 네 가지 클래스를 중심으로 구성됩니다:

  • Device: GPU와 대화하기 위한 핵심 객체
  • CommandQueue: GPU에게 넘길 작업들을 담은 일감 목록
  • SwapChain: 더블 버퍼링을 통해 화면에 그릴 그림을 준비
  • DescriptorHeap: 리소스 뷰(View)를 담는 자료구조 (RTV, DSV 등)

🧱 COM(Component Object Model)이란?

DirectX는 COM 기반으로 동작합니다.
COM 객체들은 IUnknown을 상속받으며 다음과 같은 특성을 가집니다:

  • AddRef(): 참조 카운트 증가
  • Release(): 참조 카운트 감소 (0이 되면 메모리 해제)

이로 인해 언어 독립성, 하위 호환성이 보장됩니다.


🖥 Device 생성

DXGI Factory 생성

ComPtr<IDXGIFactory4> mdxgiFactory;
CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory));

DXGI는 Direct3D의 기반 라이브러리로 전체 화면 전환, 디스플레이 모드 열거 등 저수준 작업을 담당합니다.

Device 생성

ComPtr<ID3D12Device> md3dDevice;
D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&md3dDevice));
  • nullptr: 기본 디스플레이 어댑터 사용
  • D3D_FEATURE_LEVEL_11_0: 최소 요구 기능 수준

하드웨어가 DX12를 지원하지 않을 경우, WARP 어댑터를 사용해 소프트웨어 기반으로 생성합니다.


🧾 Command Queue & Command List 생성

GPU에게 명령을 직접 전달하지 않고, Command List에 명령을 모아 Command Queue를 통해 한꺼번에 전달합니다.

ComPtr<ID3D12CommandQueue> mCommandQueue;
D3D12_COMMAND_QUEUE_DESC desc = {};
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
md3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&mCommandQueue));

🔁 Swap Chain 생성

더블 버퍼링 개념

  • Front Buffer: 현재 화면에 출력 중
  • Back Buffer: 다음 프레임 작업 중
  • Swap을 통해 포인터를 교환하며 깜빡임 방지

코드 예시

DXGI_SWAP_CHAIN_DESC scDesc = {};
scDesc.BufferCount = 2;
scDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;

mdxgiFactory->CreateSwapChain(mCommandQueue.Get(), &scDesc, &mSwapChain);

SwapChain은 단순 리소스일 뿐이며, DescriptorHeap을 통해 뷰(View)를 생성해야 GPU가 인식합니다.


📦 Descriptor Heap

DescriptorHeap은 GPU에 리소스를 전달하기 위한 View들을 담는 테이블입니다.
각 View는 리소스를 어떻게 사용할 것인지 설명하는 메타데이터입니다.

예) RenderTargetView, DepthStencilView

D3D12_DESCRIPTOR_HEAP_DESC rtvDesc = {};
rtvDesc.NumDescriptors = 2;
rtvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
md3dDevice->CreateDescriptorHeap(&rtvDesc, IID_PPV_ARGS(&mRtvHeap));

이후 백버퍼 각각에 대해 RenderTargetView를 생성합니다.


⛓ Fence

CPU와 GPU는 비동기적으로 작업하므로 동기화(Synchronization)가 필요합니다.
Fence는 GPU가 특정 작업까지 완료했는지 추적하는 데 사용됩니다.

md3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence));

📐 Viewport & ScissorRect

  • Viewport: 렌더링할 실제 화면 영역
  • ScissorRect: 렌더링에서 제외할 영역을 제한
mScreenViewport = { 0, 0, (float)mClientWidth, (float)mClientHeight, 0.0f, 1.0f };
mScissorRect = { 0, 0, mClientWidth, mClientHeight };

profile
李家네_공부방

0개의 댓글