본문 바로가기
Directx12 3D 프로그래밍 입문/4. Directx12 초기화

4.1 Direct3D의 초기화

by ftftgop3 2023. 10. 22.

Directx

GPU를 제어하고 프로그래밍하는 저 레벨 API

다른 버전 과의 차이점 : CPU 부담을 줄이고 다중 스레드 지원

COM : component object model

Directx 프로그래밍 언어 독립성 및 하위 호환성 가능 하게 하는 기술

COM 특징

  1. com 인터페이스를 new 키워드로 직접 생성하지 않는다.
  2. Release 메소드 호출로 메모리 해제(스마트 포인트 때문에 직접 호출 하지 않는다
  3. 자신을 참조하는 객체 카운트 후 0이면 메모리 해제 ( 스마트 포인트)

스마트 포인트 ComPtr의 메서드

  1. Get() : com 인터페이스 포인트
ComPtr<ID3D12GraphicsCommandList> mCommandList;
mCommandList->Get(); //return ID3D12GraphicsCommandList *
mCommandList->GetAddressOf() //return &ID3D12GraphicsCommandList
mCommandList->Reset() //초기화  
  1. GetAddressOf : com 인터페이스의 주소
  2. Reset : comptr를 nullptr 설정 및 참조 횟수를 1 감소

텍스처 형식

1~3차원 행렬의 데이터 구조

텍스처의 역할 : 단순한 데이터 구조가 아니라 픽셀, 3d 벡터 값, GPU 필터링, 다중 표본화

DXGI_FORMAT 열거형의 한 멤버

DXGI_FORMAT_R32G32B32_FLOAT : 각 원소는 32비트 → 4바이트 float 3개 조합

무형식 : 메모리만 확보 후 파이프 라인 연결 시 재 해석 가능( reinterpret_Cast 비슷 )

교환 사슬과 페이진 전환, 이중 버퍼링

사용 이유: 애니메이션 시 깜빡이는 현상을 피하는 방법, 부드러운 화면 갱신

  1. 전면 버퍼에 렌더링 중 일 때 후면 버퍼의 다음 프레임을 그린다.
  2. 후면 버퍼의 프레임을 다 그리면 교환
  3. 전면, 후면 버퍼를 번갈아 그리고 화면에 표시

깊이 버퍼링

깊이 버퍼 : 이미지 자료를 담지 않은 텍스처의 데이터, 각 픽셀에는 깊이 값 0.0~1.0 저장

깊이 값 판정 시 현재 깊이 값보다 작은 깊이 값을 저장하고 갱신

특징

  1. 후면 버퍼의 크기와 깊이 버퍼의 크기는 같다.
  2. 1.0 일 때 절두체의 가장 끝 부분에 위치해 있다.
  3. 깊이 버퍼링은 렌더링 순서와 상관없이 물체를 제대로 가려지는 기법이다.

초기화 후 1.0 깊이 값 세팅→ 깊이 값 판정 후 가까우면 픽셀 및 깊이 값 갱신

 

자원과 서술자

자원 : 후면 버퍼 및 깊이 스텐실 버퍼 데이터 덩어리, 범용적인 메모리

서술자 : 렌더링 파이프라인에 바인딩, 하나의 간접층 역할, 자원에 대해서 어떻게 사용할지 서술해준다.

렌더링 파이프 라인에서 바인딩되는 객체는 서술자가 바인딩된다.

자원만 있을 경우 렌더링, 쉐이더, 깊이 값 스텐스 버퍼 등 어디서 사용하는지 아무도 알 수 없다.

무형식 경우 일 때도 서술자가 필요하다.

팁 : 뷰는 서술자와 같은 동의어이다.

서술자 힙 : 서술자가 저장되는 공간, 같은 서술자를 한 힙에 모아서 관리,

하나의 자원을 렌더링 파이프라인의 여러 단계에 사용하고 싶으면 단계마다 서술자를 사용

앨리어싱 : 선들의 계단 현상, 모니터 해상도를 증가시키면 사라진다

앨리어싱 제거 기법 : 모니터 해상도를 최대치에서도 앨리어싱이 제거되지 않을 경우 사용

초과 표본화

  1. 후면 버퍼 및 깊이 버퍼를 4배 크게 잡고 후면 버퍼에 크게 렌더링
  2. 이미지를 다시 화면에 제시할 경우 후면 버퍼를 원래 크기의 버퍼로 환원
  3. 환원 공정은 4픽셀 블록의 네 색상의 평균으로 최종 픽셀을 결정
  4. 픽셀 및 메모리 소비량이 네 배라서 비용이 증가

4X다중 표본화

  1. 후면 버퍼의 깊이 버퍼를 4배로 크게 잡고 후면 버퍼에 픽셀 당 한번 계산
  2. 1920 * 1080 일 때 7680 * 4320 후면 버퍼의 색을 정할 때

색상과 부분 픽셀의 가시성(깊이 및 스탠실 판정) 및 포괄도 (부분픽셀의 다각형의 어느 정도 덮고 있는지 판단)

  1. 부분 픽셀의 색은 하나의 색으로 통일, 포괄도 판단으로 비어 있으면 초기화 한 컬러 값을 가지고 있다(부분 픽셀이 2가지 색으로 표현, 초기값 또는 지정된 색값)
  2. 환원 공정에서 부분 픽셀의 값을 평균으로 나타 낸다.

초과 표본화와 다중 표본화의 차이점

초과 표본화는 부분 픽셀이 하나하나 색이 다르게 표현,

다중 표본화는 부분 픽셀의 색이 같게 표현, 포괄도로 초기화 한 컬러 값을 유지(픽셀 당 처리)

다중 표본화를 코드에서 사용할 때 추출한 표본의 개수(부분 픽셀) 및 퀄리티를 지정할 수 있다.

 

ID3D12Device::CheckFeatureSupport

주어진 텍스처 형식과 표본 개수로 품질 수준 개수를 구해준다.

두 번째 매개변수의 구조체의 값을 수정(입력과 출력 모두 발생하는 함수)

그래픽 드라이버에서 다중표본화 지원 여부 및 그래픽 하드웨어가 다이렉트 X를 지원하는 여부

등 여러 지원을 확인할 수 있는 함수

 

기능 수준

enum D3D_FEATURE_LEVEL
    {
        D3D_FEATURE_LEVEL_1_0_CORE	= 0x1000,
        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_12_0	= 0xc000,
        D3D_FEATURE_LEVEL_12_1	= 0xc100
    } 	D3D_FEATURE_LEVEL;

GPU 성능을 파악 후 특정 기능 수준 이하면 실행 포기 대신에 더 낮은 기능으로 우회 가능 하다.

낮은 하드웨어에서도 정상적인 동작을 가능하게 프로그램을 수정할 수 있다.

 

DXGI(Directx Graphics Infrastructure)

그래픽 관련 인터페이스(교환 사슬, 페이지 전환, 전체화면 모드, 디스플레이 어뎁터, 모니터)

디스플레이 어댑터 : 물리 하드웨어의 그래픽 카드 IDXGIAdapter 그래픽 카드 열거

디스플레이 출력 : 모니터, IDXGIOutput 모든 모니터 열거

모든 디스플레이 모드 열거 (여러 해상도 지원)

전체 모드 성능을 극대화할 때

디스플레이 갱신율이 실제 모니터에서 지원하는 것과 정확히 일치

 

상주성

자원을 GPU 메모리로부터 내림 및 입주하며 자원의 상주성 관리

GPU 메모리에 할당하는 것으로 자주 일어나면 성능 저하

활용 : 게임 맵 변경 시 기존 자원을 내리고 새로운 맵의 자원을 GPU 메모리에 자원을 상주

CPU와 GPU

GPU : 명령 큐, 명령 대기열

CPU : 명령 목록 + 메모리 할당자

명령 처리 순서

  1. CPU가 명령 목록을 대기열에 추가
  2. GPU는 명령 대기열에 있는 데이터를 바로 꺼내서 처리하지 못함
  3. GPU는 이전 명령을 처리 후에 명령 대기열에 있는 명령을 처리

동기화는 줄이고 병렬로 처리하면서 최대한 둘 다 바쁘게 동작하는 것이 목표

전체 프로세스

  1. ID3D12CommandQueue 인터페이스로 명령 대기열 생성
  2. ID3D12Device::CreateCommandAllocator () 명령 할당자 생성
  3. ID3D12Device::CreateCommandList 명령 할당자를 변수로 입력(타입 일치)및 명령 목록 생성
  4. mCommandList → RSSetViewports() 등의 그래픽 함수 호출로 명령 목록에 기록 추가
  5. mCommandList→Close() 명령 기록 종료
  6. mCommandQueue → ExcuteCommandlists : 명령 목록을 제출
  7. 명령 대기열은 메모리 할당자에 담긴 명령을 참조 후 처리

명령 할당자는 여러 명령 목록에 연관시켜도 된다 하지만 여러 명령 목록을 동시에 기록할 수 없다.

명령 목록이 열린 상태에서 다른 명령 목록을 열면 오류 발생

명령 목록 생성 및 재 설정 시 열린 상태로 변경

ID3D12CommandList::Reset() : 명령 목록 초기화 및 내부 메모리 재사용

ID3D12CommandAllocator::Reset(): 명령 할당자를 다음 프레임을 위해 재사용

이슈 사항 : GPU가 명령 할당자에 담긴 명령을 실행했음이 확실해지기 전까지는 명령 할당자를 재설정하지 말아야 한다.

 

CPU/GPU 동기화

기하구조의 위치 : R, 첫 번째 위치 값 : P1, 두 번째 위치 값 P2

동기화 문제 발생 순서

  1. CPU에서 R = P1 위치 변경 명령을 작성 후 명령 대기열에 기록
  2. 명령 대기열 처리 중 CPU가 R = P2 갱신 시
  3. 명령 대기열에서는 R =P2 값을 읽어서 기하 구조 위치 변경
  4. 기하 구조의 위치를 P1이 아닌 P2로 렌더링
  5. 프로그래머가 원하는 순서대로 동작하지 않는 문제 발생

문제 해결 방식

GPU 가 명령 대기열의 특정 지점까지 모든 명령을 처리하기 전까지 CPU 대기 상태

명령 대기열이 특정 지점까지 비우는 행위를 명령 대기열을 비운다 또는 방출한다(flush)

Fance라는 객체를 사용해서 cpu와 gpu 동기화를 돕는다.

Fance 절차적 동작 예시

UINT64 mCurrentFence = 0; // Fence 데이터 
void D3DAPP::FlushCommandQueue()
{
	mCurrentFence++; // Fence 데이터 증가, 
	//명령 대기열에 Fence 추가 
	ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));
	//팬스 완료 값이 현재 팬스 값 보다 무조건 작아야 한다.
	//원활한 동작(이벤트 발생) 이 가능하다
	if(mFence->GetCompletedValue() < mCurrentFence)
	{
		HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
		//현재 팬스 값에 도달 시 이벤트 발생 할 수 있게 연결
		ThrowIfFailed(mFence->SetEventOnCompletion(mCurrentFence, eventHandle);
		//위의 팬스 이벤트 발생 전까지 CPU 대기 상태
		//GPU가 팬스까지 처리 후 CPU 동작 
		WaitForSingleObject(eventHandle, INFINITE);
		CloseHandle(eventHandle);		
	}
}

