2020년 11월 4일 수요일

DirectX11 - Texture Mapping

1. 텍스쳐 생성 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CD3D11_TEXTURE2D_DESC desc;
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = rtv_format;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
 
auto hr = d3d_device->CreateTexture2D(&desc, nullptr, &rtvTexture);
cs

위는 빈 텍스쳐를 생성하는 코드이다.

CreateTexture2D 의 nullptr 이 D3D11_SUBRESOURCE_DATA 가 들어가는 곳으로, 특정 텍스쳐를 생성하고 싶다면 이 자료형에 원하는 데이터를 넣으면 된다.


여기서 Mapping 에 중요한 부분이 Usage 랑 CPUAccessFlags 그리고 BindFlags 이다.


https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_usage

https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_cpu_access_flag

msdn 에 잘 정리되어 있다.


1. CPU READ/WRITE


Mapping 에서 우리가 관심 있는 것은, CPU READ/WRITE, GPU READ/WRITE 이다.

CPU 는 알기 쉬운데, GPU READ/WRITE 는 무엇인가?

일단 가장 많이 쓰이는 것은 shader 의 input / output 이다. 

하지만 이것만 있는 것이 아니라, copy 등의 연산도 gpu 에서 가능하다. 

여기서 우리는 shader 와 그 밖의 용도를 구분해야한다.



그 이유는 위 테이블이 잘 말해준다.

Staging 은 모든 것에서 read/write 가 가능한데, 왜 stage 는 안되는 것인가?

그 이유는 shader 의 input/output 은 안되지만 copy 등의 연산은 가능하기 때문이다.


그렇기 때문에 Dynamic 은 cpu -> gpu 로 Shader 에 cpu 로 데이터를 보낼 때 쓰이고,

Staging 은 만든 후에 Dynamic/Default/Immutable 의 데이터에서 복사한 후 쓰인다. 


이러한 Shader 에서의 사용은 BindFlags 에서 지정한다.

D3D11_BIND_SHADER_RESOURCE 가 input, D3D11_BIND_RENDER_TARGET 가 output 이다.

이것 외에도 다른 플래그도 Staging 의 다른 용도의 input 또는 output 을 설정한다.


CPUAcessFlags 는 CPU_READ/WRITE 두개 밖에 없고 Usage 에 맞춰서 넣어주면 된다.  


 

2. MAPPING

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
d3d_context->CopyResource(copy, texture);
 
D3D11_MAPPED_SUBRESOURCE data;
HRESULT hr = d3d_context->Map(rtvcopy, 0, D3D11_MAP::D3D11_MAP_READ, 0&data);
{
    UCHAR* pTexels = (UCHAR*)data.pData;
    uint width = desc.width;
    uint height = desc.height;
    uint pixelSize = bitperpixel / 8;
    uint index = 0;
 
    for(int row = 0 ; row < height ; row++)
    {
        for(int col = 0 ; col < width ; col++)
        {
            uint index = row * data.RowPitch + col * 4;
            _rgba = pTexels[index] | pTexels[index+1<< 8 | pTexels[index+2<< 16 | pTexels[index+3<< 24;
        }
    }
 
}
d3d_context->Unmap(copy, 0);
cs

위 코드는 texture 라는 Texture2D 에서 copy 로 복사 후, 데이터를 읽는 코드이다.

copy 는 당연히 cpu 에서 읽고 있으므로 Usage 는 Staging 이어야 한다.


CopyResource 함수가 device context 에서 실행되는 것을 보자.

위에서 말한대로 gpu 에서 copy 가 실행되는 것을 보여준다. 


그렇게 복사한 copy 를 가지고 device context 는 Map, UnMap 을 실행한다.

Map - UnMap 사이에서 그 데이터가 gpu 에서 사용되지 못하도록 Lock 이 걸린다. 

그 상태에서 우리는 cpu 에서 데이터를 사용할 수 있는 것이다.


그렇게 Lock 이 걸린 데이터는 D3D11_MAPPED_SUBRESOURCE 자료형으로 나온다.

그 자료형은 pData, RowPitch, DepthPitch 로 이루어져 있다.

pData 는 원하는 데이터의 시작 주소를, RowPitch/DepthPicth 는 byte 단위 크기를 말한다.


그런데 그 크기에는 약간의 패딩이 있어서, 우리가 지정한 텍스쳐의 크기와는 약간 다르다.

지정한 크기가 desc.width, desc.height 에 있다고 하면,

desc.width < RowPitch  ( padding 이 들어감 )

desc.height = DepthPitch / desc.width 

이런 상황이기 때문이다.


그래서 우리가 mapped data 의 인덱스를 조사하기 위해선 원래의 width, height 가 필요하다.

padding 이 들어간건 row 마다 있기 때문에, row, col 이 원하는 텍스쳐의 좌표라면

uint index = row * data.RowPitch + col * 4;

이렇게 원하는 픽셀의 시작 인덱스를 구할 수 있다.



List