Search

'DirectX 11'에 해당되는 글 51건

  1. 2011.01.14 [알콜코더의 미리 배워보는 DX11-입문편] 1.튜터리얼01:백버퍼의 설정 #1 4
  2. 2010.12.29 [알콜코더의 미리배워보는 DX11-입문편] 1. 튜터리얼01 : 디바이스와 스왑체인의 생성 4
  3. 2010.12.28 [알콜코더의 미리 배워보는DX11 입문편] DirectX 11의 특징들 3
  4. 2010.11.24 [JumpToDX11-18] DirectX11의 테셀레이션 ( 테셀레이션을 위한 하드웨어의 등장편 ) 2
  5. 2010.11.13 [발표자료] 예제로 느껴보는 다이렉트X 11의 매력 3
  6. 2010.10.11 [JumpToDX11-17] DirectX9 세대의 테셀레이션( ATI 라이브러리편 ) 2
  7. 2010.10.07 [알콜코더의 미리 배워보는 DX11 – 입문편] DX11에서 무엇이 추가되었나? 4
  8. 2010.10.07 [JumpToDX11-16] DirectX9 세대의 테셀레이션( D3DXTessellateNPatches편 )
  9. 2010.09.20 [알콜코더의 미리 배워보는 DirectX11-입문편] 1.튜터리얼 01 : 다이렉트 3D 기초 #2
  10. 2010.09.08 [알콜코더의 미리 배워보는 DirectX11-입문편] 1.튜터리얼 01 : 다이렉트 3D 기초 #1 11
  11. 2010.08.31 [알콜코더의 미리 배워보는 DirectX 11 - 입문편] 0. 누구를 위한 연재인가 15
  12. 2010.08.26 [JumpToDX11-15] DirectX9 세대의 테셀레이션( IDirect3DDevice9::DrawXXXPatch편 )
  13. 2010.07.19 [JumpToDX11-14] DirectX9 세대의 테셀레이션( ID3DXPatchMesh 편 )
  14. 2010.06.24 [DX11_#5]DirectX11의 활용 사례(1/3) 4
  15. 2010.06.15 [JumpToDX11-13] Tessellation 등장.
  16. 2010.04.26 [JumpToDX11-12] DirectCompute 의 절차.
  17. 2010.02.11 [JumpToDX11-11] DirectCompute 를 위한 한걸음!
  18. 2010.01.27 [JumpToDX11-10] GPGPU 를 위한 DirectCompute. 2
  19. 2010.01.11 [JumpToDX11-9] Multi-threaded Rendering 을 위한 API.
  20. 2009.12.02 [JumpToDX11-8] Deferred Contexts


참고 소스 : DirectX SDK – DirectX 11 Tutorial 02

위 소스를 기반으로 연재를 진행합니다.

연재외에 자세한 내용은 소스와 DX SDK 문서를 참조하시면 도움이 됩니다.

 

안녕하세요. 알콜코더 민군입니다. ^^

지난 연재에서는 DirectX11의 3D 그래픽 디바이스를 생성하는 방법을 배웠습니다. 하지만 3D 디바이스는 어디까지나 그래픽 카드 드라이버의 디바이스의 인터페이스일뿐, 실제로 게임 화면 내용을 렌더링하는 인터페이스는 아닙니다. 실제 게임 화면은 디바이스에 설정된 렌더타겟에 렌더링 됩니다. 저번 연재 내용에서 디바이스와 함께 스왑체인을 생성했기 때문에, 스왑체인의 백버퍼도 당연히 같이 생성이 되었지만, Direct3D의 렌더타겟으로는 아직 설정되지는 않았습니다. 그래서 생성된 스왑체인으로부터 백버퍼를 얻어와서 디바이스의 렌더타겟으로 설정해야만 합니다. 그리고 깊이/스텐실 버퍼를 사용하는 경우에는 렌더타겟의 설정과 동시에 깊이/스텐실 버퍼도 설정합니다.

 

이번 연재에서는 이렇게 실제로 렌더링을 하기위한 렌더타겟으로 사용하기 위해서 백버퍼를 설정 하는 방법에 대해서 이야기 하겠습니다.

 

 


 

 

 

버퍼를 렌더타겟으로 설정하기 위해서는, 우선 버퍼의 '렌더타겟 뷰(RenderTarget View)'를 작성하여, 그 뷰를 디바이스의 렌더타겟으로 설정해야 합니다. 렌더타겟 뷰에는 'ID3D11RenderTargetView 인터페이스'를 사용합니다.


 


우선 스왑체인으로부터 백버퍼의 포인터를 받아와야 합니다. 스왑체인으로부터 백버퍼를 가져오기 위해서는'IDXGISwapChain::GetBuffer()' 메소드를 사용합니다.

다음의 코드에서는 2D텍스쳐(ID3D11Texture2D 인터페이스)로서 백버퍼를 가져옵니다. 백버퍼는 기본적으로2D텍스쳐로 만들어져 있기 때문입니다.

 


 

HRESULT GetBuffer(

[in]       UINT Buffer,

[in]       REFIID riid,

[in, out]  void **ppSurface

);

Buffer

접근할 버퍼의 번호(인덱스)

Riid

백버퍼를 받을 인터페이스의 타입

ppSurface

반환된 인터페이스를 받을 변수의 포인터

 


텍스쳐는 파이프라인으로부터 '뷰(View)'를 통해서 접근이 가능합니다. 렌더타겟도 텍스쳐의 일종이기 때문에 역시 뷰를 이용하여 접근해야 합니다. 렌더타겟에 접근할 때는 '렌더타겟 뷰'를 사용하여 접근할 수 있습니다.

렌더타겟 뷰는 'ID3D11Device::CreateRenderTargetView 메소드'를 이용해서 생성할 수 있으며, 얻어온 렌더타겟 뷰는 'ID3D11RenderTargetView 인터페이스'로서 사용됩니다.

첫번째 인자는 뷰에서 액세스하는 리소스, 두번째 인자는 렌더타겟 뷰의 정의, 3번째 인자는 반환된 렌더타겟뷰의 포인터를 받을 변수의 포인터를 넘겨줍니다.

 

렌더타겟 뷰의 정의는 'D3D11_RENDER_TARGET_VIEW_DESC 구조체'로 정의됩니다. 하지만 일반적인 경우에는 디폴트값으로 NULL을 넘겨주면 됩니다.

 

렌더타겟 뷰가 생성된 이후에는 직접 백버퍼의 포인터에 접근하지 않고, 이 렌더타겟 뷰를 사용합니다. 그렇기 때문에 렌더타겟 뷰의 인터페이스를 얻고 난 이후에는 백버퍼의 포인터를 해제합니다.


 

HRESULT CreateRenderTargetView(

[in]   ID3D11Resource *pResource,

[in]   const D3D11_RENDER_TARGET_VIEW_DESC *pDesc,

[out]  ID3D11RenderTargetView **ppRTView

);

pResource

렌더타겟 뷰에서 엑세스하는 리소스

pDesc

렌더타겟 뷰를 정의하는 'D3D11_RENDER_TARGET_VIEW_DESC'구조체의 포인터. NULL을 넘겨주면, 리소스가 만들어졌을때의 포맷을 그대로 사용하며, 모든 리소스의 밉맵 레벨 0에 액세스하는 뷰를 생성한다.

ppRTView

ID3D11RenderTargetView 인터페이스를 받는 변수의 포인터. NULL을 넘겨주게 되면, 다른 인자들의 유효성을 체크할 수 있다.

 

typedef struct D3D11_RENDER_TARGET_VIEW_DESC {

DXGI_FORMAT         Format;

D3D11_RTV_DIMENSION ViewDimension;

union {

D3D11_BUFFER_RTV        Buffer;

D3D11_TEX1D_RTV         Texture1D;

D3D11_TEX1D_ARRAY_RTV   Texture1DArray;

D3D11_TEX2D_RTV         Texture2D;

D3D11_TEX2D_ARRAY_RTV   Texture2DArray;

D3D11_TEX2DMS_RTV       Texture2DMS;

D3D11_TEX2DMS_ARRAY_RTV Texture2DMSArray;

D3D11_TEX3D_RTV         Texture3D;

} ;

D3D11_RENDER_TARGET_VIEW_DESC;

Format

리소스내의 데이터를 해석하는 포맷. 'DXGI_FORMAT_UNKNOWN'을 넘겨주면, 리소스에 설정되어 있는 포맷이 사용된다.

ViewDimension

리소스가 액세스 되는 방법. 이 값에 의해서 아래의 union의 멤버 가운데, 사용할 변수가 결정된다.

Buffer

버퍼 리소스로 접근하는 경우의 정의

(ViewDemension = D3D11_RTV_DIMENSION_BUFFER)

Texture1D

1D텍스쳐로 접근하는 경우의 정의

(ViewDemension = D3D11_RTV_DIMENSION_TEXTURE1D)

Texture1DArray

1D텍스쳐의 배열로 접근하는 경우의 정의

(ViewDemension = D3D11_RTV_DIMENSION_TEXTURE1DARRAY)

Texture2D

2D텍스쳐로 접근하는 경우의 정의

(ViewDemension = D3D11_RTV_DIMENSION_TEXTURE2D)

Texture2DArray

2D텍스쳐의 배열로 접근하는 경우의 정의

(ViewDemension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY)

Texture2DMS

멀티샘플링된 2D텍스쳐로 접근하는 경우의 정의

(ViewDemension = D3D11_RTV_DIMENSION_TEXTURE2DMS)

다만, 멀티샘플링된 2D텍스쳐는 보통 1개의 서브 리소스를 가지고 있기 때문에, 설정은 필요없다.

Texture2DMSArray

멀티샘플링된 2D텍스쳐의 배열로 접근하는 경우의 정의

(ViewDemension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY)

Texture3D

3D텍스쳐로 접근하는 경우의 정의

(ViewDemension = D3D11_RTV_DIMENSION_TEXTURE2D)

각 구조체의 내용에 대해서는 MSDN을 참조하시면 됩니다. 하지만 지금은 딱히 별로 건드릴만한 내용은 없습니다. ^^

 


렌더타겟은 'Ouput-Merger(OM)' 스테이지에 'ID3D11DeviceContext::OMSetRenderTargets 메소드'로 설정합니다.

그리고 렌더타겟은 DX11에서 동시에 최대 8개까지 설정이 가능합니다. 그러나 그럴 경우에도 설정할 수 있는 깊이/스텐실 버퍼는 하나뿐입니다.

 

'OMSetRenderTargets 메소드'에서는 렌더타겟 뷰와 함께 깊이/스텐실 버퍼도 동시에 같이 설정합니다. 이 예제에서는 깊이/스텐실 버퍼를 사용하지 않기 떄문에 NULL을 설정합니다. 깊이/스텐실 버퍼에 관해서는 다음 연재에서 다루겠습니다. ^^;

 

이 메소드는 넘겨진 인터페이스의 참조를 유지합니다. 이것은 'Direct3D 10'과 달라진 부분입니다.

 

 

void OMSetRenderTargets(

[in]  UINT NumViews,

[in]  ID3D11RenderTargetView *const **ppRenderTargetViews,

[in]  ID3D11DepthStencilView *pDepthStencilView

);

NumViews

설정하는 렌더타겟의 개수. 최대 8개까지 설정가능하다.

ppRenderTargetViews

렌더링 파이프라인에 설정하는 렌더타겟 뷰의 배열.

NULL을 넘기면, 렌더타겟이 설정되지 않게 된다.

pDepthStencilView

렌더링 파이프라인에 넘겨주는 깊이/스텐실 뷰의 포인터.

NULL을 넘기면, 깊이/스텐실 버퍼가 설정되지 않게 된다.




알콜코더 민군

언젠가 우즈벡에 미소녀 게임 회사를 차리고 싶은 개발자

3D게임 클라이언트 프로그래머, 살짝 오타쿠…

드래곤볼 온라인 개발, 현재는 네오위즈 서식중

'신입게임 개발자의 서울상경기'와 '초중급 게임개발자 스터디' 운영중

 

 

 

 

 

 

참고 소스 : DirectX SDK – DirectX 11 Tutorial 01

위 소스를 기반으로 연재를 진행합니다.

연재외에 자세한 내용은 소스와 DX SDK 문서를 참조하시면 도움이 됩니다.

 

안녕하세요. 알콜코더 민군입니다. ^^

그동안 연재를 본의 아니게 많이 쉬었습니다. 거의 두달만에 다시 연재를 재개하는 것 같네요..OTL

회사 프로젝트가 바쁜데다가 여러 가지 발표와 행사가 겹쳐서 너무 오래 연재를 쉰 것 같네요. 그럴리는 없겠지만… 혹시나 이런 허접한 연재를 기다리신 분들에게는 죄송합니다. T^T 앞으로는 더욱 열심히 자주 업데이트 하도록 하겠습니다. ^^;;

이번 연재에서는 저번 연재 마지막에 언급했던 DX11 디바이스와 스왑 체인을 생성하는 방법에 대해서, 좀더 자세히 알아볼까 합니다. DX를 이용한 모든 프로그램에서는 DX 디바이스를 생성하는 것이 가장 기본이 됩니다. 그전까지의 과정들은 전부 디바이스를 생성하기 위한 준비단계에 해당합니다. 그리고 DX10 버전부터는 DXGI 기반 구조로 변경되면서 스왑체인(SwapChain)과 디바이스 컨텍스트를 생성해야 합니다. 스왑체인에 대한 설명은 이전 연재를 참조하시면 됩니다.

 

 

디바이스와 스왑체인의 생성

 

디바이스와 스왑체인은 'D3D11CreateDeviceAndSwapChain' 함수를 이용해서 동시에 생성합니다.

이때 실제 렌더링에 사용하는 백버퍼인 Device Context 역시 함께 생성됩니다.


 

함수는 위와 같은 형태로 사용됩니다. 들어가는 인자들이 꽤 많죠. ^^

DX11의 가장 기본이자 핵심인 함수이기 때문에 세팅을 해야하는 내용들이 꽤 많습니다.

이제부터 하나하나 들어갈 인자들을 설명해 드리겠습니다.

여기에 들어가는 인자들을 만드는 방법은 이전 연재를 참고하시면 됩니다.

 

함수 원형 및 인자들에 대한 설명은 아래와 같습니다.


HRESULT D3D11CreateDeviceAndSwapChain(

__in   IDXGIAdapter               *pAdapter,

__in   D3D_DRIVER_TYPE      DriverType,

__in   HMODULE                     Software,

__in   UINT                                Flags,

__in   const D3D_FEATURE_LEVEL *pFeatureLevels,

__in   UINT                               FeatureLevels,

__in   UINT                               SDKVersion,

__in   const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,

__out  IDXGISwapChain        **ppSwapChain,

__out  ID3D11Device             **ppDevice,

__out  D3D_FEATURE_LEVEL *pFeatureLevel,

__out  ID3D11DeviceContext **ppImmediateContext

);

pAdapter 

표시할 '디스플레이 디바이스(비디오 카드)'의 'IDXGIAdapter 인터페이스'를 설정합니다. 이것은 DXGI의 인터페이스입니다. NULL을 지정하면 최초에 발견한 디바이스를 사용합니다. 비디오카드를 지정할 필요가 있을때를 제외하고는 기본적으로 NULL로 설정하면 됩니다

DriverType 

생성할 DX11 디바이스의 종류를 지정합니다. 보통은 하드웨어 가속을 사용하기 위해서 'D3D_DRIVER_TYPE_HARDWARE'를 지정합니다. pAdapter에 NULL 이외의 값을 지정한 경우에는'D3D_DRIVER_TYPE_UNKNOWN'을 지정합니다.

Software 

소프트웨어 래스터라이저가 구현되어 있는 DLL이 핸들을 지정합니다.보통은 NULL을 설정하면 됩니다.

Flags 

사용할 DX11의 API 레이어를 D3D_CREATE_DEVICE_FLAG 값들을 조합하여 설정합니다.

pFeatureLevels

피처레벨(지원 기능 레벨) 배열을 설정합니다. 우선 순위가 높은 순서대로 배열을 채웁니다. 만약 NULL로 설정하면 DX11->DX10->DX9 순으로 모든 레벨을 설정합니다.

FeatureLevels 

위에서 설정한 피처레벨 배열의 개수를 입력합니다

SDKVersion

사용하고 있는 DX SDK의 버전을 넘겨줍니다.

pSwapChainDesc 

스왑체인의 설정값들을 저장한 구조체의 포인터를 넘겨줍니다.

ppSwapChain 

생성된 스왑체인 인터페이스의 포인터를 담을 변수를 설정합니다.

ppDevice 

생성된 디바이스 인터페이스의 포인터를 담을 변수를 설정합니다.

pFeatureLevel 

생성에 성공한 경우에는 pFeatureLevels에서 지정했던 배열의 처음값을 돌려줍니다. 실패한 경우에는 0이 반환됩니다.

ppImmediateContext 

생성된 디바이스 컨텍스트 인터페이스의 포인터를 담을 변수를 설정합니다.

 

D3D_DRIVER_TYPE

D3D_DRIVER_TYPE_HARDWARE

하드웨어 드라이버 (HAL 드라이버)

D3D_DRIVER_TYPE_REFERENCE 

레퍼런스 레스터라이저 (REF 드라이버)

D3D_DRIVER_TYPE_NULL 

NULL 디바이스, 렌더링이 불가능한 레퍼런스 드라이버

D3D_DRIVER_TYPE_SOFTWARE 

예약만 되어있음. 사용하지 않습니다.

D3D_DRIVER_TYPE_WARP 

고속 퍼포먼스의 소프트웨어 레스터라이저(WARP). 피처레벨 9_1 ~ 10_1을 지원합니다.

D3D_DRIVER_TYPE_UNKNOWN 

종류 불명

 

D3D11_CREATE_DEVICE_FLAG

D3D11_CREATE_DEVICE_SINGLETHREADED

싱글 스레드 지원의 디바이스를 지정. 억지로 스레드 세이프 레이어를 무효로 하고 싶은 경우에 지정합니다.

D3D11_CREATE_DEVICE_DEBUG

디버그 레이어를 사용합니다.

D3D11_CREATE_DEVICE_SWITCH_TO_REF

레퍼런스 타입으로 교체할 수 있는 레이어를 사용합니다.

D3D11_CREATE_DEVICE_PREVENT_

INTERNAL_THREADING_OPTIMIZATIONS

복수의 스레드가 만들어지지 않도록 한다. 일반적으로는 사용되지 않습니다.

D3D11_CREATE_DEVICE_BGRA_SUPPORT

Direct2D 와 Direct3D 리소스의 상호운영을 가능하게 합니다.

 

 

디버그용 디바이스

 

'D3D11CreateDeviceAndSwapChain' 함수의 3번째 인자에는 D3D_CREATE_DEVICE_FLAG 의 조합이 들어가게 됩니다. 이 조합된 플래그값으로 게임에서 사용할 Direct3D11의 API 레이어를 지정할 수 있습니다. 여기서 디버그용 디바이스 플래그도 설정이 가능합니다. 아래와 같이 디버그용 버전에서는 디버그 레이어를 켜두면 여러가지 디버그 정보들을 확인할 수 있습니다.


위와 같이 설정하고 D3D11CreateDeviceAndSwapChain 함수의 3번째 인자에 위 플래그를 설정해주면, 디버그 버전으로 컴파일 되는 경우에만 디버그 레이어를 사용할 수 있습니다. 디버그 레이어를 사용 가능하게 해두면 프로그램의 성능이 떨어지기 때문에, 릴리즈에서는 사용하면 안됩니다.

 

 

WARP 디바이스의 생성

 

DX11에서는 하드웨어 드라이버가 사용할 수 없는 환경에서도, 높은 퍼포먼스의 소프트웨어 레스터라이저인 WARP(D3D_DRIVER_TYPE_WARP) 디바이스를 사용할 수 있습니다. 이전 DX 버전에서는 하드웨어가 지원되지 않는 경우 100% 소프트웨어 레스터라이저인 레퍼런스 디바이스를 사용해야 했는데, DX11과 윈도우 비스타 이상에서는 윈도우의 기본적인 아키텍쳐가 하드웨어 가속이 지원 되기 때문에 이 WARP 디바이스를 사용하면 훨씬 더 고속의 렌더링을 할 수 있습니다. 즉, 프로그램이 실행되는 환경의 그래픽 카드가 피처레벨이 지원되지 않는 그래픽카드라고 할지라도, DX11 SDK로 WARP 디바이스를 생성하면 DX11 프로그램을 동작시킬 수 있습니다. 하지만 WARP 디바이스가 지원하는 피처레벨은 'D3D_FEATURE_LEVEL_9_1' ~ 'D3D_FEATURE_LEVEL_10_1' 까지입니다.

WARP 디바이스는 윈도우 비스타이상에서만 사용 가능합니다.


 

 

레퍼런스 디바이스의 생성

 

앞서 이야기한 WARP는 고속이긴 하지만, DX10 버전의 기능들까지만 지원하기 때문에 DX11의 기능들은 사용할 수가 없습니다. 하지만 프로그램을 개발할 때 DX11 지원 그래픽카드가 없는 환경에서, DX11의 모든 기능을 테스트 해보기 위해서는 DX11의 모든 기능들이 구현되어 있는 레퍼런스 디바이스를 사용해야 합니다. 주의할 점은 레퍼런스 드라이버는 'DirectX SDK'가 인스톨되어있는 환경에서만 사용 가능합니다.

튜터리얼의 소스 코드에서는 DX11을 지원하지 않는 그래픽카드를 위해서 '하드웨어 디바이스 -> WARP 디바이스 -> 레퍼런스 디바이스' 순으로 생성을 해보는 코드가 포함되어 있습니다. 그래서 만약 성공했다면 그 디바이스를 사용하고, 만약 앞의 단계에서 생성에 실패했다면 다음 단계의 디바이스를 생성해 보게 됩니다. 그래서 DX11을 지원하지 않는 그래픽 카드를 가지고 있어도 샘플 코드의 실행이 가능합니다. 만약 윈도우 비스타 이하 버전에 DX11을 지원하지 않는 그래픽카드를 사용하고 있다면, WARP를 사용할 수 없기 때문에 레퍼런스 디바이스가 생성됩니다. (물론 DX11 지원 그래픽 카드라면 당근 하드웨어 디바이스가 생성되겠죠)


 

 

다음 시간에는…

 

다음 연재에서는 위와 같이 방법으로 생성한 스왑체인으로 백버퍼를 가져와서, 렌더 타겟 뷰를 생성하고, 생성된 렌더타겟뷰를 디바이스 컨텍스트에 설정하는 방법을 다루겠습니다. 즉, 실제로 게임 화면을 렌더링하는 백버퍼를 생성하는 단계에 대해서 이야기 해보겠습니다. ^^

 

Ps. 조금이라도 도움이 되셨다면, 응원의 리플을~~!!! 굽신~굽신~~

 

알콜코더 민군

언젠가 우즈벡에 미소녀 게임 회사를 차리고 싶은 개발자

3D게임 클라이언트 프로그래머, 살짝 오타쿠…

드래곤볼 온라인 개발, 현재는 네오위즈 서식중

'신입게임 개발자의 서울상경기' '초중급 게임개발자 스터디' 운영중

 

[알콜코더의 미리 배워보는DX11 입문편] DirectX 11의 특징들

DirectX 11 2010. 12. 28. 22:01 Posted by 알 수 없는 사용자


안녕하세요. 알콜코더 민군입니다. ^^

그동안 연재를 한동안 못하고 있었습니다. 에궁.

회사의 게임 개발일이 바쁘다보니 그동안 시간을 제대로 못냈네요.

죄송합니다.. 앞으로 열심히 하겠습니다. T^T

 

그동안 연재가 많이 끊겨서, 그전의 내용의 복습겸해서…

이번 연재에서는 DX 11의 특징에 대해서 간략하게 정리하였습니다.

그동안 잊고 있엇던(?) 내용들을 다시 한번 새롭게 정리하는 기회가 되시길 바랍니다. ^^

 

DirectX 11의 특징    

Direct3D 11은 윈도우 비스타, 윈도우 7에서 지원되는 Direct 3D의 최신 버전입니다. 기능의 확장, 퍼포먼스의 개선, GPU 메모리의 완전한 추상화, GPGPU 지원 기능, 등 보다 유연하고 뛰어난 기능들이 심플한 API셋트로 제공됩니다.

Direct3D 11은 Direct3D 9까지의 Direct3D와는 하드웨어 레벨부터 소프트웨어 레벨까지 전부 근본적으로 변경되었습니다. Direct3D 10을 확장한 구조로 되어 있어서, Direct3D 10에서의 개발 이행이 용이합니다.

 

  • 컴퓨트 셰이더 (Compute Shader)

    OS나 일반 어플리케이션 프로그램 등을 실행하는 범용 프로세서인 CPU에 대해서, 그래픽스 처리에 특화된 전용 프로세서를 일반적으로 GPU라고 말합니다만, Direct3D 11에서 지원되는 [컴퓨트 셰이더](Compute Shader)을 사용하면, 그래픽스 처리 이외에도, 데이터를 병렬 처리하는 범용적인 프로세서로 GPU를 활용하는 것이 가능합니다.

    컴퓨트 셰이더는 Direct3D 디바이스를 통해서 그래픽스계열 셰이더와 리소스를 공유 가능합니다만, 다른 셰이더와는 직접 연결이 불가능합니다.

     

    테셀레이션(Tessellation)

    테셀레이션은 디테일이 낮은 모델을 분할하여, 보다 세밀한 프리미티브를 생성하여 출력하는 기능입니다.

    Direct3D 11의 그래픽스 파이프라인에는 테셀레이션을 수행하는 새로운 하나의 스테이지와 2개의 셰이더가 추가 되어서, 리얼타임에서 테셀레이션의 실행이 가능합니다.

     

    멀티스레드 렌더링

    Direct3D 11에서는 멀티스레드의 대응이 강화되어, Direct3D 10의 [디바이스](ID3D10Device 인터페이스)의 기능은 [디바이스](ID3D11Device 인터페이스)와 [디바이스 컨텍스트](ID3D11DeviceContext 인터페이스)로 분할 되었습니다.

    [디바이스 컨텍스트]에서는 [이미디어트 컨텍스트](Immediate Context)와 [디퍼트 컨텍스트](Deferred Context)가 있습니다.

    이미디어트 컨텍스트는 디바이스에 직접 렌더링하는 디바이스 컨텍스트이며, 디바이스에 하나가 존재합니다. 싱글 스레드의 어플리케이션은 이미디어트 컨텍스트만을 사용합니다.

    디퍼드 컨텍스트는 메인 렌더링 스레드 이외의 워커 스레드로 사용되는 디바이스 컨텍스트입니다.

    또한 [디바이스](ID3D11Device 인터페이스)의 메소드는 멀티 스레드에 안전하게 되어있지만, [디바이스 컨텍스트](ID3D11DeviceContext 인터페이스)의 메소드는 프리 스레드화 되어 있지 않습니다.

     

    동적 셰이더 링크

    일반적인 렌더링에서는 각종 마테리얼을 여러가지로 조합하면 렌더링을 하게 됩니다. 그 때문에 각각의 렌더링마다 서로 다른 셰이더가 필요하게 됩니다.

    이러한 셰이더를 작성하는 방법으로는 "(1)모든 기능을 포함하고 있는 셰이더를 만드는 방법" 과 "(2) 필요한 조합합의 셰이더만을 만든느 방법"이 있습니다만, (1)의 방법에서는 셰이더의 퍼포먼스가 희생이 되고, (2)의 방법에서는 셰이더의 조합이 조금씩 늘어나는 것만으로 상당히 많은 양의 세이더를 처리할 필요가 생깁니다.

    Direct3D 11에 도입된 '동적 셰이더 링크'를 사용하면, 셰이더를 파이프라인에 분할 할당하여, 셰이더 코드를 드라이버에서 최적화가 가능하게 되었습니다.

     

    WARP(Windows Advanced Rasterizer Platform)

    WARP는 실제 어플리케이션에서 이용 가능한, 고속의 '멀티 코어 스케일링 래스터라이저'입니다. Direct3D 10.1 레벨의 기능을 지원하는 어플리케이션에서, 지원 가능한 하드웨어를 찾지 못하는 경우에 WARP 디바이스를 선택합니다.

    비슷한 기능으로, Direct3D의 모든 기능을 소프트웨에서 구현했던 '레퍼런스 레스터라이저'가 있습니다만, 이 것은 실행 속도가 상당히 느리고, 개발용으로 밖에 사용할 수 밖에 없다는 한계가 있습니다.

     

    Direct3D 9/10/10.1 레벨의 하드웨어를 지원

    Direct3D 10을 사용한 프로그램을 실행하기 위해서는 Direct3D 10을 지원하는 그래픽스 하드웨어가 필요했었습니다. 그러나 Direct3D 11에서는 6단계의 '기능 레벨'(Feature Level)을 정의하여서, Direct3D 9/10/10.1의 디바이스에서도 대응하는 '기능 레벨'에 맞게 DirectX 11의 기능을 사용한 프로그램이 실행 가능합니다.

     

    세이더 모델 5.0 (Shader Model 5.0)

    Direct3D 11에서는 Direct3D 10과 동일하게 '통합형 셰이더(Unified Shader) 아키텍쳐'가 채용되었기 대문에, '컴퓨트 셰이더'를 포함한 모든 셰이더를 하나의 HLSL(High Level Language : 고수준 셰이딩 언어)에 작성 가능합니다.

    Direct3D 11의 셰이더 모델은 '셰이더 모델 5.0(SM5.0)'입니다. 또한 셰이더 모델 4.0의 기능도 확장되었습니다.

     

    리소스

    Direct3D 11에서는 '읽고/쓰기 버퍼 (텍스쳐)', '구조화 버퍼', '바이트 어드레스 버퍼', 'Unodered Access Buffer(텍스쳐)'등의 새로운 리소스 타입이 정의되었습니다.

    또한 4GB보다 큰 리소스를 지원하게 되었습니다. (다만, 리소스 인덱스는 32bit 입니다)

     

    Direct3D 10의 특징을 승계

    Direct3D 11은 Direct3D 10을 확장한 설계 형태로 되어 있습니다. 그래서 Direct3D 10의 '디바이스의 소멸 처리가 필요없음', 'CAPS 비트의 폐지', '고정 기능의 폐지', '지오메트리 셰이더'등의 특징을 물려받았습니다.

     

     

    Ps. 다음 시간부터는 그동안 중지되었던 DX11 튜터리얼 연재와 함께 DX11의 기본 개념들에 대한 연재도 같이 진행하겠습니다.

    Ps2. 그동안 연재가 너무 많이 쉬었습니다. 흑.. OTL 다음 연재부터는 빨리 빨리 올리도록 하겠습니다. ^^



                                                                                                                                                             

    언젠가 우즈벡에 게임 회사를 차리고 싶은 오타쿠 개발자

    3D게임 클라이언트 프로그래머

    드래곤볼 온라인 개발, 현재 네오위즈 서식중

    '신입게임 개발자의 서울상경기'와 '초중급 게임개발자 스터디' 운영중





앞선 시간들을 통해서, 우리는 테셀레이션에 대해서 꾸준히 살펴보았습니다.
DirectX9 세대에서부터 테셀레이션을 사용했었으며,
ATI 의 일부 그래픽 카드들은 하드웨어 기반의 테셀레이터를 지원했었습니다.

DirectX11의 테셀레이션 작업은 하드웨어 기반으로 처리됩니다.
즉, 이는 무수히 많은 연산을 처리해서 많은 폴리곤을 화면에 보여주겠다는 하나의 의지입니다.
이들이 강력한 이유는 이전 글인 다음을 참고해 주시기 바랍니다.
http://vsts2010.net/331

요약해 보자면,
이제는 텍스쳐링보다는 폴리곤 갯수를 증가시켜서 퀄리티의 향상을 도모하겠다는 것입니다.
사실 이것에 관해서는 많은 우려와 논란이 많았던 것이 사실입니다.
하지만, 현재의 하드웨어 상황은 이들 테셀레이션 기능을 중심으로 변화하고 있는 것이 사실입니다.
아래는 초창기 ATI의 DirectX11 기반의 하드웨어 구조입니다.



ATI의 경우에는 렌더링 목적에 집중하기 위해서 하나의 테셀레이터와 두개의 래스터라이저로 처리를 했었습니다.
물론 이것은 초기 DirectX11을 지원하는 하드웨어의 경우입니다.

ATI가 이런 테셀레이션 기반의 하드웨어를 출시하자,
상대적으로 후발주자였던 NVIDIA의 경우에는 이 테셀레이터를 더 많이 사용한
DirectX11 기반의 하드웨어를 출시하게 됩니다.




위의 빨간 동그라미 영역에 4개씩 보이는 노란 박스가 모두 테셀레이터입니다.
즉 위의 경우에는 16개의 테셀레이터가 존재합니다.( 4개의 래스터라이져 )
NVIDIA의 이런 과감한(?) 테셀레이터의 지원은 ATI의 향후 대응을 기대하게 만들기도 했습니다.

이런 하드웨어적인 논란은 본 글의 취지와는 맞지 않습니다.
이 글은 이런 현재의 상황에 어떻게 대응하는 API 단계의 개발자들을 주요 대상으로 하기 때문입니다.
즉, 어느 것이 더 효과적인지는 분별하기 어렵습니다.
다만 현재까지 나온 의견을 종합해 본다면,
ATI의 경우에는 테셀레이션과 래스터라이져의 본질적인 기능을 중심으로 설계가 되었고,
NVDIA의 경우에는 테셀레이션과 래스터라이져 외에도 GPGPU 환경의 기능을 더 고려해서 설계가 되었다고 합니다.
( NVIDIA의 경우에는 CUDA라는 GPGPU 플랫폼을 XP세대에서부터 강력히 지원했었습니다.^^ )

테셀레이션은 현재까지도 많은 논란이 있습니다.
그 논란의 중심에는 '빠를까?' 라는 의구심과 '엄청난 양의 연산' 에 대한 우려가 있습니다.
또한 하드웨어 기반의 테셀레이션으로의 패러다임 전환이 쉽지 않은 것도 사실입니다.

실제로 테셀레이션 관련 샘플을 실행시켜보면, GPU의 성능에 굉장히 의존적입니다.( 당연한 얘기겠지만요...^^ )
테셀레이션 샘플들은 DirectX11 기반의 하드웨어에서 그렇게 빠르지도, 또한 느리지도 않습니다.
오히려 일반적인 상황에서는 약간의 성능 저하가 일어날 수도 있으며,
최적화를 잘한 경우에는 테셀레이션 처리가 더 느릴 수도 있습니다.
하지만, 이제 하드웨어는 테셀레이터라는 기능을 장착을 했으며,
앞으로는 테셀레이터 기반으로 최적화하는 것이 더 개발 패러다임에 적합할 것입니다.

당분간 개발 패러다임이 과도기적인 상태를 보이겠지만,
이미 그래픽카드의 발전 방향이 테셀레이터 기반으로 변경되고 있다는 것에 우리는 주목해야 합니다.

[발표자료] 예제로 느껴보는 다이렉트X 11의 매력

DirectX 11 2010. 11. 13. 09:00 Posted by 알 수 없는 사용자

안녕하세요. 알콜코더 민군입니다. 


지난 11월 9일날 양재동에서 있었던 

'C++&게임 개발자를 위한 개발 생산성 및 퍼포먼스 향상 세미나' 에서 제가 발표했던 발표 자료입니다. 


부족한 발표였는데도 참석해 주신분들에게 다시 한번 감사드립니다. 꾸벅. (^^)(__)



[JumpToDX11-17] DirectX9 세대의 테셀레이션( ATI 라이브러리편 )

DirectX 11 2010. 10. 11. 08:30 Posted by 알 수 없는 사용자

오늘은 DX9 세대의 테셀레이션 마지막입니다.
ATI 는 DirectX9를 지원하는 일부 그래픽카드들은 하드웨어 기반의 테셀레이션 작업을 지원합니다.
( HD 2000 시리즈 이후 지원되었다고 합니다. )
이 방법은 왜 DirectX11의 테셀레이션 작업이 강력한지를 이해하는 좋은 출발점이 될 수 있습니다.

이 경우에는그래픽 파이프라인 구조가 다음과 같습니다.




이는 현재 X-BOX 360 에도 동일하게 적용되는 그래픽 파이프라인 구조입니다.
주목할 만한 것은 Tessellator의 위치입니다.
즉, 버텍스 쉐이더( VertexShader ) 스테이지의 앞단계에 위치하고 있습니다.
이 위치는 DX11 세대에서는 버텍스 쉐이더 다음 단계로 변경됩니다.


아래의 그림은 ATI 카드에서 지원되는 DX9 기반의 테셀레이션 작업을 보여줍니다.



DirectX9의 테셀레이션을 위해서 총 3번의 패스를 통과해야 합니다.
즉, 3번의 렌더링 작업이 필요합니다.
이렇게 많은 패스가 필요한 이유는 테셀레이션을 위해서 인접한 정점의 정보가 필요하기 때문입니다.
DX9 의 시대에서는 VertexShader 단계에서 인접한 정점의 정보를 쉽게 확인할 수 있는 방법이 없습니다.
그래서 인접한 정보를 구성하는 단계가 첫번째 패스입니다.

첫번째 패스의 렌더타겟은 백버퍼가 아니라, 텍스쳐입니다.
이 텍스쳐에 정점 정보와 정점의 인덱스를 기록하게 됩니다.
즉, rgb 에는 위치 정보가 기록되며 a 에는 정점의 인덱스 정보가 기록됩니다.

이 때, 주의할 것은 메시가 인덱스 버퍼 기반으로 렌더링 되어지는 경우입니다.
이 경우에는 인덱스 버퍼를 모두 풀어서 새로운 버텍스 버퍼를 만들어야 합니다.
우리가 필요한 것은 폴리곤을 렌더링하는 작업이 아닙니다.
인접정보를 구성하는 일임을 잊지 말아야 합니다.
첫번째 패스에서의 렌더링은 TRIANGLELIST가 아니라, POINTLIST 로 수행하게 됩니다.

또 하나 주의할 것이 있습니다.
POINTLIST 로 렌더링을 수행할 때는 WVP( World-View-Projection ) 변환이 아니라,
World-View까지만 변환
을 해야 합니다.
이유는 간단합니다.
테셀레이션은 주로 시점에 근거해서  얼마나 많은 폴리곤을 생성할지를 판단해야 합니다.
이를 앞 시간들을 통해서 Adaptive 한 방식이라고 언급을 했었습니다.
이후의 패스에서는 이들 정점에 근거해서 LOD를 판정해서 Tessellation Factor 를 연산하게 되니다.
그래서 View 좌표계까지만 변환을 합니다.
첫번째 패스에서는 이렇게 View 공간으로 POINTLIST들을 텍스쳐에 렌더링 합니다.

이렇게 생성된 텍스쳐를 기반으로 해서 두번째 패스를 진행할 수 있습니다.
DX9 를 지원하는 모든 그래픽카드가 VertexShader 단계에서 텍스쳐 데이터를 읽어올 수 있는 것은 아닙니다.
이런 제약 사항들은 이제 큰 의미가 있는 것이 아니기 때문에,
개념적인 것에 포커스를 두시기 바랍니다.^^

두번째 패스의 목적은 Tessellation Factor를 구하는 것입니다.
즉, 얼마나 폴리곤을 세분화 할지를 결정합니다.
두번째 패스도 역시 POINTLIST 로 렌더링을 합니다.
그리고 첫번째 패스에서 생성해둔 인접 정점 정보를 가진 텍스쳐를 바인딩 합니다.
인접 정보가 있기 때문에 현재 정점을 기준으로 Tessellation Factor 를 계산할 수 있습니다.
두번째 패스에서 주의할 것은 이들 Tessellation Factor 를 저장하기 위해
R2VB 라는 일종의 버퍼에 렌더링
을 한다는 것입니다.
이는 ATI 테셀레이션 라이브러리에만 존재하는 개념입니다.

세번째 패스는 실제로 지오메트리(Geometry)를 렌더링 하는 단계입니다.
실제 렌더링 작업은 TRIANGLELIST 로 렌더링 합니다.
인덱스 기반의 렌더링이 아니라,
우리가 인덱스를 풀어서 생성한 버텍스버퍼로 렌더링 하는 것에 주의해야 합니다.
이때 스트림(Stream) 을 하나 더 연결하는데,
이것은 앞서 우리가 렌더링 했던 R2VB 라는 버퍼
입니다.

결과적으로 VertexShader 에는 Barycentric coordiate 기반의 가중치 값이 전달됩니다.
즉, 무게 중심 좌표입니다.

float3 vPosTessOS = i.vPositionVert0.xyz * i.vBarycentric.x +
                              i.vPositionVert1.xyz * i.vBarycentric.y + 
                              i.vPositionVert2.xyz * i.vBarycentric.z;


정점을 구성하는 방법은 위처럼 해야 합니다.

이상으로 DX9 세대의 테셀레이션 작업들에 대해서 아주 간단히 살펴보았습니다.
메인으로 다룰 내용이 아니라서, 쉽게 넘어간 부분이 많습니다.
아무래도 거의 사용하지 않기 때문에, 깊이있게 다루는 것은 의미가 없다고 생각합니다.

하지만, DX9 세대의 테셀레이션 작업은 이렇게 복잡한 방법과 절차를 통과해야 합니다.
DX11 의 테셀레이션 작업은 상대적으로 빠른 성능으로 구현이 됩니다.
왜냐하면 1 Pass 이기 때문입니다.


ATI 는 DX9 세대의 테셀레이션 작업을 위해서, 라이브러리를 제공하고 있습니다.
더 필요한 정보가 있으시면, 아래의 링크를 참고하시기 바랍니다.

http://developer.amd.com/gpu/radeon/Tessellation/Pages/default.aspx#d3d9

 

안녕하세요. 알콜코더 민군입니다. ^^

이번에 올리는 아티클은 일본의 게임 기술 분석가(?)로 유명한 '니시카와 젠지'씨의 저서 '게임 개발자가 되기 위한 3D 그래픽스 기술' 이라는 책에 나온 부분을 발췌, 번역해 보았습니다.

 

DX11에서 변경된 부분에 대해서 쉽고 간결하게 설명하고 있어서 공부하시는 분들에게 큰 도움이 될 것 같은 내용이라서 직접 번역해서 발췌 하였습니다.

 

DX11 이전의 DX10까지의 역사에 대해서는 알로샤님의 블로그에 매우 잘 번역이 되어 있습니다. 이전의 내용은 그 쪽을 참조하여 주시면 이해하는데 큰 도움이 될겁니다.

 

그리고, 그 후에 마이크로소프트는 Window7의 릴리즈와 동시에 DirectX 11을 릴리즈한다.

DirectX 11은 DirectX 9 대응 GPU, DirectX 10.x 대응 GPU 양쪽 모두를지원한다. 다만, 사용 가능한 API나 하드웨어 가속되는 기능은 각 베이스 구조에 의존한다. 즉, DirectX11의 풀구조를 실제로 구현한 것은 DirectX11 지원 GPU이지만, 모든 구조/기능이 아닌 DirectX 9/10.x 지원 GPU에서도 DirectX 11은 이용 가능하다…즉 하위 호환성을 보증하는 것이다. DirectX 10이 DirectX 9 지원 GPU를 지원하지 않는것과는 조금 상황이 다르다는 것이 재미있다.

DirectX11에서의 프로그래머블 셰이더는 Shader Model 5.0(SM 5.0)로의 버전업이 이루어져서, 새로운 4개의 셰이더 스테이지가 추가 되었다. 4개 중에서 3개는 프로그래머블 셰이더이고, 1개가 고정기능 셰이더가 추가 되었다. 새로운 프로그래머블 셰이더는 [헐 셰이더](Hull Shader)[도메인 셰이더](Domain Shader), [계산 셰이더](Compute Shader)이고, 고정기능 셰이더는 [테셸레이터](Tessellator)이다. 각각의 기능에 관해서는 Chapter 2와 Chapter 6에서 설명한다.

(역주* 물론 위 챕터들은 번역하지 않았습니다.. -_-;)

 

이것으로 DirectX 11의 그래픽스 파이프라인에 주어진 셰이더 스테이지는 정점 셰이더->헐 셰이더->테셸레이터->도메인 셰이더->지오메트리 셰이더->픽셀 셰이더->계산 셰이더라고하는 흐름이 되었다. 현행 게임기인 XBOX360과 PS3에 정점 셰이더와 픽셀 셰이더밖에 없다는 것을 생각해보면, 상당히 복잡한 진화를 한것이 되었다.

 

 

SM5.0은 새로운 셰이더가 추가 되었을 뿐만 아니라, 프로그래머빌리티가 향상된 것도 주목할만한 부분이다.

우선 첫번째로, 셰이더 프로그램에 서브 루틴의 사용이 가능하게 되어서, 동적링크(Dynamic Shader Linkage)가 지원된다.

DirectX 10까지는 똑 같은 기능의 셰이더라 할지라도 사용하는 변수셋트(예를 들어 광원의 종류와 개수)가 다를 경우등에서는 별도의 셰이더로 만들지 않으면 안되었다. 이 때문에 거의 기능이 똑같은데도 셰이더의 수가 알게 모르게 증가하게 되어, 셰이더의 관리가 어려워지는 패해가 발생하였다. DirectX 11에서는 셰이더의 기능 블록을 모듈화하는 것이 가능해져서, 각각을 흔히 C언어에서 이야기하는 함수포인터식으로 호출하는 것이 가능해졌기 때문에 효율 좋은 다기능 셰이더를 구성할 수 있게 되었다.

또한 각종 버퍼, 텍스쳐, 정수버퍼, 텍스쳐의 샘플러등의 셰이더의 입력 리소스에 대해서 인덱스 참조가 가능해졌다. 단지 지정 인덱스값은 정수로 한정되었다.

64비트 배정도부동소수점에 관해서는, 현행의 DirectX 10.x 지원 GPU에서도 DirectX와는 별개로 각 제조사 독자의 GPGPU모드에 한해서는 이미 지원되고 있었지만, DirectX 11에서는 Direct3D 그래픽스의 구조로 정식 지원이 된다. 다만 지원은 필수가 아니라, [옵션 지원]이라는 위치이다. 그래서 노트북용 GPU, 메인스트림 이하의 통합 칩셋GPU, 혹은 일부의 제조사의 GPU에서는 이것을 지원하지 않을 가능성이 있다.

부동소수점관련에서는, 이외에 32비트 부동소수점(FP32)와 16비트 부동소수점(FP16)의 상호 형 변환이 하드웨어 레벨에서 지원된다.

DirectX 10 때에 충실화가 이루어졌던 논리계산명령도 더욱 확장되었다.

새로 추가된 Gather()명령도 재미있다. 이것은 1개의 텍스쳐 명령으로 동시에 4곳의 텍셀의 값을 읽어 낼 수 있는 명령이다. 이것은 이전에 ATI RADEON X1900계열에서 지원되고 있던 [Fetch4]기능을 일반화시켜서 DirectX 11의 표준구조로 집어넣은 것이다. RADEON X1900의 Fetch4는 1개의 텍스쳐에 대해서 4 텍셀을 읽어낼 수 있는 기능으로, NVIDIA의 뎁스버퍼(Depth Buffer)의 특수참조기능 [NVIDIA SHADOW] 기능의 대항 기능이라고 하는 역할이었지만, SM 5.0의 Gather() 명령은 임의의 개수의 텍스쳐의 4군데 위치로부터 임의의 요소(a/R/G/B)를 읽어 내는 것이 가능하게 되었다. 이것은 구체적으로는 앞에 이야기한것처럼 뎁스쉐도우계열의 그림자생성의 고효율 실행은 물론이고, 최근 유행하고 있는 스크린 스페이스 엠비언트 오클루젼(SSAO:Screen Space Ambient Occlusion)과 같은 심도갚을 이용한 복잡한 포스트 프로세싱의 고효율화 실행에 도움이 된다.

DirectX 11에서는 뒤에 설명하는 DirectX 계산 셰이더의 지원에 싱크로하는 형태로, GPGPU적인 잠재 가능성이 픽셀 셰이더에도 추가되었다. 이것이 DirectX 11의 픽셀셰이더에 신기능으로 추가된 [Unordered Access View](UAV)라고 하는 신개념이다. Unordered Access 라는 것은 랜덤 액세스라는 뜻이다. 즉 픽셀 셰이더로부터 비디오 메모리로의 랜덤 액세스가 가능해진 것이다. 지금까지 픽셀 셰이더는 별수 없이 그 픽셀 좌표에 대응하는 비디오 메모리로의 쓰기(출력)밖에 되지 않았지만, 이 제약이 없어지게 된것이다.

UAV는 원래 DirectX 계산 셰이더를 위해서 확장된 개념이기 때문에, 당연히 DirectX 계산 셰이더에서도 이용 가능하다. 이 UAV로 인해서 값을 확산하는 것 같은 스캐터(산란) 형태의 필터 처리, A-Buffer같은 멀티 레이어형의 특수한 프레임퍼버에서 렌더링 순서에 의존하지 않고 반투명렌더링(OIT:Order Independent Transparency)등을 구현 가능하게 되었다.

A-Buffer의 개념도.
"A"에는 anti-aliased、area-averaged(영역평균화)、accumulation(연산)이라는 복수의 의미가 들어 있다.

 

이 UAV 도입에 의해서, DirectX 11/SM 5.0에서는 멀티렌러타겟(MRT:Multi Render Target)의 개수는 DirectX 10 때의 10개 그대로지만, 픽셀 셰이더에서는 8MRT와는 별대로 UAV 1개가 이용 가능하게 되었다. 또한 DirectX 계산 셰이더는 8UAV까지 이용 가능하다.

픽셀 셰이더와 DirectX 계산 셰이더에서 랜덤 메모리 액세스가 가능하게 된 관계로, 귀찮아 진 것이 복수 스레드로부터의 동일한 메모리 어드레스 엑세스의 관리다. 실행 타이밍에 의해 메모리의 내용이 변경되어버릴 가능성이 생기기 때문에, 이것은 멀티 스레드 프로그램에서 일어나는 디버그 곤란한 현상이 생길 수 밖에 없다. 그래서 DirectX 11에 있어서 픽셀 셰이더와 DirectX 계산 셰이더에서는 Atomic Operation(단위조작)을 지원하는 명령이 추가되었다.

 

이런 것들을 보면 DirectX 11은 새로운 프로그래머블 셰이더가 추가되고, 거기에 GPGPU지원으로 인한 기능강화가 픽셀 셰이더에 대해서 이루어 졌다는 느낌으로, 덕분에 복잡도가 상당히 높아지게 되었다. 이 복잡한 처리체계(파이프라인구조)는 CPU와 비교도 되지 않을 정도이다.

PS3, XBOX360등의 지금 세대(사실상 DirectX9 세대) 게임기의 다음 세대의 게임기등은 DirectX 10을 넘어서, DirectX 11 새대 이후의 아키텍쳐를 채용하게 되지 않을까.



DirectX SDK February 2010  버전까지는 'EnhancedMesh' 라는 샘플이 있었습니다.
아쉽게도 2010 June 버전에서 이 샘플은 사라졌습니다.
메시의 퀄리티를 향상시키는 샘플인데, 실제로는 폴리곤 갯수를 증가시키고 있습니다.
굳이 실행을 실켜보실 이유는 없습니다. ^^

ID3DXMesh 인터페이스에는 멤버함수로 CloneMeshFVF() 를 가지고 있습니다.
이 멤버함수의 옵션으로 D3DXMESH_NPATCHES 을 사용하게 되면,
하드웨어 가속을 받아서 폴리곤을 증가시킬 수 있습니다.
물론 내부적으로는 많은 연산을 수행할 것입니다.



만약 테셀레이션 작업이 그래픽카드에서 지원을 해주지 않는다면,
이는 CPU 기반으로 작업을 수행해야 합니다.
바로 이를 도와주는 API 가 D3DXTessellateNPatches() 입니다.



이렇듯 DirectX9 세대에도 테셀레이션을 위해서 API들을 지원해 주고 있었습니다.
물론 정식으로 그래픽카드에서 지원을 하지 않았기 때문에,
성능에 많은 문제점을 가지고 있었습니다.
테셀레이션 자체가 근본적으로 많은 연산을 수반하기 때문입니다.

다음 시간에는, 마지막으로 ATI의 DirectX9 기반의 테셀레이션 작업에 대해서 살펴보도록 하겠습니다.^^

 

 

 

 

안녕하세요. 알콜코더 민군입니다. ^^

DX11 튜터리얼 2번째 연재를 시작하겠습니다.

 

저번시간에는 3D 디바이스를 초기화하는 전체 과정에 대해서 살펴보았습니다. 3D 디바이스를 초기화 한다는 말은 곧 3D 화면을 생성하고 오브젝트를 렌더링하기 위한 준비단계를 의미합니다. 이 과정을 거쳐서야 비로서 애플리케이션의 윈도우에 3D 렌더링을 위한 화면이 준비되고, 거기에 오브젝트들을 렌더링하고 움직이면서 게임이나 3D 그래픽스 프로그래밍이 시작되는 것입니다.

 

이번 시간에는 지난 시간에 알아본 내용들을 실제로 코드로 어떻게 구현하는지에 대해서 이야기 해보겠습니다. 전체 예제 코드는 DirectX SDK에 포함되어있는 Tutorial을 보시면 됩니다. ^^

 

우선은 튜터리얼의 코드를 수정하지 않고 있는 그대로 설명을 하겠습니다. 튜터리얼은 간단히 내용만 구현하기 위해서 코드가 지저분한 부분도 있고, 필요없는 부분도 있습니다 수정을 해서 좀더 깔끔하게 만들 수도 있습니다만 우선은 기본 이해를 위해서 튜터리얼 1번 예제 코드를 수정없이 그대로 분석하면서 연재를 진행하겠습니다. (제가 수정해서 헷갈릴수도 있으니까요.. 예제 코드와 다르잖아!! 이런걸 방지하기 위해 ㅡ.ㅡ;)

 

그리고 이해를 돕기위해 튜터리얼 코드에 한글 주석을 달아서 첨부파일로 첨부하였습니다. 참조하시기 바랍니다.

 

 

DirectX3D 디바이스와 SwapChain 생성하기

 

DX11에서 3D디바이스와 SwapChain은 동시에 생성됩니다. "D3D11CreateDeviceAndSwapChain" 함수를 이용해서 두개의 디바이스 객체를 한번에 생성하게 됩니다. 생성하는 방법은 DX의 객체의 생성법이 다 그렇듯이, 생성을 위한 인자들과 생성 정보를 담고 있는 구조체를 인자로 넘겨주면 생성이 됩니다.

이제는 예제 코드의 순서에 따라서 생성하기 위한 정보들이 어떤것들이 있고, 어떻게 설정하는지 Step by Step으로 설명하겠습니다. (다시 말씀드리지만 이 연재는 초보자용으로 쓰고 있는 입문 연재입니다.^^)

  1. 변수 선언


디바이스를 생성하고, 생성된 디바이스 객체 정보를 담을 변수들을 선언합니다.

 

위 객체들이 어떤 역할을 하는지는 타입명만 보면 대충 감이 잡히실겁니다. 초기화의 목적은 아래 세개의 객체 디바이스, 디바이스컨텍스트, SwapChin을 생성하고 초기화하는 것입니다. 생성 방법은 아래 내용에서 알아보겠습니다.

 

2. CreateDeviceFlag의 설정
생성할 DX 디바이스의 플래그를 설정해줍니다.



위와 같이 UINT값으로 플래그를 설정합니다. 디버그 버전에서는 위처럼 디버그용 플래그를 설정하고 릴리즈의 경우에서는 0을 설정하면 됩니다.

 

3. 3D 드라이버 타입의 설정
생성할 3D 드라이버 타입 배열을 설정합니다.



원래는 바로 하드웨어 지원으로 설정해도 되지만, 아직 DX11을 지원하지않는 그래픽카드가 많기 때문에, DX11을 지원하지 않는 그래픽 카드에서는 REF 모드로 생성하기 위해서 가능할만한 드라이버 타입들을 배열로 생성해서 나중에 디바이스를 생성할 때 for 문을 돌면서 하나씩 직접 생성해 볼것입니다. 그래서 처음으로 생성에 성공하는 드라이버 타입을 사용할 겁니다.
즉, 하드웨어 모드나 WARP 모드로 디바이스가 생성되지 않는다면, DX11을 지원하지 않는 하드웨어이기 때문에 마지막 레퍼런스 모드로 생성이 됩니다.
레퍼런스 모드는 하드웨어가 지원하지 않을 때 소프트웨어적으로 지원을 해서 디바이스를 생성하여 줍니다. 물론 하드웨어 가속을 사용하지 못하기 때문에 성능이 상당히 처절하긴 하지만, 이로 인해서 DX11을 지원하지 않는 하드웨어서도 DX11 디바이스를 생성하고 사용할 수 있습니다. 아직 기초 예제에서는 성능에 부하가 큰 내용이 없기 때문에 DX11을 지원하지 않는 그래픽 카드에서도 이 예제를 실행해 보는데는 무리가 없습니다.

4. Feature Level의 설정
피처레벨은 DX의 어느 버전의 기능을 지원할지를 설정하는 것입니다.



피처레벨에 대해서는 '조진현'님이 상세한 설명을 해주셨기 때문에 그 연재 내용을 참고하시면 됩니다.

 

5. DXGI_SWAP_CHAIN_DESC 구조체 설정
스왑체인을 생성하기 위한 정보들을 구조체에 세팅하여 줍니다.



가장 설정하게 많은 스왑체인 생성 구조체입니다. 우선 하나하나 설명을 하겠습니다.

BufferCount는 생성할 백버퍼의 카운트입니다. 현재는 하나만 생성합니다. Width와 Height는 생성할 백버퍼의 해상도입니다. 생성한 윈도우와 같은 크기의 백버퍼를 생성하기 때문에, 윈도우의 크기와 같게 설정하여 줍니다. (윈도우 크기를 구하는 코드는 예제코드 위쪽에 있습니다) Formatr은 역시 백퍼러를 생성할 포맷을 설정합니다. 지금은 기본 포맷이기 때문에 R8G8B8A8, 즉 각각의 RGBA 컬러에 8비트씩을 할당하는 32비트 컬러포맷으로 백버퍼를 생성합니다.

RefreshRate는 화면 주사율을 의미합니다. 일반적으로 60으로 설정합니다. Denominator 역시 1로 설정합니다.

BufferUsage는 생성한 버퍼의 사용 용도를 의미합니다. 백버퍼에 사용할 렌더타겟으로 버퍼를 생성하고 있기 때문에 위와 같이 설정하여 줍니다.

OutputWindow는 이 버퍼를 출력할 윈도우 핸들을 지정합니다. 어플리케이션을 생성할 때 저장한 현재 윈도우의 핸들을 지정해 줍니다.

SampleDesc는 멀티샘플링을 사용할지에 대한 설정입니다. 멀티샘플링은 다른 말로 하면 안티얼라이싱과 같은 의미입니다. 화면의 계단 현상을 줄이기 위한 기술로 독자 여러분도 많이들 들어보셨을 겁니다. 이 설정을 Count = 1, Quality = 0으로 설정하면 이 기능을 사용하지 않는 다는 의미입니다. 이 예제는 멀티샘플링을 사용하지 않기 때문에 위와 같이 설정합니다.

Windowed는 풀스크린으로 출력할지, 윈도우 창으로 출력할지에 대한 설정입니다. 예제에서는 윈도우 모드로 생성하기 때문에 TRUE를 설정하였습니다.

 

6. 3D 디바이스와 스왑체인의 생성
이제 위와 같은 설정들이 모두 끝나면 실제로 디바이스와 스왑체인을 생성할 수 있습니다. 지금까지 위의 과정은 이 과정을 위한 준비단계였습니다. 실제로 Create함수를 호출하여 디바이스를 생성하겠습니다.



지금껏 준비과정에서 생성된 정보들을 가지고 함수를 호출해서 필요한 객체들을 생성합니다.
실제 예제 코드에서는 DX11을 지원하지 않는 하드웨어를 위해서 for문 내에서 드라이버 타입을 바꿔가면서 위 함수를 호출하고 있습니다. 그러다가 디바이스가 성공적으로 생성되면 그 디바이스를 사용하게 되어있습니다. 따라서 이 예제는 DX11이 지원되지 않는 그래픽카드에서도 레퍼런스 모드로도 돌아가게 되어있습니다. 나중에 DX11이 기본적으로 지원되는 세상이 된다면 이런 작업을 해주지 않고 바로 하드웨어 지원으로 설정해도 되겠죠. (사실 이런 코드 때문에 코드가 좀더 복잡해 보입니다.. =ㅅ=)

 

 

다음 내용으로 이어집니다. ^^;;

이 연재에는 예제 코드가 첨부 파일로 포함되어 있습니다.

 

 

 

 

언젠가 우즈벡에 미소녀 게임 회사를 차리고 싶은 개발자

3D게임 클라이언트 프로그래머, 살짝 오타쿠

드래곤볼 온라인 개발, 현재는 네오위즈 서식중

'신입게임 개발자의 서울상경기' '초중급 게임개발자 스터디' 운영중

 

 

 

 

 

 


안녕하세요. 알콜코더 민군입니다. ^^

이제부터 본격적으로 연재를 시작하게 되었습니다. 많은 응원 부탁드립니다.

 

전에 예고했던 DX 11 튜터리얼을 하나씩 예제로 공부하면서 기초 단계부터 하나하나씩 밟고 올라가겠습니다. 이미 튜터리얼쯤이야 건너뛰신 분에게는 별로 도움이 안될지도…(쿨럭;;)

 

자 그럼 대망의 첫번째 튜터리얼을 공부해 보죠

 

 

 

이번 예제의 목표

 

l  DirectX 11을 이용하여 간단한 기본 어플리케이션을 만들어 본다

l  DirectX 11을 사용하기 위한 기본 디바이스 및 오브젝트들을 셋업한다

 

위가 이번 튜터리얼 01의 목표입니다. 우선은 3D 렌더링 화면을 가진 윈도우를 뛰우는것부터 시작해보죠. 3D 화면에 맵을 뛰우고 캐릭터가 뛰어다니는 게임을 만들어 보고 싶으시겠지만천리길도 한걸음 부터입니다. 우선 차근 차근 하나씩 시작해 보죠. ^^

 

 

참고 소스

 

l  DirectX 11 Sample – Tutorial 01 : Direct3D Basics

 

Direct X SDK에 포함되어 있는 샘플입니다. 이 샘플을 기반으로 설명하겠습니다.

직접 SDK에서 설치해서 공부하셔도 되고, 제가 첨부한 파일을 받아서 보셔도 됩니다.

 

 

사전에 준비해야 할 것

 

우선 기본적으로 튜터리얼 역시 윈도우 프로그래밍이기 때문에, 기본적인 윈도우는 뛰우고, DirectX를 설정해야 겠죠? ^^

윈도우를 뛰우는건 기본적인 내용이고 이 연재의 진행을 위해서도 과감하게 생략하겠습니다. (__)

하지만 공부하는 데 도움이 되시라고, 간단히 기본 윈도우를 뛰우는 프로그램을 첨부파일로 첨부합니다. (DX10의 튜터리얼 0번 샘플)

 

직접 코딩을 하면서 공부를 해보기 위해서는 샘플 파일을 받아서 기본적인 윈도우 프로그래밍 기반을 세팅한 후에 아래 내용을 따라서 DX11을 직접 작성해 보면 큰 도움이 될 겁니다.

 

첨부파일 – DirectX 10 Tutorial 00 : Win32 Basics

DirectX 를 아직 세팅하지 않고, 빈윈도우만 뛰우는 기본적인 프로그램입니다.

 

 

Direct3D 디바이스 오브젝트를 셋업하자

 

3D 디바이스 셋업은 3D 프로그래밍의 시작이자, 가장 필수적인 과정입니다.

3D 디바이스, 즉 그래픽카드를 셋업해야 그래픽카드를 통해서 3D 맵을 그리던가, 캐릭터를 렌더링하거나 할 수 있죠. 이 부분이 여타 일반 프로그래밍과 3D 프로그래밍이 시작부터 다른 점입니다. 일반 윈도우 프로그래밍은 그래픽 카드에 접근할 일이 없기 때문에 이런 과정이 당연히 없습니다. ^^

(나름 3D 프로그래머만의 특권일지아니면 지옥의 시작일지…)

 

DX11에서는 우선 아래 세가지 오브젝트를 만들어야 합니다

 

l  Device

l  Immediate Context

l  Swap Chain

 

Device

3D 그래픽 카드와 연결되는 기본 디바이스 객체

 

DX9의 디바이스와 같은 계열이지만, DX 11에서부터는 멀티스레드를 최적화하기 위하여 직접 디바이스를 사용하지 않고, Context를 이용하여 디바이스에 명령을 내립니다. 따라서 별로 사용할 일이 많지는 않을겁니다.

Immediate Context

DX11에 새로 추가된 오브젝트

 

DX11에서는 디바이스 객체에 직접 접근하지 않고, 이 객체를 이용해서 디바이스에 명령을 내린다고 생각하시면 됩니다.

 

DX11에서는 멀티코어/멀티스레드를 최적화 하기 위해서 Device의 하위에 Context를 분리하여 사용합니다. 현재는 이 Context로 디바이스에 명령을 내린다고만 알고 넘어가면 됩니다.

Swap Chain

화면에 렌더링할 백버퍼를 관리하고, 실제로 화면에 렌더링 하는 역할을 담당하는 오브젝트


이 오브젝트를 이용하여 백버퍼의 렌더 타겟을 얻어내 3D  오브젝트를 렌더 타겟에 렌더링하고, 프론트 버퍼와 백버퍼를 스왑(플립(Flip)이라고도 합니다)하여 실제 모니터에 게임 화면을 렌더링하는 역할을 합니다.

 

 

 

기본적인 셋업 및 오브젝트 생성 순서

 

기본적인 DX11의 셋업 순서는 아래와 같습니다.

 

1.   ID3D11Device SwapChain을 생성한다

2.   SwapChain에서 백버퍼를 가져온다(2D Texture)

3.   백버페 실제 렌더링할 렌더 타겟 뷰를 생성하여 바인딩 시킨다

4.   뷰포트를 생성한다

5.   매 프레임마다 위에서 생성한 렌더 타겟 뷰에 게임 화면을 렌더링한다.

6.   SwapChain을 이용하여 디바이스에 화면을 그린다

 

위 순서에서 가장 큰 목적은 바로 렌더 타겟 뷰(Render Target View)’를 생성하는 것입니다. 바로 이 렌더 타겟 뷰가 실제로 우리가 3D 게임 화면을 렌더링할 타겟이 됩니다. (그러니까 렌더타겟이라고 부르는 거겠죠.. ==)

 

이 랜더타겟뷰는 리소스뷰의 일종으로 디바이스의 백버퍼에 바운딩되면서 생성됩니다. 그래서 렌더타겟뷰에 게임 화면을 렌더링한다는 것은 백버퍼에 렌더링하는것과 같습니다. 그리고 백버퍼에 모든 화면을 렌더링하고 난 다음에는 SwapChain을 이용해서 백버퍼를 화면에 그리면 되는겁니다. 정확히는 백퍼버를 프론트버퍼와 바꾸는 스와핑 (또는 플립(Flip))이 일어나는 것이죠.

 

간단히 이야기면, SwapChain을 생성하고, SwapChain안에서 백버퍼를 꺼내오고, 꺼내온 백버퍼를 가지고 렌더타겟뷰라는 오브젝트에 바운딩합니다.

 

그리고 실제 내부에서는 SwapChain이 백버퍼라는 리소스를 가지고 있고, 개발자는 그 백버퍼를 렌더 타겟 뷰에 바인딩하여 사용하는 것입니다.

 

 

 

 

다음 내용으로 이어집니다. ^^;; 

 

 

언젠가 우즈벡에 미소녀 게임 회사를 차리고 싶은 개발자

3D게임 클라이언트 프로그래머, 살짝 오타쿠

드래곤볼 온라인 개발, 현재는 네오위즈 서식중

신입게임 개발자의 서울상경기’초중급 게임개발자 스터디’ 운영중

 

 


 

 

 

안녕하세요. 알콜코더 민군이라고 합니다. ^^

처음 뵙게 되었네요. 이미 DX11에 글을 올리고 있는 조진현님의 협박과 강요에 힘입어 오늘부터 DX 11입문 강좌를 연재하게 되었습니다.

 

이미 DX11의 변경점이나 중요한 개념들은 앞서 연재하시는 분이 두분이나 계시기에… 저는 기술적인 이야기보다는 실제로 예제들을 만들어보고, 분석하면서 DX11을 배워가는 내용을 연재하기로 결정하였습니다.,

 

그리고 단지 변경된 점뿐만 아니라, 실제로 DX11을 이용하여 3D 그래픽스 프로그래밍의 기초도 배울 수 있는 내용으로 연재하려고 합니다. DX 11을 배우기 위해서는 3D 그래픽스에 대한 이해는 당연한 필수 조건이니까요. ^^

 

 

강좌 연재 목적은 무엇인가요

 

이 연재의 목적은 여러 예제 및 튜터리얼을 통하여 실제로 DirectX 11을 사용하여 개발을 하면서 익히는 것이 목적입니다. 사실 아직 DirectX 11은 주류로 자리잡지는 않고 있습니다만,분명 2년 이내에 주류로 자리 잡을 것은 확실합니다. (DX10은 비스타와 같이 묻혀버렸죠…)

현재 주류로 자리잡고 있는 DX9과 DX11은 상당히 많은 차이를 보이고 있습니다. 미리미리 DX11을 공부해두어야만 차후에 DX11이 주류로 자리 잡았을 때 당황하지 않고 개발할 수 있을것입니다. 간단히 말해서 미리 배워두면 도움이 되었으면 되었지, 손해 보지는 않을거란 이야기입니다. :)

 

 

어떤 사람들에게 도움이 될까요

 

일단 초보자 대상의 내용으로 연재를 진행할 생각입니다. 입문편이니까요(^^). 하나 하나 예제를 차근 차근 따라오고 설명하면서 기본적인 부분부터 배워나갈 예정입니다. 그래서! 혼자서 DX11 도움말을 보면서 배울수 있는 중고급 개발자분들에게는 크게 도움이 되지 않을 수도 있습니다. (차후에 여유가 생기면 좀더 고급스런 예제들을 다루는 '중급편'이나 '고급편'도 연재해 볼 생각입니다. )

 

'입문편'은 아래와 같은 분들을 대상으로 연재를 진행합니다.

 

  • DirectX 11을 처음 사용해보는 사람
  • DirectX 11로 게임 개발을 시작하려는 사람
  • DirectX 11로 3D 그래픽에 입문하려는 사람
  • DirectX 9을 학습하고 DirectX 11을 배우려는 사람
  • 초보 및 입문 3D 게임 프로그래머

 

 

개발 환경

 

  • Direct SDK (June 2010) – 현재 최신 버전입니다. SDK가 업데이트가 될 때마다 최신버전으로 연재를 진행할 예정입니다.

 

  • Visual Studio 2010

 

  • DX11 지원 그래픽카드 – DX 11이 지원되지 않는 그래픽카드에서도 REF 모드로 개발은 가능합니다. 프레임이 상당히 낮게 나오긴 하지만 기초적인 예제를 공부할 수는 있습니다.

 

 

연재 목차

 

우선은 DX SDK에 포함된 아래 DX11 튜터리얼을 공부하면서 DX11 사용법과 기본을 익혀갈 생각입니다. 그 후에는 쉽고 유용한 예제들을 만들고 분석하면서 배워갈 예정입니다.

 

  1. 다이렉트 3D 기초
  2. 삼각형 렌더링
  3. 셰이더
  4. 3D 공간
  5. 3D 변환
  6. 조명 (라이팅)
  7. 텍스쳐 매핑과 Constant Buffers

 

 

 

 

언젠가 우즈벡에 게임 회사를 차리고 싶은 오타쿠 개발자

3D게임 클라이언트 프로그래머

드래곤볼 온라인 개발, 현재 네오위즈 서식중

'신입게임 개발자의 서울상경기'와 '초중급 게임개발자 스터디' 운영중



앞선 시간을 통해서 ID3DXPatchMesh 를 이용하면
간단하게 테셀레이션이 적용된 메시를 만들 수 있음을 언급했었습니다.
실제로 D3DX 유틸리티 클래스들이 테셀레이션을 손쉽게 적용할 수 있도록 구비가 되어있습니다.
그렇다는 것은 실제로는 DirectX 내부적으로 코어한 API가 있다는 얘기입니다.

테셀레이션과 관련한 DirectX 에서 코어한 API가 바로
IDirect3DDevice9::DrawTriPatch() 와 IDirect3DDevice9::DrawRectPatch() 입니다.
API 이름에서 쉽게 이해할 수 있듯이 전자는 삼각형과 관련한 것이고 후자는 사각형과 관련한 것입니다.
두 함수의 원형은 다음과 같습니다.

HRESULT DrawTriPatch
(
  [in]  UINT Handle,
  [in]  const float *pNumSegs,
  [in]  const D3DTRIPATCH_INFO *pTriPatchInfo
);


HRESULT DrawRectPatch(
  [in]  UINT Handle,
  [in]  const float *pNumSegs,
  [in]  const D3DRECTPATCH_INFO *pRectPatchInfo
);


그런데 조금 생소한 구조체 정보를 함수 인자로 받습니다.
이 두 API들은 함수 이름에서도 알 수 있듯이 실제로 렌더링을 수행하는 API 입니다.
테셀레이션을 위해서는 테셀레이션을 위한 정보들이 존재해야 합니다.
이들에 대한 설정 작업이 이루어져야 하는데,
이를 위한 구조체가 세번째 인자인 D3DTRIPATCH_INFO와 D3DRECTPATCH_INFO 입니다.
사각형과 관련한 작업은 삼각형과 유사하기 때문에 지금부터는 삼각형에 국한에서 글을 진행하겠습니다.


D3DTRIPATCH 구조체의 원형은 다음과 같습니다.

typedef struct D3DTRIPATCH_INFO
{
  UINT          StartVertexOffset;
  UINT          NumVertices;
  D3DBASISTYPE  Basis;
  D3DDEGREETYPE Degree;
} D3DTRIPATCH_INFO, *LPD3DTRIPATCH_INFO;


이 구조체는 버텍스 버퍼처럼 오프셋과 버텍스 갯수를 먼저 설정합니다.
D3DBASISTYPE 은 고차원 패치( high-order patch )의 기본 타입을 설정합니다.
삼각형의 경우에는 D3DBASIS_BEZIER 만 설정할 수 있습니다.

D3DDEGREETYPE 는 고차원 패치의 차수 정도를 설정하게 됩니다.
즉, 곡선을 표현하는 방정식의 차수를 표현하는데,
높은 차수를 선택할 수록 당연히 연산량이 많아질 것입니다.

이들에 대한 종류는 다음과 같습니다.

종류

버텍스 갯수
D3DDEGREE_CUBIC 10 ( 3차 방정식 )
D3DDEGREE_LINEAR 3   ( 1차 방정식 )
D3DDEGREE_QUADRATIC N/A ( 지원되지 않음 ) ( 2차 방정식 )
D3DDEGREE_QUINTIC 21 ( 4차 방정식 )


아래의 그림은 Cubic Bézier 방식의 삼각형 패치를 보여주고 있습니다.


Diagram of a triangular high-order patch with nine vertices


중간 중간에 생성된 정점을 기준으로 테셀레이션 작업이 수행될 것입니다.^^
이런 테셀레이션과 관련한 API들이 DirectX9 에 있었지만, 사실 거의 사용되지는 못했습니다.
왜냐하면, 정말이지 많은 연산을 필요로 하기 때문이겠죠? ^^
즉, DirectX9의 테셀레이션 작업은 소프트웨어적으로 에뮬레이션 되는 테셀레이션입니다.

[JumpToDX11-14] DirectX9 세대의 테셀레이션( ID3DXPatchMesh 편 )

DirectX 11 2010. 7. 19. 08:30 Posted by 알 수 없는 사용자


DirectX11 을 통해서 가장 많은 관심을 가지고 있는 부분 중 하나인 테셀레이션( Tessellation )은
갑자기 등장한 새로운 기능이 아닙니다.


< DirectX9에서의 테셀레이션의 등장 >

DirectX9 이 처음 세상에 등장할 때, 아래와 같은 특징들을 나열했었습니다.

- 2-D support
blt, copy, fill operations, GDI dialogs
- Adaptive tessellation
- Displacement mapping
- Two-sided stencil operations
- Scissor test rect
- Vertex stream offset
- Asynchronous notifications
- VS / PS 2.0
Flow control, float pixels
- Multiple render targets
- Gamma correction


Adaptive tessellation 이 보이시죠?
저도 그냥 무심코 지났던 DirectX9 소개 자료에서 우연히 찾았습니다.^^


< Adaptive tessellation >

테셀레이션에는 몇 가지 방법이 있는데,
그 중에 가장 유명한 것이 Adaptive 형식과 Uniform 형식입니다.
아래의 이미지를 보시기 바랍니다.


< 이미지 출처 : GPU Gems 2권 >


좌측의 경우가 Adaptive 한 방식입니다.
Adaptive 한 방식을 간단히 설명드리면,
시점의 위치에 근거에서 얼마나 많은 면을 생성할 지를 판단해서,
테셀레이션 작업
을 하는 것입니다.

반면에 Uniform 한 방식은,
모두 균일한 면의 갯수로 테셀레이션 작업을 수행하는 방법
입니다.
Uniform 한 방식이 더 연산 수가 많은 것이 일반적이기 때문에,
Adaptive 한 방식이 게임 분야에서 주로 사용됩니다.



< 테셀레이션을 위해 필요한 정보 >

테셀레이션 작업을 위해서는 두 가지가 필요합니다.
그것은 제어점들( Control Points )과 테셀레이션 팩터들( Tessellation Factors ) 입니다.
제어점들은 파이프라인에 입력으로 들어감으로써 패치( Patch ) 형태로 변환되어서
최종적으로 렌더링
되게 됩니다.
이 과정에 대한 자세한 설명은 앞으로도 꾸준히 언급될 것입니다.
지금은 간단하게 이 정도로만 설명하고 넘어가겠습니다.^^



< ID3DXPatchMesh >

그러면 DirectX9 은 어떤 방식으로 테셀레이션 작업을 지원했을까요?
그것은 ID3DXPatchMesh 라는 인터페이스를 통해서 간접적으로 지원했습니다.

참고적으로 얘기드리면, DirectX 에서는 D3DX 라는 유틸리티를 통해서
메시를 관리할 수 있는 클래스를 제공했습니다.
ID3DXBaseMesh, ID3DXMesh, ID3DXSPMesh, ID3DXPMesh,
그리고 마지막으로 언급드렸던 ID3DXPatchMesh 입니다.

ID3DXPatchMesh 인터페이스는 다른 메시들을 지원하는 클래스와 다릅니다. 
일반적인 메시 인터페이스들은 ID3DXBaseMesh와 계층 관계를 이루는 반면에,
ID3DXPatchMesh 는 완전히 별도로 구성된 클래스입니다.
즉, ID3DXPatchMesh 클래스는 IUnknown 인테페이스를 상속받습니다.


ID3DXPatchMesh는 테셀레이션 작업을 위해서 각종 멤버 함수를 가지고 있습니다.
실제로 테셀레이션 작업을 하는 함수는 ID3DXPatchMesh::Tessellate() 와
ID3DXPatchMesh::TessellateAdaptive()
입니다.
이들 함수에 대한 형태는 다음과 같습니다.

HRESULT Tessellate
(
  [in]  FLOAT fTessLevel,
  [in]  LPD3DXMESH pMesh
);

HRESULT TessellateAdaptive
(
  [in]  const D3DXVECTOR4 *pTrans,
  [in]  DWORD dwMaxTessLevel,
  [in]  DWORD dwMinTessLevel,
  [in]  LPD3DXMESH pMesh
);

두 멤버함수 모두 LPD3DXMESH 형태의 테셀레이션 작업이 끝난 메시를 리턴합니다.

이들에 대한 모든 작업은 CPU 가 담당합니다.
또한 연산량도 많기 때문에 Adaptive Tessellation을 처리하기는 상당한 무리가 있습니다.
왜냐하면 Adaptive Tessellation은 시점에 근거해서 매번 폴리곤을 생성해야하기 때문입니다.
ID3DXPatchMesh::Optimize() 라는 최적화 함수를 미리 호출해 줄수도 있지만,
그래도 이는 분명 매우 부담스러운 연산입니다.

< 마치면서... >
이상으로 ID3DXPatchMesh 를 활용한 DirectX9 의 테셀레이션 작업에 대해서 살펴보았습니다.
DirectX9 에서의 테셀레이션 작업의 불편함과 성능 문제를 이해한다면,
DirectX11 에서의 테셀레이션 작업의 우수성을 알 수 있을 것이라 생각됩니다.
다음 시간에도 계속 DirectX9 에서의 테셀레이션 작업에 대해서 살펴보겠습니다.^^

[DX11_#5]DirectX11의 활용 사례(1/3)

DirectX 11 2010. 6. 24. 22:19 Posted by 알 수 없는 사용자


 DirectX11의 여러가지 기능이 있지만 한눈에 무엇이 좋아졌는지 왜 써야 하는지에 대해서 의문을 가지시던 분들이 계실 것 같습니다.
 Dirt2라는 게임에서 사용된 DX11의 기능을 보면 그런 의문이 조금은 사라질것 같습니다.

tessellation은 GPU파이프라인이 정점을 생성하는 것을 신속하고 능률적으로 계산하는 과정입니다.
Xbox360의 Xeons칩부터 ATI의 GPU까지도 존재하는 기능이나 HD 5000 시리즈에서 최초로 DX의 버젼이 지원되었습니다.
 높은 bandwidth를 필요로하는 하이폴리곤 모델을 쓰지 않게 해주고, 특히 신속하게 현장의 풀잎 전체를 빨리 처리해야하는 상황에서도 유용한 기술입니다.


Dirt2에서는 저수치가 교차 할 때, 경기를 보고 있는 관중 그리고 트랙 바깥쪽에서 휘날리는 깃발에서  총 3개의 상황에서 사용을 하였습니다.
 위 그림에서 본 것 처럼 tessellation은 더 상세한 모양을 추가해 더 현실적인 표현을 할 수 있습니다.
그러나 하이폴리곤 모델에서는 사용해선 안됩니다. 오히려 tessellation을 쓰지 않으니만 못한 경우가 생길 수 있습니다.

AMD
에 따르면 " CPU 차가 그것을 통과할 height field texture generates한다. tessellator 카메라 위치에 근거를 개의 삼각형을 생성한다.

  결과로 아름답고, 물리적으로 정확한, 동적인 표면을 있다. 게임의 DirectX 9에서의 surface 다만 2개의 삼각형에 의해 나타낸 환영이다.라고 했다.
 
물에 있는 여분의 잔물결 생성뿐만 아니라, 또한 바람에 휘날리고 있는 깃발에 있는 뒤틀림 잔물결을 추가하기 위해 사용 됩니다. Tessellation 또한 현실적으로 보이는 곡선을 창조하고, 기술은 low polygon군중을 디테일하게 보여주는 사용되었습니다.



아래의 동영상을 보시면 확실하게 차이를 느끼실 수 있습니다.



원문 : http://www.bit-tech.net/gaming/pc/2009/12/01/directx-11-performance-first-look-dirt-2/1

[JumpToDX11-13] Tessellation 등장.

DirectX 11 2010. 6. 15. 09:00 Posted by 알 수 없는 사용자


요즘 vs2010 에 아티클이 많아져서 너무 좋습니다..^^


< Tessellation 개념 잡기 >

지금부터 언급할 내용은 Tessellation 입니다.
Tessellation을 간단히 정의하자면, 적은 수의 폴리곤이 그래픽 파이프라인을 통과했을 때,
많은 수의 폴리곤들을 생성해서 최종 화면에서는 훨씬 정밀한 결과를 나타내는 기술
이라고
할 수 있습니다.
간단히 이미지를 보시면 쉽게 개념화 할 수 있을 것입니다.






감동적이신가요?
저는 꽤 큰 감동을 받기는 했는데, 어마어마한 연산량이 걱정이 되었습니다.
물론 여러 분들도 저와 같은 생각일 것이라고 생각합니다.



< Tessellation 의 등장 배경 >

오늘 날의 컴퓨터 그래픽스의 발전은 정말이지 급격하게 변화했습니다.



유명한 파이날 판타지7 과 최신작을 비교해 보았습니다.
그래픽적인 큰 변화가 느껴지시나요?
아마도 느껴지실 것입니다.( 안느껴지시면 곤란합니다..^^ )

저 변화의 중심에 서 있는 기법 혹은 기술은 어떤 것일까요?
다양한 의견이 있을 수 있지만, 개인적인 견해를 전제로 제가 언급하면 텍스쳐링이라고 생각합니다.
오늘 날의 하나의 폴리곤에 적용되는 텍스쳐의 갯수는 하나가 아닙니다.
노말맵이 거의 표준적으로 사용되고 있는 현 세대에서는
각종 라이팅 처리를 위해서 많은 갯수의 텍스쳐가 사용되고 있습니다.
그래서 우리는 현실감 있는 게임을 즐길 수 있습니다.
이러한 발전의 방향은 폴리곤 갯수를 증가시키는 것보다,
텍스쳐링을 활용하는 것이 성능적인 측면에서 더욱 효과적이기 때문입니다.

그러던 과정에서 이제는 GPU의 성능이 급격히 발전하기 시작했습니다.
많은 사람들이 GPU의 활용에 대해서 고민하기 시작했고,
DirectX9 부터 이런 GPU을 활용한 Tessellation 위한 기법들이 공개적으로 소개되기 시작했습니다.
특히나 ATI 쪽에서는 DirectX9 을 위한 Tessllation SDK 를 제공했었습니다.
여담이지만, 엔비디아쪽에서는 자사의 GPGPU 인 CUDA 를 DirectX9 에서 지원했었습니다.
두 회사의 발전 방향이 이때부터 사실 조금씩 차이가 나기 시작했었다고 볼 수 있습니다.



위의 그림은 ATI 사에서 Tessellation의 필요성을 표현하고 있는 그림입니다.
텍스쳐링을 아무리 많이해도, 폴리곤 갯수가 적으면 더 큰 현실감을 느끼는데는 제한이 있다는 정도로 정리할 수 있을 것입니다.
( 그림(c) 에서 몬스터의 부자연스러운 손가락이 보이시죠? )

그래서 조금 더 큰 현실감을 위해서 폴리곤을 증가시키는 방법을 고안하게 되었고,
급기야 이것이 현 DirectX11 의 정식 그래픽 파이프라인 스테이지로 추가되었습니다.
즉, 공부할 것이 훨씬 더 많아졌습니다...T.T


< 왜 Tessellation 인가? >

조금 과장된 표현을 해서, 게임에서 폴리곤을 많이 사용하는 것은 범죄(?) 행위에 해당합니다.
그래픽 카드가 놀라운 속도로 발전을 하고 있지만,
아직도 게임 개발자들은 비디오 메모리의 부족을 호소하고 있습니다.
당연한 얘기지만, 이는 폴리곤 갯수와 퀄리티의 증가에 의한 것입니다.



위의 그림처럼 그래픽 카드는 약간 독특한 성능을 가지고 있습니다.

첫번째로 대역폭입니다.
CPU쪽 대역폭보다 훨씬 크기 때문에, 대량의 데이터를 전송할 수 있습니다.

두번째는 비디오 메모리가 시스템 메모리 보다 훨씬 작다는 것입니다.

세번째는 수치 연산과 병렬연산에 강한 GPU 라는 것입니다.
실제로 Tessellation 파이프라인 스테이지는 병렬적으로 처리됩니다.
( 다음 시간에 이에 대한 언급이 있을 것입니다. )

결과적으로 Tessellation 의 이점은
폴리곤 갯수를 줄임으로써 비디오 메모리 사용량을 감소시킵니다.
이는 결국 적은 데이터 전송으로 인해 대역폭을 절약할 수 있습니다.
하지만, Tessellation 은 GPU 의 성능에 좌우된다고 할 수 있습니다.
연산량이 실제로 많기 때문에, 정말이지 빠른 성능이어야 한다는 것입니다.
다행스러운 것은 GPU 의 성능이 비디오 메모리의 확장보다는 더 빨라지고 있다는 것입니다.

사실 Tessellation 에 대한 가장 큰 의구심은 '과연 빠를까?' 입니다.
이것에 대한 정답은 아직은 없습니다.
적절한 곳에서 사용한다면 유용할 수도 있을 것이고, 그렇지 않을 수도 있을 것입니다.
다만, 현재 DirectX 의 새로운 패러다임으로 Tessellation 이 선택되어졌으며,
좋은 성능을 위해서 꾸준히 노력할 것이라는 것입니다.^^

[JumpToDX11-12] DirectCompute 의 절차.

DirectX 11 2010. 4. 26. 09:00 Posted by 알 수 없는 사용자


앞선 시간에서 우리는 GPGPU 의 실행에 대한 간단한 개념에 살펴보았습니다.
이제 실제적으로 GPGPU 를 활용하는 절차를 살펴볼 차례입니다.
큰 절차는 다음과 같습니다.


 

< DirectCompute 의 초기화 >

가장 먼저 DirectCompute 를 초기화 해야 합니다.
 

hr = D3D11CreateDevice

(

     NULL,     // default gfx adapter

  D3D_DRIVER_TYPE_HARDWARE,  // use hw

     NULL,               // not sw rasterizer

     uCreationFlags,     // Debug, Threaded, etc.

     NULL,               // feature levels

     0,                  // size of above

     D3D11_SDK_VERSION,  // SDK version

     ppDeviceOut,        // D3D Device

     &FeatureLevelOut,   // of actual device

     ppContextOut );     // subunit of device

);

어디서 많이 본 API 라고 생각이 드시죠.
DirectCompute 를 초기화하는 작업은 바로 전통적인 CreateDevice() API 를 사용하는 것입니다.
즉, DirectX 를 사용하는 것입니다.
이로 인해서 DirectX 는 더욱 더 넓은 범위에서 활용되어 질 것입니다.


< HLSL 의 로드 >

그 다음은 실제적으로 GPU 가 실행을 하게될 로직을 로드할 차례입니다.
이것은 워낙 다양할 수 있는 부분이기 때문에, 여기서는 간단하게 예를 들겠습니다.


#define BLOCK_SIZE 256

StructuredBuffer   gBuf1;
StructuredBuffer   gBuf2;
RWStructuredBuffer gBufOut;

[numthreads(BLOCK_SIZE,1,1)]
void VectorAdd( uint3 id: SV_DispatchThreadID )
{
  gBufOut[id] = gBuf1[id] + gBuf2[id];
}


보통 이를 두고 ComputeShader 라고 합니다.
ComputeShader 를 위한 여러종류의 버퍼가 존재합니다.
더 많은 종류의 버퍼는 차후에 설명드리기로 하겠습니다.

StructuredBuffer 라고 정의된 키워드는 C언어의 구조체와 같은 구조를 가집니다.
즉, 개발자가 정의한 구조체입니다.
그런데 앞에 식별자가 없으면 읽기 전용이라는 의미입니다.
반면에 앞에 'RW' 라고 명시된 버퍼는 읽기/쓰기 가 가능한 버퍼라는 의미입니다.
우리는 GPU 가 처리한 결과는 읽기/쓰기가 가능해야 하기 때문에,
결과를 저장하는 버퍼는 'RW" 가 명시되어 있습니다.
최적화를 위해서 각 목적에 맞게 버퍼를 사용해야 할 것입니다.^^


< ComputeShader 의 생성 >

pD3D->CreateComputeShader( pBlob->GetBufferPointer(),
                                             pBlob->GetBufferSize(),
                                             NULL,
                                             &pMyShader );  // hw fmt


CreateComputeShader() API 를 통해서 간단히 ComputeShader 를 생성할 수 있습니다.


< 입력을 위한 GPU 버퍼 만들기 >

우리가 GPGPU 를 활용하는 것은 CPU 를 활용하는 것보다 빠르게 결과를 도출하기 위해서입니다.
이를 위해서는 GPU 가 빠르게 액세스할 수 있는 버퍼가 있어야 할 것이며,
당연히 이것은 비디오 메모리에 존재해야 할 것입니다.
그래서 우리는 DirectX 인터페이스를 통해서 비디오 메모리를 생성을 합니다.


D3D11_BUFFER_DESC descBuf;
ZeroMemory( &descBuf, sizeof(descBuf) );
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
desc.StructureByteStride = uElementSize;
desc.ByteWidth = uElementSize * uCount;
desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;

pD3D->CreateBuffer( &desc, pInput, ppBuffer );

주의해야 할 것은 바로 'BindFlags' 입니다.
'D3D11_BIND_UNORDERED_ACCESS' 라는 플래그를 주고 있습니다.
이것은 PixelShader 나 ComputeShader 에서 병렬적으로 실행하는 버퍼를 의미
합니다.


< 뷰를 만들자!! >

버퍼 리소스를 만들었으면, 이제 이를 실제 파이프라인에서 액세스할 수 있는 매커니즘을 만들어야 합니다.
즉, ShaderResourceView 를 만들어야 합니다.
DirectX10 부터는 아래와 같이 리소스들을 다루어야 합니다.



앞선 시간들을 통해서 View 라는 개념을 충분히(?) 숙지하셨을 것이라 생각합니다.^^
대체로 ShaderResourceView 는 파이프라인 스테이지에서 읽기 전용입니다.
그런데, 아래 UnorderedAccessView 는 양방향 화살표로 되어있습니다.
읽기/쓰기가 가능한 형태입니다.
이를 통해서 결과를 비디오 메모리에 있는 버퍼에 결과를 기록할 수 있음을 보여주고 있는 것입니다.


실제 API 를 통한 View 생성은 다음과 같습니다.

D3D11_UNORDERED_ACCESS_VIEW_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
desc.Buffer.FirstElement = 0;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Buffer.NumElements = uCount;

pD3D->CreateUnorderedAccessView( pBuffer, // Buffer view is into
                                                      &desc,  // above data
                                                      &pMyUAV ); // result


중요한 부분은 ViewDimension 부분입니다.
'D3D11_UAV_DIMENSION_BUFFER' 를 설정하고 있는데,
이는 ComputeShader 상에서 이 버퍼를 일반적인 버퍼로 보겠다
는 의미입니다.
즉, 샘플링 작업을 전혀하지 않습니다. 
이는 어떠한 수정도 없이 데이터를 있는 그대로 보존합니다.


< 실행 단계 >

이제까지는 모두 준비 단계였습니다.
이제는 실제 실행 단계에 대해서 언급해 보겠습니다.

먼저, ComputeShader 를 현재 파이프라인 스테이지에 아래와 같이 바인딩 해주어야 합니다.
pD3D->CSSetShader( pMyShader, NULL, 0 );


그 다음에는 뷰를 바인딩해야 합니다.
pD3D->CSSetUnorderedAccessViews( 0,
                                                        1,
                                                       &pMyUAV,
                                                       NULL );


이제 마지막으로 GPU 에게 현재 바인딩된 내용을 바탕으로 연산해 줄 것을 요청합니다.
pD3D->Dispatch( GrpsX, GrpsY, GrpsZ );


이제 실행의 단계가 모두 끝났습니다.
이 단계까지 끝나면, 실행 결과가 비디오 메모리에 존재합니다.
우리가 결과를 확인하기 위해서는 CPU 가 액세스할 수 있는 버퍼로 결과를 복사
해야 합니다.


< 결과 복사해 오기 >

결과를 CPU 가 액세스 하기 위해서는 어떻게 해야 할까요?
이전 시간을 통해서 언급드렸듯이,
DX10 부터는 리소스에 대한 세부적인 액세스 권한에 대한 플래그를 설정할 수 있습니다.
그래서 다음과 같은 설정으로 버퍼를 만듭니다.

D3D11_BUFFER_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.MiscFlags = 0;
pD3D->CreateBuffer( &desc, NULL, &StagingBuf );


'D3D11_CPU_ACCESS_READ' 라는 플래그를 통해서,
이 버퍼는 CPU 가 액세스 할 수 있는 형태로 만듭니다.
그리고 'D3D11_USAGE_STAGING' 라는 플래그를 통해서
단순히 비디오 메모리에 있는 내용을
CPU 도 접근 할 수 있는 메모리로 복사해오는 버퍼임을 명시
합니다.


그리고 아래와 같이, 준비해둔 버퍼에 실제로 메모리를 복사해 옵니다.
pD3D->CopyResource( StagingBuf, pBuffer );

마침내 우리는 GPU 에 의해서 처리된 결과를 확인할 수 있게 되었습니다.


< 마치며...>

지금까지 DirectCompute 를 활용하는 일련의 절차에 대해서 살펴보았습니다.
DirectX11 의 API 가 생소해서 어려워 보일 수 있지만,
실제로 DirectCompute 의 절차는 그리 복잡하지는 않습니다.
현재 DirectCompute 의 활용은 SDK 샘플에 'NBodyGravity' 라는 이름으로 들어있습니다.
제가 여기에 대해서 자세히 언급드리면 좋겠지만,
그것은 차후로 미루기로 하겠습니다.^^


참고 자료
http://microsoftpdc.com/Sessions/P09-16
본 내용은 위의 PDC 를 참고해서 만들었습니다.

[JumpToDX11-11] DirectCompute 를 위한 한걸음!

DirectX 11 2010. 2. 11. 09:00 Posted by 알 수 없는 사용자


앞선 시간을 통해서 GPGPU 를 위해서 마이크로소프트가 제공하는 플랫폼이
DirectCompute 라는 것이라고 말씀드렸습니다.
앞으로 DirectX11 을 지원하는 모든 그래픽카드들은 이 DirectCompute 를 지원할 것입니다.
그 이외에도 일부 DirectX10 을 지원하는 그래픽카드들도 지원을 하고 있습니다.


GPGPU 를 위해서 가장 기본적이고 핵심이 되는 기능은 무엇일까요?
저는 GPU 에서 처리된 메모리를 CPU 쪽의 메모리로 보내는 것이라고 생각합니다.
( 이는 개인 의견입니다.^^ )
즉, 그래픽카드에 있는 메모리를 메인메모리로 보내는 작업입니다.
DirectX9 세대까지는 이 작업이 불가능 했습니다.
예를 들면, 그래픽스 파이프라인 중간에 처리된 결과를 다시 가공할 수 있는 방법은
VertexShader 나 PixelShader 같은 쉐이더 스테이지 정도 뿐이였습니다.

하지만 DirectX10 부터는 이들에 대한 중간 결과를 메인메모리로 보내는 기능이 추가되어지면서,
GPGPU 의 시작을 알렸다고 생각합니다.
이 단순한 Copy 작업이 앞으로도 얼마나 유용하게 사용될 수 있을지는 기대가 상당합니다.



< DirectCompute 를 위한 ComputeShader >

DirectCompute 를 위해서 개발자가 할 일은 ComputeShader 를 작성하는 일입니다.
ComputeShader 는 HLSL 이라는 기존 DirectX 의 쉐이더 문법 구조로 작성을 합니다.




HLSL 코드는 DirectX 쉐이더 컴파일러인 FXC 나 API 를 통해서 컴파일 됩니다.
HLSL 은 결국 최적화된 IL 코드를 생성하게 되고,
이 IL 코드를 기반으로 런타임에 각각의 하드웨어에 최적화된 명령어들로 변환
되어져서 실행됩니다.


< GPGPU 에게 실행이란? >

GPGPU 를 활용해서 실행한다는 것은 하드웨어 내부적으로 어떻게 동작하도록 할까요?
앞선 시간에 GPU 는 병렬 처리에 최적화된 많은 SIMD 형태로 구성되어져 있다고 언급했었습니다.
결국 이들은 스레드들의 그룹으로써 실행합니다.
스레드들을 얼마나 많이 생성할 것인지를 개발자가 정해주면, 그에 맞게 연산을 수행합니다.

API 에서는 이들을 큰 그룹으로 나누어 줍니다.
큰 그룹으로 나누어 주는 API 는 ID3D11DeviceContext::Dispatch() 입니다.

ipImmediateContextPtr->Dispatch( 3, 2, 1 );

이렇게 큰 블럭 단위로 나누고 난 후에
ComputeShader HLSL 에서는 이들을 세부적인 스레들로 분할하는 문법을 지정합니다.

[numthreads(4, 4, 1)]
void MainCS( ... )
{
        ....
}




결과적으로 위의 그림처럼 스레드들이 생성되어서 병렬적으로 실행이 됩니다.
위에 나열된 숫자들은 스레드 ID 로써의 역활을 합니다.
즉, 어떤 스레드의 ID 가 MainCS 함수에 파라메터로 넘오오면,
그 ID 를 통해서 해당 버퍼에 값을 작성하게 됩니다.

아래에 간단한 예가 있습니다. 

[numthreads( 256,1,1) ]

void VectorAdd( uint3 id: SV_DispatchThreadID )
{

  gBufOut[id] = gBuf1[id] + gBuf2[id];

}


아무리 스레드들이 복잡하게 동작하더라도, 위와 같이 ID 를 통해서 제어한다면
그 어떤 작업도 문제없이 할 수 있습니다.

일단 먼저 어떻게 DirectCompute 가 실행되어지는지에 대해서 살펴보았습니다.
실행까지 가기 위해서는 일련의 절차를 거쳐야 합니다.
이들에 대해서는 앞으로 차근차근 살펴보겠습니다.



참고 자료
http://microsoftpdc.com/Sessions/P09-16
본 내용은 위의 PDC 를 참고해서 만들었습니다.

[JumpToDX11-10] GPGPU 를 위한 DirectCompute.

DirectX 11 2010. 1. 27. 09:00 Posted by 알 수 없는 사용자


아주 오래 전 컴퓨터에는 GPU 라는 개념이 특별히 존재하지 않았습니다.
그저 화면에 얼마나 많은 픽셀을 나타낼 수 있는가 정도가 그래픽 카드의 성능을 나타내는 기준이였습니다.
그랬던 상황이 오늘 날에 이르게 된 것입니다.( 굳이 자세히 언급할 필요가 없을 것 같습니다.^^ )

오늘날의 GPU 의 성능은 가히 놀라울 정도입니다.
하지만 이런 놀라운 성능을 가진 GPU의 processing unit 들이 대부분의 시간을 놀면서 있다는 것이
우리의 신경에 거슬렸던 것입니다.
그래서 이들에게 일감을 분배시키기 위한 방안을 생각하게 되었고,
이를 배경으로 등장한 것이 바로 GPGPU 입니다.

GPU 를 활용한 일반적인 처리 방식을
GPGPU( General-purpose computing on graphics processing uints ) 라고 합니다.
범용성 있게 GPU 를 활용해서 처리하겠다는 것이지만,
사실 CPU 와 GPU 의 목적은 엄연히 다릅니다.

CPU 는 광범위한 영역에서도 효율적으로 이용될 수 있도록 설계를 된 것이지만,
GPU 는 그래픽 처리를 위한 산술 연산에 특화된 processing unit 입니다.
오늘 날 PC 는 멀티코어 형식이 많아지고 있는 추세인데,
하나의 CPU 는 기본적으로 특정 시간에 하나의 연산만 수행할 수 있습니다.
GPU 의 경우에는 병렬처리 형식에 완전히 특화된 형태입니다.
오늘날 GPU의 코어는 32개라고 합니다.
즉 32개가 연산이 동시에 실행될 수 있다는 얘기입니다.
아래 그림을 한번 보실까요?




GPU 에는 SIMD 라는 것이 굉장히 많은 것을 볼 수 있습니다.
SIMD( Single Instruction Multiple Data ) 라는 것은 병렬 프로세서의 한 종류입니다.
벡터 기반의 프로세서에서 주로 사용되는데,
하나의 명령어를 통해서 여러 개의 값을 동시에 계산할 수 있도록 해줍니다.
( http://ko.wikipedia.org/wiki/SIMD  --> 여기서 참고 했습니다^^ )

벡터 기반이라는 사실에 우리는 주목할 필요가 있습니다.
GPU 는 광범위한 목적으로 설계된 processing unit 이 아닙니다.
즉, GPGPU 를 활용하는 목적은 주로 수치 연산에만 국한된 이야기 입니다.
일반적인 로직으로 GPGPU 를 활용하는 것은 그리 좋은 선택이 아니라는 것입니다.
현재 GPGPU 가 활용되고 있는 영역은 이미지 프로세싱, 비디오 프로세싱, 시뮬레이션 등과 같이
많은 수학 연산이 필요한 영역입니다.
분명한 것은 이들 수치 연산에 국한된 모델이라 할지라도, 그 성능이 무척 매력적이라는 것입니다.

이런 GPGPU 활용을 위해서 마이크로소프트는 어떤 준비물을 가지고 등장했을까요?
그것이 바로 'DirectCompute' 라는 것입니다.^^
아래 그림을 한번 보실까요?



DirectCompute 외에도 친숙한 이름이 보이시나요?
개인적으로 현재 GPGPU 분야에서 가장 앞서 있다고 보여지는 CUDA 가 있습니다.
이것들에 대한 우열을 가리기는 어려운 문제입니다.
여러분이 처한 상황에서 최선의 선택을 하면 되는 것입니다.
그 중에 DirectCompute 도 하나의 선택지일 뿐입니다.
CUDA 도 굉장히 훌륭한 GPGPU 모델입니다.
( 사실 저도 CUDA 를 공부하면서 GPGPU 의 개념을 잡았습니다.^^ )
CUDA 는 제가 지금 언급하지 않아도 될 정도로 많은 정보들이 공개되어 있습니다.

DirectCompute 는 마이크로소프트에서 가지고 나온 GPGPU 모델입니다.
앞으로 OS 의 강력한 지원을 가지고 등장하게 될 것입니다.

사실 GPGPU 와 DirectCompute 는 매우 혼란스럽게 사용될 수 용어들입니다.
그래서 오늘은 이들 두 용어를 확실히 구분하는 것으로 마무리 하겠습니다.^^
다음 시간부터는 DirectCompute 에 대해서 조금씩 살펴보겠습니다.


참고 자료
http://microsoftpdc.com/Sessions/P09-16
본 내용은 위의 PDC 를 참고해서 만들었습니다.

[JumpToDX11-9] Multi-threaded Rendering 을 위한 API.

DirectX 11 2010. 1. 11. 09:00 Posted by 알 수 없는 사용자



이번 시간에는 Multi-threaded Rendering 을 위한 API 들에 대해서 살펴보겠습니다.
기능 위주의 설명을 위해서 인자들에 대한 명시는 생략했습니다.
이점 주의해주시기 바랍니다.

ID3D11Device::CreateDeferredContext()

가장 먼저 살펴볼 것은 DeferredContext 의 생성입니다.
DeferredContext 는 스레드당 하나씩 생성되어질 수 있음을 앞선 시간을 통해서 언급했습니다.
또한 이 DeferredContext 는 Command List 들을 생성해서 가지고 있습니다.
즉, 렌더링이 가능한 상태라는 것입니다.
그런 기능을 우리는 Device 인터페이스를 통해서 생성합니다.
이것은 역시 Free thread 한 작업이기 때문에 Device 인터페이스를 이용합니다.

하나의 DeferredContext 는 thread-safe 합니다.
즉, 스레드 상에서 DeferredContext 가 관련 Command 들을 기록하는 것은 안전한 작업입니다.

간단한 사용 방법은 아래와 같습니다.
ID3D11DeviceContext* pDeferredContext = NULL;
hr = g_pd3dDevice->CreateDeferredContext(0, &pDeferredContext);


ID3D11DeviceContext::FinishCommandList()

신기하게도 우리는 이 API 호출 한번으로 CommandList 들을 기록하고 생성할 수 있습니다.
API 이름이 Finish 여서 Start나 Begin 계열의 API 를 검색해 보았지만, 없었습니다.^^
각각의 DeferredContext 별로 호출되기 때문에 DeviceContext 의 멤버함수로 되어 있습니다.
앞선 시간을 통해서 DeviceContext 는 ImmeidateContext 와 DeferredContext 로
분리될 수 있다고 언급했었습니다.
두 Context 모두 ID3D11DeviceContext 인터페이스를 사용하기 때문에 오해의 소지가 약간 있습니다.
FinishCommandList 는 DeferredContext 를 위한 API 임을 유념하시기 바랍니다.

간단한 사용 방법은 다음과 같습니다.
ID3D11CommandList* pd3dCommandList = NULL;
hr = pDeferredContext->FinishCommandList( FALSE, &pd3dCommandList );


ID3D11DeviceContext::ExecuteCommandList()

이 API는 DeferredContext 에 의해서 생성된 CommandList 들을 실행합니다.
역시나 ID3D11DeviceContext 의 멤버함수이기 때문에 혼란스러울 수 있습니다.
과연 ImmediateContext 가 이 함수를 호출할까요? 아니면, DeferredContext 일까요?

지난 시간들을 통해서 우리는 실제로 Multi-threaded Rendering 이라는 것은
CommandList 생성을 Multi-thread 기반으로 하는 것이라고 언급했었습니다.
그 이후에 실제 그래픽 카드로의 전송은 하나의 스레드만 할 수 있다고 했었습니다.
바로 그 사실입니다.
이 함수는 ImmediateContext 에 의해서 호출됩니다.
즉, 이 API 는 그래픽 카드로 해당 CommandList 들을 전송하는 것입니다.

간단한 사용 방법은 아래와 같습니다.
g_pImmediateContext->ExecuteCommandList( g_pd3dCommandList, TRUE );


이상 3가지 API 에 대해서 살펴보았습니다.
믿기지 않으시겠지만(?)
Multi-threaded Rendering 작업은 이 세가지 API로 할 수 있습니다.
나머지는 스레드 생성과 제어를 위한 작업이 결합되어야 할 것입니다.
일반적인 스레드 프로그래밍과 관련된 내용이라 이곳에서는 배제를 했습니다.
현재 DirectX SDK Sample 에는 'MultithreadedRendering11' 라는 것이 있습니다.( 2009 August 버전 기준 )
이것과 관련된 소스가 있으니 참고해서 보시면 좋을 것 같습니다.

이상으로 Multi-threaded Rendering 의 기본 개념 설명을 마치고자 합니다.
이 부분과 관련된 내용은 앞으로 정리가 되는대로 추가하거나 수정이 되어질 수 있을 것입니다.
다음 시간부터는 DirectX11 의 다른 주제를 가지고 돌아오겠습니다.^^
 

[JumpToDX11-8] Deferred Contexts

DirectX 11 2009. 12. 2. 09:00 Posted by 알 수 없는 사용자

멀티스레드 기반의 렌더링을 위해서 DirectX11 에서는 세가지를 중점에 두었습니다.

- 비동기적으로 자유스러운 리소스 로딩과 관리.
  ( 이들은 렌더링과 동시에 수행될 수 있어야 한다. )

- 멀티스레드 형식으로 렌더링 커맨드의 생성.
  ( 여러 스레드로 나누어서 렌더링 작업을 할 수 있어야 한다. )

- 디스플레이 리스트( Display lists )의 지원.


첫번째 리소스와 관련한 것을 지난 시간에 알아보았습니다.
이제는 실제 렌더링 작업에 대해 알아보아야 합니다.
그 실제 렌더링 작업을 위해서 우리는 새롭게 등장한 Deferred Context 라는 것을 살펴볼 것입니다.
Deferred Context 가 멀티스레드 기반의 렌더링에서 가장 중요한 키워드입니다.

< Device 의 분리 >
지난 세대의 DirectX는 모든 GPU 관련 내용의 처리는 Device 인터페이스를 통해서 수행했습니다.
즉 지난 회들에서 꾸준히 언급했던 것처럼,
Device 인터페이스를 통해서만 커맨드( Command ) 를 생성할 수 있었습니다.



오직 싱글코어 기반으로 설계된 지난 세대의 DirectX 였기 때문에,
위의 그림과 같은 상황이 연출되었습니다.
이들에 대한 내용은 지난 시간을 통해서 꾸준히 언급되었기 때문에, 더 자세한 언급은 하지 않겠습니다.

Device 인터페이스에 모든 작업이 집중되어 있었기 때문에,
이를 분리할 방법이 필요했습니다.
그 기준은 앞서 언급했듯이, 
그래픽카드에 보내는 작업이 Free threaded 한지였습니다.
결론적으로 얘기 드리면 DirectX11 에서는 기존의 Device 가 분리에 분리를 거듭했습니다.
그래서 아래와 같은 구조로 되었습니다.





DirectX11 에서 이제 개발자가 다루어야하는 커맨드 생성 인터페이스는 총 3가지입니다.
Device 는 Free threaded 한 API 만 사용하는 인터페이스입니다.
주로 리소스들이 포함됩니다.( 버퍼나 텍스쳐, 쉐이더 등 )

Device Context 는 실제로 렌더링과 관련된 인터페이스입니다.
렌더스테이트의 교체나 Draw 명령을 내리기 위해서는
반드시 Device Context 를 통해서 커맨드를 생성해야 합니다.
더 자세한 사항은
http://vsts2010.net/115 여기를 보시기 바랍니다.^^

Device Context 는 다시 두개로 분리될 수 있습니다.
Immediate Context 와 Deferred Context 가 바로 그것들입니다.

만약 멀티스레드 기반으로 렌더링 하고 싶지 않다면,
Deferred Context 는 사용하지 않으셔도 됩니다.
Deferred Context 의 유무가 바로 멀티스레드 기반의 렌더링이냐,
아니면 일반적인 렌더링이냐를 결정
합니다.
반면에 Immediate Context 는 반드시 한개만 존재해야 합니다.
이 인터페이스는 실제로 렌더링과 관련된 커맨드를 생성하기도 하지만,
생성된 커맨드를 그래픽 카드로 보내는 일
도 합니다.

< Deferred Context >

Deferred Context는 애플리케이션에서 여러개 생성될 수 있습니다.
하나의 스레드에 하나씩 Deferred Context 가 사용될 수 있으며, 이들은 Thread unsafe 합니다.
이렇게 하나의 스레드에 할당되어진 Deferred Context는 Display List 를 생성합니다.
이들은 GPU 가 바로 처리 가능한 커맨드들을 모아둔 버퍼라고 할 수 있습니다.
이렇게 Display List 를 미리 만들어둠으로써 성능을 크게 개선 시킬 수 있습니다.
일반적으로, CPU 가 커맨드를 생성시키는 시간이 꽤 오래 걸리기 때문입니다.
( 생성될 커맨드가 변화가 없다면, 이렇게 미리 만들어 두면 크게 도움이 되겠죠? ^^ )


사실 위의 그림은 개념적인 것입니다.
실제 소스레벨에서 Deferred Context 를 가리키는 인터페이스는 별도로 존재하지 않습니다.
Immediate Context 를 가리키는 인터페이스는 ID3D11DeviceContext 입니다.
Deferred Context 를 가리키는 인터페이스도 ID3D11DeviceContext 입니다.
즉, 둘 다 동일한 인터페이스를 통해서 처리되고 있는 것입니다.
실제 멤버 변수 선언들을 조금 나열해 보면 다음과 같습니다.

ID3D11Device*      m_ipGPU;
ID3D11DeviceContext*    m_ipImmediateContext;
ID3D11DeviceContext**  m_ippDeferredContextArray;

동일한 인터페이스 선언을 확인하셨습니까?
하지만 인터페이스가 동일하다고 해서, 이를 동일하게 생각해서는 안됩니다.
동일한 인터페이스를 사용하는 이유는
Deferred Context 는 Immediate Context 의 모든 기능을 지원한다는 의미로 받아들여야 합니다.
결과적으로 Device Context 는 아래의 그림과 같은 구조로 확장될 수 있습니다.




멀티스레드 기반으로 렌더링을 한다는 것은 엄밀히 말해서는 지원하지 않습니다.
정확히 말하자면, 멀티스레드 기반으로 커맨드를 생성해서
이들을 순차적으로 그래픽 카드로 보냅니다.

위의 그림이 이를 잘 표현하고 있습니다.

Deferred Context 는 각각의 스레드에 의해서 DisplayList 를 생성하고,
이들을 그래픽 카드에 보내기 위해 버퍼에 저장합니다.
그리고 실제로 그래픽카드에 커맨드를 보내기 위해서는
반드시 Immediate Context 를 통해야 합니다.
이때 Immediate Context 를 통해서 직접적으로 커맨드를 생성시킬 수 있습니다.

아무래도 렌더링이라는 작업은 순간순간의 렌더링 상태들에 의해서
결과가 변하기 때문에, 최종적으로 전송하는 작업만큼은 순차적으로 설계한 듯 합니다.
이로써 우리는 Deferred Context 를 사용할 준비가 되었습니다.^^