[미리보는 C++ AMP-1] C++ AMP를 구상해 보다!

DirectX 11 2011. 11. 22. 08:00 Posted by 알 수 없는 사용자

GPU를 활용하는 일은 모든 개발자에게 열려있는 길이여야 합니다.
하지만 DirectX를 직접적으로 활용해야만 하는
MS의 GPGPU 플랫폼인 DirectCompute는 그렇지가 않습니다.

그래픽카드라는게 원래 특수한 목적성을 가지고 등장한 장치이기 때문에,
이를 활용하는 사람들 또한 특정 영역에 국한되어 있는게 현실입니다.
'이제부터 GPGPU 를 적극 활용합시다!' 라고 생각을 하더라도, 
실제로 그것을 활용하기 위한 진입 장벽은 굉장히 높을 수 밖에 없습니다.

그러면 어떻게 해야만 이 장벽을 조금이라도 낮출 수 있을까요?
엔비디아의 CUDA 를 보면, 힌트가 있습니다.
하지만 몰라도 상관없습니다.^^
C++ 파일 내에서 컴파일러에 의해서 자동적으로 처리가 될 수 있으면 가장 좋지 않을까요?
순수 C++ 의 기능만 사용해서 컴파일러가 자동적으로 처리해 준다면,
개발자는 DirectX와 ComputeShader 에서 해방될 수 있을 것입니다.
그것이 바로 C++ AMP 가 등장하는 배경
입니다.
C++ AMP는 다음 버전의 VisualStudio 에 탑재 되어져서 등장할 예정이라고 합니다.


어떤 함수가 아래와 같이 있습니다.
void Func( ... )
{
    코드
}

위의 함수는 결국 컴파일러에 의해서 CPU 와 관련한 명령어를 생성하게 됩니다.
이를 AMP 적으로 확장하면 정확히 아래와 같이 구성됩니다.
void Func( ... ) restrict( cpu )
{
   코드
}

restrict 이라는 키워드를 함수에 적용함으로써 간단히 이를 구현합니다.
눈치가 좀 빠르신 분들이라면
'저 cpu를 gpu 로만 변경하면, gpu 로 컴파일 되어지는 것인가?' 라고 생각이 드실 겁니다.
네. 맞습니다.
그것이 바로 C++ AMP 가 DirectCompute 를 구현하는 방법입니다.
정확히는 아래와 같습니다.
void Func( ... ) restrict( direct3d )
{
   코드
}
'direct3d' 가 바로 'gpu' 를 의미합니다.
현재 이 옵션용 예약어는 확정적인 것은 아닙니다.
'direct3d' 가 확정될 수도 있고, 그렇지 않을 수도 있습니다.
아직 C++ AMP가 출시되지 않아서 유동적인 부분이 있습니다.
그 점 주의해서 읽어주시기 바랍니다.^^

다음 버전의 Visual C++ 부터는 
함수마다 저렇게 restrict 한정자에 컴파일 옵션을 지정해주어야 합니다.

물론 지정을 하지 않았을 때는, 디폴트로 restrict( cpu ) 로 자동 처리할 것입니다.

그러면 한 함수 내에서 CPU와 GPU를 활용해야 하는 경우는 어떻게 해야할까요?
void Func( ... ) restrict( direct3d, cpu )
{
   GPU를 사용하는 코드
   CPU를 사용하는 코드
}

위와 같이 혼합해서 사용하는 것도 가능합니다.
또한 오버로드와 관련한 이슈도 문제 없이 처리될 것입니다.
void Func( ... );
void Func( ... ) restrict( direct3d );

간단히 위와 같이 restrict 만으로 GPU를 사용하는 것이 완전히 된다면 얼마나 좋겠습니까만,
restrict( direct3d ) 로 정의되어지는 함수들은 그에 상응하는 규칙으로 코딩 작업을
해야만 합니다.
이것이 사실 그렇게 쉬운 개념만으로 이해할 수 있는 것은 아닙니다.
하지만 DirectCompute를 직접 제어하는 것보다는 쉽습니다.

