Programming/D3D12

[책공부] FrameResource 예제 ( + Root Constant)

Dorasima 2024. 1. 8. 20:32

(사실 이해했는지 모르겠다. 어렴풋이 느껴질 뿐... 그래도 그 느낌을 정리한다.)

 

1. FrameResource를 이용한 지형 + 파도 그리기 예제(까지)가 연습시켜주는 기술

 

1_1 어뎁터 초기화 단계

( 이전 예제  에서 한 초기화와 같다.)

더보기

- 창(window) 생성

- 어뎁터 (D3DDevice) 생성

- GPU와 CPU 동기화를 위한 Fence 생성

- GPU와 CPU의 [데이터 전송을 위한 Descriptor View]의 Handle Size 초기화

    (아마 요걸로 데이터 블록(아마도 테이블?)을 점프하면서 데이터를 읽을 것 같다.)

- Command Queue, Command Allocator, Command List 생성

    (멀티 스레딩을 위해 새로 바뀐 GPU에게 렌더링 동작을 요청하는 방법)

- Swap Chain에서 Render Target 얻기

- Render Target 의 정보를 GPU와 연결할 Descriptor (View)

- Render Target 의 정보를 GPU와 연결할 Descriptor 를 또 (GUID로 구분하는) 공간인 Descriptor  heap에 연결

- GPU에서 사용할 Depth - Stencil의 정보를 가지는  Descriptor (View)

- GPU에서 사용할 Depth - Stencil의 정보를 가지는  Descriptor 를 또 (GUID로 구분하는) 공간인 Descriptor  heap에 연결

- Viewport 설정

 

 

1_2. FrameResource 아이디어와 그것을 사용하기 위한 준비

이전 글 과 거의 같다. 

- 하지만 1_4 항목을 위해 추가된 부분이 있다.

 

1_3. Root Signature를 Root Constant로 사용하기

(얘는 Descriptor Heap 안쓴다. 직접 Shader에 꽂아 넣는 느낌.)

- Root Signature를 생성할때, D3D12_ROOT_PARAMETER_TYPE을 D3D12_ROOT_CONSTANTS로 만든다.

(d3dx12.h에서 편하게 CD3DX12_ROOT_PARAMETER를 사용하도록 하자.)

- View Heap이 없으니 Shader에 넘겨주는 것도 ID3D12Resource::GetGPUVirtualAddress()을 통해 얻은

- D3D12_GPU_VIRTUAL_ADDRESS 으로 Shader에 넘겨준다. 

- D3D12_ROOT_PARAMETER를  생성할 때, 슬롯 번호와 레지스터 번호를 맞춰서

-  ID3D12GraphicsCommandList::SetGraphicsRootConstantBufferView으로 IA 단계에서 Shader에 넘어가게 된다

 

1_4. CPU에서 실시간으로 바꾸는 Wave Vertex 정보

- 예제에서 제공된 Waves::Update 함수는 실시간(조건부)로 호출 된다. 그 업데이트 된 정보는 실시간으로 적용이 되어야 한다. ( 이건 뭔가 엄청 수학적인 내용이고, 내가 이해를 못했으니 여기에 적지 않는다.)

-   Wave를 나타내는 Vertex 정보가 FrameResource 에 맴버로 들어가면? Buffer 오염도 되지 않고 편하게, 성능적으로 큰 문제(?) 없이 실행할 수 있게 된다.

- 이렇게 실시간으로 CPU가 접근해서 Buffer의 값을 바꾸려면, D3D12_HEAP_TYPE_UPLOAD 을 이용하는 Buffer여야 하고, Map을 용해서 얻은 CPU 주소를 가지고 값을 변경하게 된다.

- 이전 글에서 언급했는지는 모르겠는데, Upload Heap을 사용하여 Buffer를 올리면, Default Heap을 사용하는 Buffer 보다 느리다고 한다.

-  매번 바뀌는 Buffer를 한번 만들면 GPU 만 접근할 수 있는 Default Heap을 통하는 Buffer를 만드는 것은...

-  중간에 Upload Heap Buffer를 사용해서 처음 값을 올려줘야 하고, ID3D12Device::CreateCommittedResource() 를 쓰고, ID3D12GraphicsCommandList::ResourceBarrier 가지고 막 상태를 바꿔주고 해야하니까... 뭐랄까 복잡해보인다.

 

1_5. Initialize 와 Update 와 Draw 단계 간단 정리

1_5_1. Initialize

- landscape를 예제에서 제공한 Grid 생성 함수와, height(x, z)를 이용해서 Default Buffer를 만든다.

- wave 를 예제에서 제공한 Waves 클래스를 이용해서 Upload Buffer를 만든다.

- landscape와 wave를 RenderItem 화 시킨다.

- 새로운 맴버가 추가된 FrameResource를 생성한다.

- Constant Buffer를 직접 받는 Root Constant로 Root Signature를 받는다.

- PSO를 만든다.

 

1_5_2. Update

- 현재 FrameResource를 얻는다.

- 혹시 모를 Fence를 체크하고

- Object Constant, Frame Constant와 Wave의 Vertex 정보를 업데이트한다.

 

1_5_3. Draw

- 현재 FrameResource의 Allocator를 초기화하고

(이전 글 처럼 기본적인 것을 Command List에 넣어주고)

- 일단 Frame 공통 Buffer를 넘겨주는데, SetGraphicsRootConstantBufferView로 설정했던 slot 번호와 Buffer의 GPU Address와 함께 쉐이더로 넘겨준다. ( D3D12_GPU_VIRTUAL_ADDRESS  )

- Object 별 (Render Item) Buffer도 넘겨주는데 이것도, Object Buffer 안에 Object에 맞는 Offset 값을 점프한 것을 GPU Address로 slot 번호에 맞게 쉐이더에 넘겨준다.

- DrawIndexedInstanced 에도 Render Item에 지정해 놓은 값을 넣어줘서 렌더링 요청을 한다.

(이전 글 처럼 Command Queue를 마무리 해주고)

- Frame Resource Index를 하나 늘려주고,

- 작업이 끝나면 GPU에게 fence 하나 늘리도록 요청한다.

 

 

2. 아직 잘 모르는 것

- 역시 제품을 만들면서 새로 삽질을 해야 진짜로 내것이 되는법

- 일단 다음 챕터로 간다.