Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
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
Archives
Today
Total
관리 메뉴

codingfarm

6. 자원, 서술자 본문

computer graphics/DirectX12

6. 자원, 서술자

scarecrow1992 2021. 7. 4. 15:09

자원과 서술자에 대해서 알고싶다면 크게 4가지 요소에 대해서 집중하여 살펴보면된다.

  1. 자원의 생성
  2. 서술자의 생성
  3. 데이터 업로드
  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 생성시 만들어짐

 

일반적인 자원 생성 방법

  1.  D3D12_RESOURCE_DESC 구조체를 정의하여 생성될 자원에 대해 서술한다
  2. D3D12_HEAP_PROPERTIES 구조체를 정의하여 자원이 저장될 heap에 대해 서술한다
  3. D3D12_CLEAR_VALUE 구조체를 정의하여 리소스를 초기화할 값을 결정한다
  4. 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는 자원서술자를 통해 여러 기능들을 수행한다.
    1. 자원의 실제 자료에 접근
    2. 자료를 사용하는데 필요한 정보(resource, mip-map...) 획득
  • 존재 이유
    • GPU 자원은 범용적인 메모리 조각에 불과하므로 GPU나 프로그래머는 서로의 data의 용도를 알 방법이 없음
    • 자원 자체는 자신의 용도(렌더 대상, 깊이 스텐실 버퍼, 셰이더 자원...)에 대한 정보를 담지 않으므로
    • 자원자료의 지정 및 자원을 GPU에게 서술하기 위해 존재
      • ex
        1. 동일한 자원일지라도 pipeline의 다른 단계들에서 쓰기 위해
          • 자원 자체에는 사용 목적에 대해 명시되어 있지 않으므로
            • 가령 texture를 렌더 대상 및 shader 자원으로 사용
        2. 자원의 일부 영역만 rendering pipeline에 묶고 싶을때
          • 자원자체에는 영역에 관한 정보가 없으므로
        3. 자원을 무형식으로 생성하기 위해
          • GPU는 자원의 형식을 모르므로

 서술자는 Direct3d에게 자원의 사용법을 설명해준다. (사용법 : 자원을 pipeline 어떤 단계에 묶을지)

  • 서술자는 자원의 사용법에 따라 여러 종류(형식)가 있다.
    1. CBV/SRV/UAV 서술자들 : 각각 상수 버퍼(constant buffer), 셰이더 자원(shader resource), 순서 없는 접근(unordered access view)을 서술
    2. 표본추출기 서술자 : 텍스처 적용에 쓰이는 표본 추출기(sampler) 자원을 서술
    3. RTV 서술자 : 렌더 대상(render target) 자원을 서술
    4. 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 생성

  1. D3D12_DESCRIPTOR_HEAP_DESC를 채운다.
  2. 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의 생성방법에 대해서는 별도로 작성하겠으나, 대략적인 프로세스는 아래와 같다.

즉, 전반적으로 아래의 과정을 따른다 볼 수 있다.

  1. ID3D12_DESCRIPTOR_HEAP_DESC를 정의한다.
  2. ID3D12Device::CreateDescriptorHeap를 호출하여 descriptor heap을 생성한다.
  3. 생성할 descriptor에 대한 view_desc 구조체를 정의한다.
  4. descriptor heap의 handle과 view_desc, buffer를 참조하여 Create~~를 호출한다.

 

생성과 함께

  1. 자신이 서술할 resource의 주소
  2. 자신이 저장될 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, 00, 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
      1. D3D12_RESOURCE_DESC 구조체를 정의하여 생성될 자원에 대해 서술한다
        • 차원, 가로/세로 크기, 밉레벨, 포맷 ....
      2. D3D12_HEAP_PROPERTIES 구조체를 정의하여 자원이 저장될 heap에 대해 서술한다
      3. D3D12_CLEAR_VALUE 구조체를 정의하여 리소스를 초기화할 값을 결정한다
      4. ID3D12Device::CreateCommittedResource를 호출하여 resource를 생성하고 ID3D12Resource에 할당한다.
    • View
      1. D3D12_DESCRIPTOR_HEAP_DESC를 채운다.
      2. ID3D12Device::CreateDescriptorHeap를 호출

 

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
Comments