다음 시간부터 C++ AMP 로 프로그래밍 하는 개념에 대해서 살펴보겠습니다.^^

이번 시간에는 지난 시간들까지 언급한 내용을 기반으로 해서,
간단한 테셀레이션 작업을 구현해 보려 합니다.

당연한 얘기이겠지만,
하드웨어 기반의 테셀레이션은 하드웨어의 지원이 없으면 매우 느립니다.
즉 DirectX11 이상을 지원하는 그래픽 카드가 아니면,
효과를 눈으로 확인하는 것조차 무척 고통스럽습니다.

그래서 이번 시간에 만들 테셀레이션은 간단히 삼각형 하나를 이용합니다.
우리는 이 삼각형 하나를 가지고 테셀레이션 작업을 수행할 것이며,
DirectX11 을 지원하지 않는 그래픽카드라면
강제적으로 REF 모드로 테셀레이션 작업을 수행하도록 합니다.

먼저 결과 샘플을 보면 아래와 같습니다.



이제 우리가 만들려는 그림이 그려졌으니, 직접 코딩 작업을 시작하겠습니다.
이 글에서는 DirectX11 의 기본 셋팅과 관련한 사항은 생략합니다..^^
자세한 API 적인 설명은 생략을 하니 DirectX 2010 6월 버전의 SDK 의 튜토리얼을 참고하시거나,
'알코코더의 DirectX11' 을 참고하시기 바랍니다.^^

우리가 이번 샘플에서 사용할 버텍스 데이터의 형식은 위치 정보만 있으면 됩니다.
이번 샘플에서는 최대한 간단하게 작성하는 것을 목적으로 했기 때문에,
많은 정보를 필요로 하지는 않습니다..^^
그래서 아래와 같이 간단한 버텍스 형식을 정의했습니다..^^



생소한 데이터 타입이 보입니다. 바로 XMFLOAT3 입니다.
DirectX11 부터는 D3DX 계열의 수학 데이터 타입들은 더 이상 업데이트 되지 않습니다.
지금부터는 XNA Math 라는 수학 라이브러리를 사용합니다.
그렇다고 더 이상 D3DX 계열의 수학 데이터 타입들을 사용할 수 없는 것은 아니니, 안심하시기 바랍니다.
이들에 대해서는 향후 언급할 기회가 있으니,
지금은 D3DX 계열의 수학 클래스 대신에 XNA Math 라는
새로운 수학 클래스를 사용한다는 정도로만 인식하고 넘어가겠습니다.^^


아래는 우리가 애플리케이션 전역으로 사용할 변수들의 선언입니다.



그 동안의 DirectX11을 언급하면서 꾸준히 언급되던 내용이기에 자세한 설명은 생략하겠습니다.

특이할 만한 것이라면, 래스터라이져 스테이트 오브젝트를 2개 만드는 것입니다.
이는 우리의 샘플이 솔리드( Solid ) 한 렌더링과 와이어프레임( Wire-Frame ) 기반의 렌더링으로
전환이 가능하기 때문입니다.

다음은 상수버퍼( ConstantBuffer ) 에 관한 전역 선언들 입니다.



우리는 월드 좌표계의 정점을 버퍼에 입력할 것입니다.
그래서 View-Projection 행렬만 변환을 위해서 필요합니다.
그리고 얼마나 테셀레이션 작업을 세밀하게 할지를 결정하는 상수를 하나 추가합니다.



쉐이더를 컴파일 해주는 보조 함수를 다음과 같이 하나 만듭니다.


이제 본격적으로 시작을 합니다.
InitD3D() 에 각종 초기화 작업을 수행합니다.
앞서 잠깐 언급드렸듯이,
DirectX11을 지원하는 하드웨어가 아니면, 강제로 REF 모드로 동작하도록 합니다.
또한 이 함수에서는 각 쉐이더 스테이지에 대응되는 HLSL 코드를 컴파일 해줍니다.
그리고 이들에 대한 각 오브젝트를 만듭니다.
초기화 작업은 주로 반복적인 작업이 많기 때문에, 설명은 생략합니다.

