일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 다익스트라
- DP
- 스토어드 프로시저
- Trie
- SQL
- Two Points
- binary search
- MYSQL
- Brute Force
- 이진탐색
- Dijkstra
- Stored Procedure
- 그래프
- Hash
- union find
- two pointer
- String
- Today
- Total
codingfarm
6. 자원, 서술자 본문
자원과 서술자에 대해서 알고싶다면 크게 4가지 요소에 대해서 집중하여 살펴보면된다.
- 자원의 생성
- 서술자의 생성
- 데이터 업로드
- 바인딩
0. 자원
- Rendering을 위해 GPU가 write/read 하는 대상
- physical memory나 heap에 읽고 쓰기 위한 CPU와 GPU의 일반적인 ability를 encapsulated 한 것
- 종류
- 각종 buffer들, texture ...
- ID3D12Resource로 대표됨
- DXGI_FORMAT 형식을 지님
- 별도의 heap 공간에 저장됨
- 상태를 지님
버퍼(buffer)
- GPU가 접근 가능한 GPU 자원(ID3D12Resource) 공간
- 응용 프로그램에서 정점 같은 자료 원소들의 배열을 GPU에 제공해야 할 때에는 항상 버퍼를 사용함
- 텍스처보다도 단순한 자원(다차원이 아님)
- mipmap이나 filter, 다중 표본화 기능이 없음
- D3D12_RESOURCE_DESC::Dimension 에 할당된 열거형 타입에 따라 다양한 용도로 사용 가능
- D3D12_RESOURCE_DIMENSION_TEXTURE1D
- D3D12_RESOURCE_DIMENSION_TEXTURE2D
- D3D12_RESOURCE_DIMENSION_TEXTURE3D
- D3D12_RESOURCE_DIMENSION_BUFFER : vertices, indexes
- D3D12_RESOURCE_DIMENSION_UNKNOWN : unknown type
0-1. 텍스처 형식
- data element들의 행렬(2차원 배열)
- 텍셀(texel) : 텍스처의 원소
- 응용
- 이미지 자료의 저장 : 픽셀의 색상
- 픽셀(pixel) : 색상 정보를 저장하는 원소(정보를 담고만 있어도 픽셀이라 할때도 있다.)
- 법선 매핑 : 3차원 벡터
- 이미지 자료의 저장 : 픽셀의 색상
- DXGI_FORMAT 이라는 열거형으로 지정된 특정 형식(format)의 자료 원소들만 담을 수 있음
- 정점 및 색인 자료 형식의 서술에 쓰임
- data element format들을 나열하면 아래와 같음
docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
위 열거형 자료가 뜻하는바는 아래와 같다.
_FLOAT | 실수, 32비트, 16비트 |
_SINT | 2의 보수, 부호화 정수 |
_UINT | 부호화 되지 않은 정수 |
_SNORM | 부호화 정수, 정규화(Normalized) [-1.0, +1.0] |
_SRGB | 표준 RGB |
_TYPELESS | 무형식 리소스 |
_UNORM | 부호화 되지 않은 정수, 정규화 [0.0, 1.0] |
R: Red, G: Green, B: Blue, A: Alpha, D: Depth, S: Stencil, X: Don't Care, BC: Block Compression
가령
DXGI_FORMAT_R8G8B8A8_UNORM
_R8G8B8A8
- 4개 요소(Component) : RGBA
- 각 요소는 8bit
- 하나의 픽셀은 32bit
_UNORM
- 각 요소는 부호화 되지 않은 정수
- 각 요소는 정규화됨(0,1255,2255,⋯,1.0)
- 000000000→0, 000000001→1255, 111111111→1
무형식(typeless)
- 특수한 텍스처 형식
- 메모리만 확보해두고 자료의 구체적인 해석 방식은 나중에 텍스처를 파이프라인에 묶을때 지정한다.
- void* 형식의 메모리를 가져와 reinterpret_cast 하여 원하는 자료형으로 형변환 하는것과 비슷하다.
0-2. 자원의 생성
- 자원의 종류마다 생성 방법 및 타이밍이 다름
- 가령 Back Buffer는 Swap Chain 생성시 만들어짐
일반적인 자원 생성 방법
- D3D12_RESOURCE_DESC 구조체를 정의하여 생성될 자원에 대해 서술한다
- D3D12_HEAP_PROPERTIES 구조체를 정의하여 자원이 저장될 heap에 대해 서술한다
- D3D12_CLEAR_VALUE 구조체를 정의하여 리소스를 초기화할 값을 결정한다
- ID3D12Device::CreateCommittedResource를 호출하여 resource를 생성한다.
예 : Depth Stencil Buffer의 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
// 1. D3D12_RESOURCE_DESC 를 정의한다.
D3D12_RESOURCE_DESC depthStencilDesc;
depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
depthStencilDesc.Alignment = 0;
depthStencilDesc.Width = mClientWidth;
depthStencilDesc.Height = mClientHeight;
depthStencilDesc.DepthOrArraySize = 1;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
depthStencilDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
// 2. D3D12_HEAP_PROPERTIES 를 정의한다.
D3D12_HEAP_PROPERTIES heapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT)
// 3. D3D12_CLEAR_VALUE 를 정의한다.
DXGI_FORMAT mDepthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
D3D12_CLEAR_VALUE optClear;
optClear.Format = mDepthStencilFormat;
optClear.DepthStencil.Depth = 1.0f;
optClear.DepthStencil.Stencil = 0;
// 4. CreateCommittedResourc를 호출한다.
Microsoft::WRL::ComPtr<ID3D12Resource> mDepthStencilBuffer;
ThrowIfFailed(md3dDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&depthStencilDesc,
D3D12_RESOURCE_STATE_COMMON,
&optClear,
IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf())));
|
cs |
- D3D12_HEAP_PROPERTIES 에 resource가 저장될 heap의 type을 결정한다.
- D3D12_CLEAR_VALUE에 초기화 될 리소스의 타입을 결정한다.
위 예제코드만으로는 생성된 resource가 depth stencil buffer라는 특성을 변수명을 제외하면 알아낼 방도가 없다.
resource의 쓰임새에 대한 정보는 바로 아래의 descriptor가 소유한다.
1. 서술자(Descriptor)
- 정의 : 자원을 GPU에게 서술해주는 경량의 자료구조, 간접층(level of indirection)
- 역할
- Direct3D에게 자원의 용도(자원을 pipeline의 어떤 단계에 묶어야 하는가)를 알려줌
- 무형식 자원의 구체적인 type을 결정
- GPU는 자원서술자를 통해 여러 기능들을 수행한다.
- 자원의 실제 자료에 접근
- 자료를 사용하는데 필요한 정보(resource, mip-map...) 획득
- 존재 이유
- GPU 자원은 범용적인 메모리 조각에 불과하므로 GPU나 프로그래머는 서로의 data의 용도를 알 방법이 없음
- 자원 자체는 자신의 용도(렌더 대상, 깊이 스텐실 버퍼, 셰이더 자원...)에 대한 정보를 담지 않으므로
- 자원자료의 지정 및 자원을 GPU에게 서술하기 위해 존재
- ex
- 동일한 자원일지라도 pipeline의 다른 단계들에서 쓰기 위해
- 자원 자체에는 사용 목적에 대해 명시되어 있지 않으므로
- 가령 texture를 렌더 대상 및 shader 자원으로 사용
- 자원 자체에는 사용 목적에 대해 명시되어 있지 않으므로
- 자원의 일부 영역만 rendering pipeline에 묶고 싶을때
- 자원자체에는 영역에 관한 정보가 없으므로
- 자원을 무형식으로 생성하기 위해
- GPU는 자원의 형식을 모르므로
- 동일한 자원일지라도 pipeline의 다른 단계들에서 쓰기 위해
- ex
서술자는 Direct3d에게 자원의 사용법을 설명해준다. (사용법 : 자원을 pipeline 어떤 단계에 묶을지)
- 서술자는 자원의 사용법에 따라 여러 종류(형식)가 있다.
- CBV/SRV/UAV 서술자들 : 각각 상수 버퍼(constant buffer), 셰이더 자원(shader resource), 순서 없는 접근(unordered access view)을 서술
- 표본추출기 서술자 : 텍스처 적용에 쓰이는 표본 추출기(sampler) 자원을 서술
- RTV 서술자 : 렌더 대상(render target) 자원을 서술
- DSV 서술자 : 깊이⋅스텐실(depth/stencil) 자원을 서술
- descriptor들은 GPU에 따라 다양한 크기를 가지며, SRV와 UAV, CBV의 크기를 ID3D12Device::GetDescriptorHandleIncrementSize 함수를 호출하여 구할 수 있다.
- descriptor들은 아래그림처럼 분할될 수 없는 단위로써 다뤄진다.
- SRV, CBC, UAV는 descriptor heap에서 공통된 type으로 취급하며, 모두다 pipeline에 직접 binding 가능하다.
- API함수를 호출하여 생성된다.
- 각 view마다 생성 함수가 다름
- 하나의 자원을 참조하는 서술자는 여러개일수도 있다.
- ex) 한 자원의 여러 부분영역을 여러 서술자가 참조
- 한 자원을 렌더링 파이프 라인의 여러 단계에 개별적인 서술자로 묶음
- 하나의 텍스쳐를 렌더대상이나 셰이더자원으로 사용
- 이 경우 RTV 및 SRV 서술자가 쓰인다.
- 서술자들은 응용 프로그램의 초기화 시점에 생성되어야 함
- 초기화 시점에 일정정도의 형식점검과 유호셩 검증이 발생함
- 형식이 완전히 지정된 자원은 런타임에 대한 접근을 최적화 할 수 있게 함(DirectX SDK 문서)
- 즉, 무형식 자원은 필요할때만 쓸것
1-1. 바인딩(Binding)
- 그리기 명령 제출 전, 해당 그리기 호출이 참조할 자원들을 렌더링 파이프에 묶는(bind)것
- 이를 가리켜 "자원을 파이프 라인에 연결(link)한다 또는 바인딩(binding)한다 라고 말하기도 한다.
- 그리기 호출마다 달라지는 자원도 있으며, 필요에 따라 그리기 호출마다 그런 자원들의 binding을 갱신해 주어야 한다.
- GPU 자원들이 직접 파이프 라인에 묶이는게 아니라, 해당 자원을 참조하는 서술자(Descriptor) 객체가 묶인다.
1-2. 서술자 힙(Desciptor Heap)
- 서술자들의 배열
- 응용 프로그램이 사용하는 descriptor들이 저장되는 곳
- ID3D12DescriptorHeap 인터페이스로 대표됨
- 서술자 종류마다 개별적인 서술자 힙이 필요
- 같은 종류의 서술자들은 같은 서술자 힙에 저장됨
- 한 종류에 서술자 힙에 대해 여러 개의 힙을 둘 수도 있음
Descriptor Heap 생성
- D3D12_DESCRIPTOR_HEAP_DESC를 채운다.
- ID3D12Device::CreateDescriptorHeap를 호출
descriptor heap의 종류마다 D3D12_DESCRIPTOR_HEAP_DESC::Type 에 다른값을 채워야함
예 : depth stencil buffer를 참조하기 위한 descriptor를 저장할 descriptor heap의 생성
1
2
3
4
5
6
7
8
9
10
|
// 1. D3D12_DESCRIPTOR_HEAP_DESC를 채운다.
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsvHeapDesc.NodeMask = 0;
// 2. ID3D12Device::CreateDescriptorHeap를 호출
ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
&dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
|
cs |
1-3. 서술자 생성
서술자의 생성은 그 view의 종류에 따라 생성 함수가 서로 다르기 때문에 생성 방법 또한 다양하다.
- render target view : md3dDevice->CreateRenderTargetView(~)
- depth stencil view : md3dDevice->CreateDepthStencilView(~)
각 view의 생성방법에 대해서는 별도로 작성하겠으나, 대략적인 프로세스는 아래와 같다.
즉, 전반적으로 아래의 과정을 따른다 볼 수 있다.
- ID3D12_DESCRIPTOR_HEAP_DESC를 정의한다.
- ID3D12Device::CreateDescriptorHeap를 호출하여 descriptor heap을 생성한다.
- 생성할 descriptor에 대한 view_desc 구조체를 정의한다.
- descriptor heap의 handle과 view_desc, buffer를 참조하여 Create~~를 호출한다.
생성과 함께
- 자신이 서술할 resource의 주소
- 자신이 저장될 heap의 handle
2가지 정보가 필요하다.
가령 Depth 버퍼와 view의 생성부터 pipeline에 binding 하기 까지의 일련의 과정을 코드로 대략 작성해보면 아래와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
void Init() {
// 1. 리소스 생성
Microsoft::WRL::ComPtr<ID3D12Resource> mDepthStencilBuffer;
D3D12_RESOURCE_DESC depthStencilDesc;
depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
depthStencilDesc.Alignment = 0;
depthStencilDesc.Width = WIDTH;
depthStencilDesc.Height = HEIGHT;
depthStencilDesc.DepthOrArraySize = 1;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
depthStencilDesc.SampleDesc.Count = 1;
depthStencilDesc.SampleDesc.Quality = 0;
depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
D3D12_CLEAR_VALUE optClear;
optClear.Format = mDepthStencilFormat;
optClear.DepthStencil.Depth = 1.0f;
optClear.DepthStencil.Stencil = 0;
md3dDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&depthStencilDesc,
D3D12_RESOURCE_STATE_COMMON,
&optClear,
IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf()));
// 2. 뷰 생성
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mDsvHeap;
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
dsvHeapDesc.NodeMask = 0;
md3dDevice->CreateDescriptorHeap(
&dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf()));
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc;
dsvDesc.Flags = D3D12_DSV_FLAG_NONE;
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
dsvDesc.Format = mDepthStencilFormat;
dsvDesc.Texture2D.MipSlice = 0;
md3dDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), &dsvDesc, mDsvHeap->GetCPUDescriptorHandleForHeapStart());
}
void Update() {
// 3. binding
mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(mDepthStencilBuffer.Get(),
D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE));
D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = mDsvHeap->GetCPUDescriptorHandleForHeapStart();
mCommandList->ClearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
mCommandList->OMSetRenderTargets(1, &cBackBufferViewHandle, true, &dsvHandle);
}
|
cs |
2. 서술자 핸들
Descriptor는 Descriptor Heap에 저장된다. 흔하진 않지만, 어플리케이션 코드에서 Heap에 저장된 각각의 Descriptor에 직접적으로 접근해야할 상황이 존재한다.
모든 종류의 Descriptor Heap은 ID3D12DescriptorHeap 인터페이스의 인스턴스로써 존재하며(확인필요) 이 인터페이스는 자신의 첫번째 Descriptor를 가리키는 핸들을 반환하는 함수 GetCPUDescriptorHandleForHeapStart를 제공한다.
1
|
D3D12_CPU_DESCRIPTOR_HANDLE GetCPUDescriptorHandleForHeapStart();
|
cs |
각 Descriptor는 이 handle을 통해 접근 가능하며, 이 핸들은 D3D12_CPU_DESCRIPTOR_HANDLE로 대표된다.
1
2
3
|
typedef struct D3D12_CPU_DESCRIPTOR_HANDLE {
SIZE_T ptr;
} D3D12_CPU_DESCRIPTOR_HANDLE;
|
cs |
앞선 함수는 Descriptor Heap 내의 첫번째 Descriptor의 handle을 반환하며, 내부의 다른 요소들에 접근을 하기 위해선 포인터 값을 직접 조종해야한다. 주소값을 이동시키기 위해선 Descriptor의 Increment크기를 알아야 하며, 이는 GetDescriptorHandleIncrementSize 함수로 얻을 수 있다.
1
2
3
|
UINT GetDescriptorHandleIncrementSize(
[in] D3D12_DESCRIPTOR_HEAP_TYPE DescriptorHeapType
);
|
cs |
인수로 크기를 알기 원하는 Descriptor type을 전달하면 된다.
- Descriptor Heap의 시작주소
- Descriptor Increment 크기
위 2가지 정보가 있다면 Descriptor Heap 내의 원하는 index 위치의 Descriptor의 handle을 확보할 수 있다.
가령 RTV Descriptor heap의 index 번째 descriptor의 handle을 구하는 코드는 아래와 같다.
1
2
3
4
5
|
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mRtvHeap;
mRtvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE handle;
handle = mRtvHeap->GetCPUDescriptorHandleForHeapStart() + mRtvDescriptorSize * index;
|
cs |
요약
- resource와 view의 생성방법은 종류마다 상이하지만 전체적인 절차는 아래와 같다.
- Resource
- D3D12_RESOURCE_DESC 구조체를 정의하여 생성될 자원에 대해 서술한다
- 차원, 가로/세로 크기, 밉레벨, 포맷 ....
- D3D12_HEAP_PROPERTIES 구조체를 정의하여 자원이 저장될 heap에 대해 서술한다
- D3D12_CLEAR_VALUE 구조체를 정의하여 리소스를 초기화할 값을 결정한다
- ID3D12Device::CreateCommittedResource를 호출하여 resource를 생성하고 ID3D12Resource에 할당한다.
- D3D12_RESOURCE_DESC 구조체를 정의하여 생성될 자원에 대해 서술한다
- View
- D3D12_DESCRIPTOR_HEAP_DESC를 채운다.
- ID3D12Device::CreateDescriptorHeap를 호출
- Resource
Question
자원생성시 D3D12_RESOURCE_DESC와 D3D12_CLEAR_VALUE의 Format 필드에서 DXGI_FORMAT 형식을 지정 가능하다. 각각의 값이 생성될 resource에 어떤 영향을 주는가?
'computer graphics > DirectX12' 카테고리의 다른 글
12. 다중표본화(multisampling) (0) | 2021.07.04 |
---|---|
8. 교환사슬과 더블버퍼링 (0) | 2021.07.04 |
7. COM (0) | 2021.07.04 |
5. Fence (0) | 2021.07.04 |
2. COM (0) | 2021.07.04 |