비동기 문제점 해결 방식 : GPU가 명령 대기열 특정 지점까지 명령 처리 할 때까지 CPU 대기

Fence 사용하여 CPU 대기 상태로 만들어버린다. 비효율적인 방식

 

자원 상태 전이

gpu 자원 위험을 피하기 위해서 (모든 쓰기 연산이 완료되기 전에 자원 읽기를 시도)

자원 상태 전이를 Direct3D 보고 하는 이유 : 성능, 개발자가 자동으로 추적하게 만들어야 하는 게 부담된다.

전이 자원 장벽 : 한 번의 API로 여러 개의 자원을 전이한다.

GPU에게 자원의 상태가 변경되었다고 명령을 추가한다고 생각하면 편하다.

명령 목록을 다중 스레드 활용

물체가 많이 큰 장면을 그리기 위해서는 여러 스레드로 분할 및 병렬로 처리하는 게 가장 효과적이다.

 

병렬 스레드 구성 요소

명령 목록, 명령 할당자는 자유 스레드 모형을 따르지 않아서 각 스레드마다 구성

명령 대기열 : 자유 스레드 모형에 따라서 각 스레드들이 생성한 명령 목록을 동시에 명령 대기열에 제출 가능 하다

성능상의 이유로 명령 목록들의 최대 개수를 반드시 초기화 시점에 설정

 

참고 및 내용 인용
프랭크 D 루나 지음, 류광 옮김 
한빛 미디어
Directx 12를 이용한 3D 게임 프로그래밍 입문(2017)