InitD3D() 에 버텍스버퍼의 데이터를 설정해 줘야 합니다.
이번 샘플에서는 월드 좌표로 정의된 삼각형을 사용할 것입니다.
또한 카메라 공간에 대한 설정도 같이 해 줍니다.
이들에 대한 코드는 아래와 같습니다.


이 정도로 초기화와 관련된 작업을 마무리 합니다.
이제는 프레임 관련한 처리를 작성합니다.( Render() )

이 Render() 부분에서는 상수버퍼에 설정할 데이터들을 다음과 같이 업데이트 합니다.

 


우리는 와이어프레임 모드와 솔리드 모드의 렌더링 방식 둘 다를 표현할 것이기에,
이들에 대한 설정도 아래와 같이 고려해 주어야 합니다.



그리고 마지막으로 입력되는 버텍스 형식을 알려주고 버텍스 버퍼를 연결한 후에,
그리기 작업을 수행합니다.^^



이제 키보드 이벤트에 따라 약간의 변화를 주는 작업을 합니다.
현재는 'w' 키로 렌더링 모드를 Wire 와 Solid 간의 토글이 되도록 설정합니다.
그리고 위/아래 방향키로 테셀레이션의 분할 정도를 증감합니다.

이번 작업은 여기까지 입니다.
지금까지 DX11을 살펴보면서, 언급된 내용들이 대부분이라 전체적으로 설명드리지는 않습니다.
( HLSL 코드도 최대한 간결하게 작성했습니다..^^ )
샘플을 같이 첨부드리니, 직접 작성하시면서 익혀보시기 바랍니다.^^

[JumpToDX11-1] 사라진 Direct3D 오브젝트를 찾아서...

DirectX 11 2009. 8. 17. 14:00 Posted by 알 수 없는 사용자

< 인사 및 소개 >

안녕하세요.
저는 이번에 vsts2010 에 참여하게 된 조진현 이라고 합니다.

어떤 주제에 대해서 글을 쓰다는 것은 무척 어려운 일입니다.

그렇기 때문에, 이 스터디 참가를 굉장히 망설이기도 했습니다.
많은 분들과 함께 열정을 가지고 참가를 결심했고, 드디어 처음으로 글을 남기게 되었습니다.
제가 가장 우려하는 것은 잘못된 지식을 전달하는 것입니다.
그래서 조심스러운 마음으로 글을 작성할 것입니다.
잘못된 부분이나 미흡한 부분이 있으면, 바로 지적해주시면 감사하겠습니다.

제가 언급할 큰 주제는 DirectX 11 과 관련이 있습니다.
그 중에서도 멀티 코어를 활용한 DirectX 사용에 초점을 두고 글을 전개할 생각입니다.
글의 주요 대상은 DirectX9 를 사용하시다가 DirectX11 을 사용하고자 하시는 분들입니다.

일단 방대한 변화에 대해서 모두 나열하기는 힘듭니다.

그래서 간단히 제가 코딩을 하면서 필요했던 API 위주로 살펴보면서 변화를 언급하고자 합니다.
그런데 하나 문제가 있습니다.
현재 DirectX 11 은 하드웨어 가속이 지원되지 않습니다.
오직 REF 모드로만 작동을 합니다.
아마도 아직 정식으로 widnows 7 이 출시가 이루어지지 않아서 그런 듯 합니다.
이점, 꼭 주의하시기 바랍니다.
괜히 DirectX 11 예제 실행했다가, 실행 성능이 떨어진다고 컴퓨터를 부수는 행위는 자제해 주세요.^^


< 사라진 Direct3D 오브젝트를 찾아서... >

우리가 가장 먼저 접하게 되는 DirectX 의 API 는 CreateDevice() 일 것입니다.
사실 이전 버전까지는 CreateDevice() 에 대해서 별도로 언급할 내용이 없었을 것이지만,
늘(?) 그렇듯이 DirectX 의 변화를 설명해주는 API 가 바로 CreateDevice() 입니다.
일단 CreateDevice() 를 위한 관련 변수들부터 봐야겠죠?
 



잠깐!!
가장 먼저 헤더 파일들을 살펴보는게 순서이죠.

헤더는 다음과 같이 변경되었습니다.
굳이 헤더의 용도에 대해서 일일이 나열하지는 않았습니다.

// Direct3D11 includes
#include <dxgi.h>
#include <d3d11.h>
#include <d3dCompiler.h>
#include <d3dx11.h>
#include <dxerr.h>

 

라이브러리 링크는 아래의 것들을 해주시면 됩니다.

#pragma comment( lib, "dxguid.lib" )
#pragma comment( lib, "d3dcompiler.lib" )
#pragma comment( lib, "dxerr.lib" )
#pragma comment( lib, "dxgi.lib" )
#pragma comment( lib, "d3d11.lib" )
#pragma comment( lib, "d3dx11.lib" )




변수들을 나열해 보겠습니다.




 생소한 부분이 눈에 보이시나요?
 'ID3D11DeviceContext' 라는 것이 새롭게 등장했습니다. ( 다음 번에 언급할 것입니다. )
 그리고 Direct3D 인터페이스가 사라진 것을 찾으셨습니까?




위의 그림은 DirectX 9 의 아키텍쳐입니다.
우리가 작성하는 프로그램은 오직 Direct3D 나 GDI 를 통해서 저수준의 하드웨어와 통신을 할 수 있었습니다.

그런데 현재의 DirectX 아키텍쳐는 아래와 같습니다.



 여기서 또 하나 생소한 것이 등장했습니다.
바로 DXGI ( DirectX Graphics Infrastructure ) 입니다.
"DirectX9 에서 사라진 'Direct3D 오브젝트'를 'DXGI' 가 대체하는게 아닐까?" 라는 의문이 들었다면,
박수를 보내드리고 싶습니다.( 브라보~~ )


네, 맞습니다.
'DXGI' 라는 것이 바로 사라진 'Direct3D 오브젝트' 입니다.
'Direct3D 오브젝트' 의 역활에 대해서 혹시 기억하십니까?
하드웨어와 연결된 디바이스들을 나열하고, 모니터로 출력되는 결과들을 관리해주기도 했었습니다.
우리가 관리하기 힘든 저 수준의 작업들을 바로 이 'Direct3D 오브젝트'가 했었습니다.
그런데 이제는 이것을 'DXGI' 가 해주고 있습니다.
( IDXGISwapChain 보이시나요? 이것도 다음 회에 언급하겠습니다. )
 

아키텍쳐 구조를 보시면 아시겠지만, DirectX9 까지는 일반 애플리케이션에서 DirectX API 를 통하지 않고는
DirectX 를 사용할 수 없었습니다.
그런데 최근에는 일반 애플리케이션은 모두 DXGI 를 통해서 DirectX 를 사용하고 있습니다.
( 저만 놀라운 것은 아니겠죠? +_+ )
마이크로소프트에서도 강조하고 있는 사실 중에 하나가 바로 DirectX 는 더 이상 게임만을 위한 것이 아니라는 것입니다.
이제 사라진 줄 알았던 'Direct3D 오브젝트' 가 DXGI 라는 사실을 알았습니다.
앞으로 저수준의 작업이 필요하면 DXGI 를 직접 제어하거나 DirectX API 를 이용하셔도 됩니다.


< 다음 회에는... >

다음 번에는 실제로 DirectX API 를 이용한 초기화 작업에 대해서 다루고자 합니다.
즉, 우리가 앞서 선언했던 변수들에 대한 이야기를 하겠습니다.

'DirectX 11' 카테고리의 다른 글

[DX11_#2]D3D Buffer( 2 / 2 )  (0) 2009.10.13
[DX11_#1]D3D Buffer( 1 / 2 )  (0) 2009.09.22
[JumpToDX11-4] ID3D11View  (0) 2009.09.07
[JumpToDX11-3] Feature Level  (0) 2009.08.31
[JumpToDX11-2]DeviceContext...넌 누구냣!!  (1) 2009.08.24