Visual Studio 2010 공식 팀 블로그 @vsts2010

Posted by 이석준 도플광어(圖佛光語)

닷넷기반위에서 게임프로그래밍을 하기 위해서 기존네이티브코드로된 엔진을 매나지드방식으로 불러오는 방법을 생각해볼수있습니다. 
저는 오거,일리히트,네뷸러,판다3D등의 공개엔진중에서 일리히트엔진을 선택했는데요.

가장 중요한 이유로 유연성이 가장 좋은 엔진을 선택 했습니다. 오거도 멀티 플랫폼이지않느냐 반문하실수도있겠는데요. 제가 말하는건 멀티 플랫폼뿐만 아닙니다. 언어적으로도 유연해야된다고 봅니다. 일리히트를 표현 하는데 언어는 중요치 않습니다. c++로 구현된 예가 있을뿐 c++기반은 아니기 때문입니다.(영어로 쓰여진 성서책이있을뿐 성서가 영어는 아니죠.) ,irrlicht 는 단순한 게임엔진이 아닌 게임엔진 구조 설계 철학이라고 이해 하시면 됩니다.

이런 철학은 닷넷과 궁합이 잘맞아 떨어집니다.


일리히트엔진 공식 프로잭트 싸이트는 http://irrlicht.sourceforge.net 입니다. 여기서 정식 버전(1.7.1)을 릴리즈하고있으며 현제는 1.8버전을 준비중에 있습니다. 여러 언어와 바인딩한 예가있는데요. 그중에서 닷넷기반 으로 바인딩하는 방법에 대해서는 일리히트 포럼에 가셔셔 먼저 irrlicht lime을 검색하시면 관련 정보를 얻으실수있습니다.

소스포지 프로잭트가 위와같이 진행되고있으며 현재(2010.7) 버전 0.5가 나온상태입니다.

접근방식은 네이티브코드쪽 irrlicht.dll을 중간단계의 매나지드코드dll인 irrlichtlime.dll을 사용해서 닷넷기반의 매나지드코드로 접근 하는 방식을 사용했습니다.
이 방법의 최대장점은 이해하기 쉽고 단순하다는것입니다. 또한 그다지 큰단점도 없는듯합니다.




위의 소스 코드는 네이티브 일리히트코드중에 createDevice함수를 랩핑한 예를 보여줍니다. 



매나지드코드용 랩퍼 dll프로잭트를 만들때는 위와같이 CLR을 사용한다는 옵션을 줍니다.




라임솔루션 디랙토리 모양입니다. irrlichtLime프로 잭트가 랩퍼 입니다. 결과물은 irrlichtLime.dll이 나옵니다.

연습으로 간단하게 화면에 큐브를 돌려보는 예제를 만들어 보겠습니다.
먼저 프로잭트를 추가합니다.


콘솔 어플로 만듭니다.





라임을 컴파일하면 매나지드 랩퍼(irrlichtLime.dll)가 /bin 디랙토리상에 존재하므로 실행파일이 같은 곳에 있도록 지정해줍니다.

리퍼런스를 추가 합니다.


브라우져 탭으로 가셔셔 /bin 디랙토리에 있는 irrlichtLime.dll을 선택합니다.



그리고 소스를 다음과 같이 편집해 줍니다.(program.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            IrrlichtLime.IrrlichtDevice device = IrrlichtLime.IrrlichtDevice.CreateDevice(IrrlichtLime.Video.DriverType.Direct3D9, new IrrlichtLime.Core.Dimension2Di(640, 480));

            IrrlichtLime.Video.VideoDriver driver = device.VideoDriver;
            IrrlichtLime.Scene.SceneManager smgr = device.SceneManager;

            IrrlichtLime.Scene.SceneNode node = smgr.AddCubeSceneNode();

            node.SetMaterialFlag(IrrlichtLime.Video.MaterialFlag.Wireframe, true);
            node.SetMaterialFlag(IrrlichtLime.Video.MaterialFlag.Lighting, false);

            IrrlichtLime.Scene.SceneNodeAnimator anim = smgr.CreateRotationAnimator(new IrrlichtLime.Core.Vector3Df(0, (float)3.14/4, 0));
            node.AddAnimator(anim);
            anim.Drop();

            smgr.AddCameraSceneNode(null, new IrrlichtLime.Core.Vector3Df(0, 10, -50), new IrrlichtLime.Core.Vector3Df(0, 0, 0));

            while (device.Run())
            {
                driver.BeginScene(true, true, new IrrlichtLime.Video.Color(100, 101, 140));

                smgr.DrawAll();

                driver.EndScene();
            }

            device.Drop();
        }
    }
}

실행 결과입니다.



위의 내용은 vs2010 과  .Net프래임웍 4.0 을 사용해서 구현했습니다.
저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

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 에서의 테셀레이션 작업에 대해서 살펴보겠습니다.^^
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Posted by simon882


 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

크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 동영상 보고 눈이 휘둥굴해 졌답니다.
    DX11 좋네요...
    많은 게임이 DX11 으로 ㄱㄱㄱ^^

  2. 날이 갈수록 DirectX의 성능이 좋아지고 있군요. =_=

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 이 선택되어졌으며,
좋은 성능을 위해서 꾸준히 노력할 것이라는 것입니다.^^
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

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 를 참고해서 만들었습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

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 를 참고해서 만들었습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

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 를 참고해서 만들었습니다.
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. CUDA는 C/C++ 로 사용을 해야해서.. CUDA.NET을 이용해본적이 있는데..
    Directcompute 를 이용하는 것과 속도차이라던지.. 실제 코드를 작성할 떄 용이성?이라던지.. 장단점이 있는건가요?

    보통 레퍼런스들이 전부 CUDA 소스들이라서.. 왠만해서는 게임이나 CAD 같은..분야 외에는 안쓰게 될수도 있을것 같아요....ㄷㄷ

    시뮬레이션 속도는 좋기야 하지만.. 계산쪽은 대부분 유닉스나 리눅스계열이라..호환이 되지 않고서는..;;

  2. 조진현 2010/02/12 10:09

    CUDA와 DirectCompute 의 속도 차이는 거의 없을 것이라고 생각이 듭니다.
    어차피 구조 상에서 GPU 를 활용해야 하는 것이기 때문에
    두 플랫폼의 성능 상 우위를 논하기는 어려운 문제인 것 같습니다.
    사실 DirectCompute 는 아직 정식으로 SDK 에 포함된 내용이 아닙니다.
    그래서 아직 공개된 자료가 거의 없는 상황입니다.
    CUDA 의 경우에는 이미 몇년전에 공개가 되어서 지금 가장 앞선 GPGPU 환경입니다.

    CUDA 의 이점은 저보다 아시는 분들이 더 많으시겠지만,
    저는 개인적으로는 XP 에서도 된다는 것입니다.
    DirectCompute 는 XP 에서 실행이 되지 않습니다.
    물론 DirectCompute 를 지원하는 그래픽카드에서만 실행되겠지만,
    그 또한 OS 에서 지원해 주지 않으면, 실행되지 않는다는 것입니다.
    반면 CUDA 쪽은 XP 지원에 대한 포럼이 있을 정도로 활용되고 있는 상황입니다.

    CUDA 의 단점은 역시 자사의 그래픽카드만 실행할 수 있다는 것입니다.
    엔비다아 계열의 카드만 지원되기 때문에 게임과 같은 대중을 목표로 하는 프로젝트에서는 쉽게 적용하기 어렵습니다.
    DirectCompute 의 경우에는 모든 밴더들이 이를 지원해야하는 좀 강제적이기 때문에 게임쪽에서는 DirectCompute 를 선택하는 것이 좋을 것이라 생각이 됩니다.

    DirectCompute 는 아직 문법적인 내용은 많이 공개가 되지 않았지만,
    HLSL 이라는 쉐이더 기반의 언어를 사용하기 때문에,
    아무래도 게임 개발자들쪽에 포커스가 간 것이 사실입니다.
    아직 계속 발전 중인 분야이니, 좀 더 지켜보면 좋을 것 같습니다.^^

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 의 다른 주제를 가지고 돌아오겠습니다.^^
 
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Posted by 이석준 도플광어(圖佛光語)

<<연재 일시 중단>>

vs 2010 beta2 가 스마트기기 개발환경을 지원하고 있지 않은 관계로 연재를 일시 중단 합니다. 

미리 확인 하지 않고 연제를 시작한 점 사과드립니다.

차후에 스마트기기 개발환경이 지원되는 대로 다시 하도록 하겠습니다.

1.     들어가며

 

안녕하세요.

저는 이번에 vsts2010에 참여하게 된 이석준 이라고 합니다.

앞으로 잘 부탁 드리겠습니다.

이런 공인된 장소를 글을 쓰는 것은 첨이다 보니 무척 부담이 많이 밀려오는 군요.

혹시 잘못된 부분이나 미흡한 부분이 있으면 바로 지적해 주시면 감사하겠습니다.

(제가 국어가 약해서 가끔 안드로메다틱한 문법이 나올 수도 있으니 이해 안 되는 문장은 반드시 지적을 해주시면 감사하겠습니다. .; )

 

VS상의 윈도우모바일(이하 wm) 개발환경은 리눅스나 기타 임베디드 개발환경에 비해 빠르고, 편리하고, 쉽습니다. 기존의 win32api, .Net 코드를 쓸 수 있음은 물론 WTL,MFC등과도 많은 부분의 코드가 호환이 됩니다. 모바일이나 임베디드 에서는 대부분 3D 랜더러로 opengl-es가 많이 사용되는 것으로 알려져 있습니다.  그러나 게임을 만들 때는 direct3d를 많이 사용합니다. Direct3d Mobile 이하 D3dm 을 사용하면 기존의 Direct3d코드를 그대로 활용할 수 있는 장점이 있습니다.  지금부터 약 10회로 나누어서 wm6상에서의 개발환경부터 오픈소스공개 랜더엔진인  irrlicht3dd3dm용 드라이버까지 제작을 해보도록 하겠습니다.

 

2.     준비물

 

wm6sdk , active sync 두 가지가 필요합니다.

여기서 wm6sdk 은 소프웨어 개발킷 이므로 애뮬레이터와 샘플소스코드는 물론 VS용 라이브러리와 클래스위져드관련 환경파일들이 모두 포함된 형태입니다.

요즘 나오는 옴니아2등의 폰에는 6.1버전이라고 되 있는데요 따로 6.1sdk는 존재하지 않습니다. 검색해보시면 DDK가 나와있는 것을 볼 수 있는데요.

DDK sdk가 다른 점은 DDKVS관련 라이브러리 파일제외하고 에뮬레이터와 샘플소스 각종 유틸리티 툴이 업그래이된 것이라고 보시면 됩니다.

요약하자면 wm6sdk 설치후 원하시는 타겟의 DDK나 에물레이터 image를 받아서 먼저 SDK설치하신 다음에 설치 하시면 됩니다.




만약 특정스마트기기의 특정한 기능을 이용하고 싶으시면 이 위에 다시 해당 기기용 sdk를 설치해서 사용하시면 됩니다.

그러면 특정 스마트기기용 추가 API Set 이 설치됩니다.

 

active sync는 스마트기기와 VS개발환경간의 통신수단을 해주는 역할을 하므로 Sdk를 설치 하기 이전에 이것부터 설치를 해주셔야 합니다.


wm6가 설치된 스마트기기는 기본적으로 이미 설치되 있으니 개발 pc에서 설치를 한번 해주시면 됩니다.

 

3.     Hello wm6!!

 

모든 설치가 순조롭게 끝나고 새프로잭트->스마트기기(Smart Device) 항목이 나온다면 설치가 성공한 것 입니다.

그럼 간단한 프로그램부터 만들어 보도록 하죠. 아래와 같이 Smart Device->Win32 Smart Device Project를 선택합니다


 


그 다음 platforms 탭에서 windows mobile6 sdk를 추가 시켜줍니다.



 

자동 생성된
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 의 WM_PAINT에 아래와 같이 DrawText 함수를 추가 해 줍니다.

프로잭트 위져드 덕택에 자동으로 기본프레임웍까지 코드가 작성되서 나오니 편리 하지 않습니까? ^^;

 기존 데스크탑용 어플처럼 빌드후 실행하면 에뮬레이터로 필요한 파일이 전송된후(Deployment 설정 참고) 크로스 컴파일된 어플리캐이션이 실행됩니다.


 

4.    Deployment

데스크톱 어플개발시와는 다르게 프로잭트 속성 페이지에 Deployment라는 항목이 있습니다.

Additional Files항목에서는 스마트기기에서 어플리캐이션실행시 필요한 파일들을 정의해줍니다. (물론 에뮬레이터도 똑같은 스마트 기기로 추상화 됩니다.)

항목을 만드는 양식은 다음과 같습니다.

원본파일이름|원본파일이 있는 경로|파일이 카피될 원격경로|0

Remote Directory 에는 스마트기기에서 실행파일이 실행될 경로를 정해줍니다. 그러나 주의 할점은 어플상에서 파일을 참조할경우 경로는 여기서 지정해준 원격경로가 아닌 저장장치단위의 최상위디랙토리인 My Device/ 기준으로 경로를 잡게됩니다.

참고로 %CSIDL_PROGRAM_FILES% 는 My Device/Program files를 가리킵니다.

실행파일은 Deployment ->Additional Files에서 추가 하지않고 디버깅->Remote Executable 에 정해진 파일이 원격실행 디랙토리에 자동으로 카피가 됩니다.

5.     to be continued…

      다음에는 샘플로 제공된 basicApp를 분석해 간단한 이미지 뷰어를 만들어 보도록 하겠습니다.

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. Visual Studio 2010 Beta 2 버전에서 Smart Device 개발이 가능한 것인가요? 아니면 Visual Studio 2008 버전 기준인가요? 혹시 Visual Studio 2010 Beta2버전이 설치되어 있는 이후에 wm6sdk를 설치해야 하나요?

    • 강보람(워너비) 2009/12/30 15:07

      제 기억이 맞다면, 2010 베타2에서는 스마트 디바이스 개발 부분이 빠져있습니다. 2008일 것 같네요.

  2. 어쩐지...기존에 2005를 확 지우고 2010베타2를 깔았습니다만. -_-;
    wm6sdk를 설치할때 studio와 sync를 요구하는데, sync는 찾지만
    studio2010은 찾지 못했습니다.
    이제 컴퓨터가 너무 지저분해져서 밀어야 할까봐요.

  3. 2010베타2는 아직 스마트기기 미지원입니다.
    정식판에서는 지원될듯합니다.

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 를 사용할 준비가 되었습니다.^^



크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Posted by 조진현


이번 회의 설명을 위해서 API 를 다음과 같이 크게 나누어 보았습니다.

·        Thread unsafe API

·        Thread safe API

·        Free threaded API


Thread unsafe 한 경우에는 멀티스레드들이 동시에 호출할 수 있지만,
이들에 대한 안정성에 대해서는 전혀 보장하지 않습니다.
결과가 갑자기 이상할 수도 있고, 예측 불가능한 상황을 연출할 수도 있습니다.
주로 즉시즉시 상황을 만들어야 할때 사용될 수 있을 것입니다.

Thread safe 한 API 는 어떤 동기화 모델에 의해서 API 가 보호되어집니다.
여러 스레드에서 API 를 호출하더라도, 그들의 안정적인 동작을 보장한다는 의미입니다.
동기화 작업이 별도로 있기 때문에 느려질 수 있다는 단점이 있습니다.

Free threaded 한 API 는 여러 스레드에서 호출을 하더라도,
어떠한 동기화 모델 없이도 API 가 안정적으로 동작한다는 것을 의미합니다.
그렇기 때문에 아주 높은 성능 향상을 얻을 수 있습니다.

< 기존의 리소스 처리 >

멀티스레드 기반의 렌더링으로 가는 첫번째 길은 리소스 부분입니다.
PC 가 발전하면서 성능적으로나 용량적으로 많은 발전을 거듭했습니다.
더블어 게임에서 사용되는 리소스의 용량도 크게 증가했습니다.

멀티스레드 처리가 어느 정도 자리를 잡으면서,
현재 많은 게임들은 이들 리소스에 대한 처리를 별도의 스레드에서 처리하기도 합니다.




일반적인 텍스쳐 로딩 상황을 한번 가정해 보았습니다.
가장 먼저 로더 스레드가 특정한 파일을 읽습니다.
읽어둔 파일을 바탕으로 실제 메모리를 할당하기 위해서는 결국 Device 인터페이스를 통해야 합니다.
DirectX 에서 리소스 관련 메모리를 할당하는 것은
Device 인터페이스를 통해서만 가능하기 때문입니다.
커맨드(Command) 를 생성하는 권한은 오직 Device 인터페이스만이 가지고 있습니다.
( 지난 회를 참고하시기 바랍니다. )

멀티스레드 기반으로 리소스를 로딩하고 있지만,
실제로는 많은 부분이 메인스레드( 여기서는 Render Thread )에 의존하여 작업이 수행되고 있습니다.
리소스를 생성하기 위해서는 반드시 Create, Lock & Unlock 작업이 필요합니다.

< 여기서 잠깐...>
사실 위의 그림의 경우에는 일반적으로 사용되는 구조로 보기 어렵습니다.
그림은 거의 순차적인 처리 구조이기 때문에,
이런 경우라면 굳이 저렇게 사용할 필요가 없습니다.
실제로 많은 게임들은 텍스쳐가 아직 로딩 중이라면,
Default-Material 을 적용해서 폴리곤을 렌더링하도록 구현하기도 합니다.
즉, Render Thread 가 쉴새 없이 계속 커맨드를 생성하는 구조입니다.^^
그리고 위의 예는 간단한 설명을 위한 것입니다.
개발자분들마다 개성 강한 여러 방법들이 존재하시겠죠? ^^

 

 < 간편해진 리소스 관련 커맨드 생성 >

우리가 DirectX 에서 사용하는 대부분의 리소스 작업은 사실 Free Threaded 합니다.
리소스 처리를 위해서 특별한 동기화 처리가 API 레벨에서는 필요하지 않습니다.
( 개발자의 몫입니다. )
그런데 위의 그림처럼 그 동안은 커맨드를 생성해주는 인터페이스가 오직 하나였기 때문에
메인 스레드에 의존해서 처리할 수 밖에 없었습니다.

두번째 시간에 저는 Device와 DeviceContext 에 대해서 설명을 드렸습니다.
기억이 나지 않으시는 분들은 다시 살펴보시기 바랍니다.^^
요약해드리자면, DirectX11 에서는 Free threaded 한 API 와 그렇지 않은 API 들을 분리했습니다.
Free threaded 한 부분에 바로 리소스 처리가 있습니다.
앞서 우리는 Free threaded 의 이점에 대해서 짧게 언급했었습니다.

위의 그림에서,
이제는 Loader therad 는 굳이 메인 스레드( Render thread ) 에 의존하지 않아도,
순차적으로 리소스 관련 커맨드들을 생성시킬 수 있습니다.
커맨드들이 생성되는 순서는 Free threaded 한 경우에는 중요하지 않기 때문에,
빠른 속도로 처리
할 수 있습니다.
특히나, 메인스레드가 별도로 다른 작업을 수행해서 커맨드를 생성시키는 작업이 있다고 해도,
그것과는 별도로 커맨드 생성 작업을 수행할 수 있습니다.
( 커맨드 생성을 위한 대기 시간이 짧아졌다고 할 수 있습니다. )
이로써, 이제는 조금 더 멀티스레딩 형식에 가까워졌다고 할 수 있습니다.

리소스 처리를 이렇게 분리하는 것은
멀티스레드 기반으로 렌더링으로 가는 중요한 기반을 제공
해 주었습니다.

< 다음 회에서는.... >
다음 회부터는 이제 멀티스레드 기반으로 렌더링 관련 커맨드를 생성하는 것을 살펴볼 것입니다.


크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Posted by simon882


 DX9에서 DXUT라는 것은 SDK의 소스에서 그리크게 눈에 띄지 않았습니다.
DXUT라는 것은 DX에서 제공하는 프레임 워크라고 생각하시면 됩니다.
DX11의 모든 예제들이 DXUT를 이용해 작성이 되어있기 때문에 기본적인 구조를 익혀두시는 것이 좋습니다.
함수명만 보아도 함수의 용도를 한눈에 알 수 있기 때문에 보시는데 어려움은 없으실 것 입니다.

void InitApp()
{
    g_SettingsDlg.Init( &g_DialogResourceManager ); // 디바이스 Setting Dlg 초기화
    g_HUD.Init( &g_DialogResourceManager );  // Dlg기본 컨트롤 초기화
    g_SampleUI.Init( &g_DialogResourceManager );

    g_HUD.SetCallback( OnGUIEvent );   // 컨트롤 콜백함수 등록
    int iY = 30;
    int iYo = 26;
     아래와 같이 버튼 추가하고 버튼을 클릭하면 'OnGUIEvent'함수의 case문에서 해당하는 함수를 호출합니다.
    g_HUD.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen", 0, iY, 170, 22 );
    g_HUD.AddButton( IDC_TOGGLEREF, L"Toggle REF (F3)", 0, iY += iYo, 170, 22, VK_F3 );
    g_HUD.AddButton( IDC_CHANGEDEVICE, L"Change device (F2)", 0, iY += iYo, 170, 22, VK_F2 );

    g_SampleUI.SetCallback( OnGUIEvent );
    iY = 10;
}

void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
{
    switch( nControlID )
    {
        case IDC_TOGGLEFULLSCREEN:
            DXUTToggleFullScreen(); // 전체화면, window모드 간의 toggle
            break;
        case IDC_TOGGLEREF:
            DXUTToggleREF();   // HAL과 reference device types 간의 toggle
            break;
        case IDC_CHANGEDEVICE:
            g_SettingsDlg.SetActive( !g_SettingsDlg.IsActive() ); // 디바이스 변경
            break;
    }
}


 이전과는 확연하게 다른 방법으로 텍스트를 출력하고 있습니다.
DX9에서 사용하던 'CD3DFont'를 사용하지 않고, 'CDXUTTextHelper'를 사용해 생성한 변수로 화면에 텍스트를 출력합니다.

[DX9]
CD3DFont* pFont;
pFont->DrawText( X, Y, color, str );


[DX11]
void RenderText()
{
    g_pTxtHelper->Begin();
    g_pTxtHelper->SetInsertionPos( 5, 5 ); // 출력 위치
    g_pTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) ); // 폰트 색상
   
     아래와 같이 'DXUTGet'으로 시작되는 함수들을 이용해 여러가지 정보를 얻어올 수 있습니다.
    g_pTxtHelper->DrawTextLine( DXUTGetFrameStats( DXUTIsVsyncEnabled() ) ); // FrameStats출력
    g_pTxtHelper->DrawTextLine( DXUTGetDeviceStats() );    // Device state출력
    g_pTxtHelper->DrawTextLine( L"Font Test" );      // 기본적인 텍스트 출력
    g_pTxtHelper->End();
}

이런 작업들을 거치면 아래와 같이 화면에 간단한 디바이스 정보와 버튼을 출력할 수 있습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Posted by 조진현


앞서 제 개인적인 판단에 DirectX11 의 큰 특징을 3가지 언급했었습니다.
( 테셀레이션, Compute Shader, Multi-threaded-rendering )
저는 이 세가지 중에서 멀티스레드 기반의 렌더링에 대해서
앞으로의 시간동안 글을 전개해 나갈 생각입니다.


< 커맨드를 생성하는 일 >


혹시 '커맨드(Command)' 라는 개념에 대해서 기억이 나시나요?
기억이 나지 않으신다면,
 [JumpToDX11-2]DeviceContext...넌 누구냣!! 편을 다시 읽어보시기 바랍니다.
요약을 해보자면,
API 를 통한 호출은 결국 하드웨어가 인식할 수 있는 커맨드 형태로 전달되어지게 됩니다.



지난 세대의 DirectX 는 이 모든 처리를 하나의 인터페이스를 통해서 처리했었습니다.
즉, 커맨드를 생성할 수 있는 인터페이스가 오직 하나였습니다.
그리고 그렇게 저장된 커맨드들을 그래픽카드는 순차적으로 실행만 합니다.

이를 멀티스레드 기반으로 처리하기 위해서는 사실 굉장한 노력을 해야합니다.
더 어려운 것은 멀티스레드 기반으로 처리 노력을 기울인다고 해도,
성능 향상에 대한 보장은 장담할 수 없습니다.
일반적으로 로직처리는 멀티스레드 기반으로 분산해서 처리하고는 있지만,
렌더링과 관련된 부분은 이러한 처리가 굉장히 어렵습니다.
( 렌더 스레드의 실행 중간에 렌더링 스테이트가 변하면 문제가 크겠죠? )

그래서 지난 세대의 DirectX 는 이러한 커맨드를 생성하는 부분은
단, 하나의 경로를 통해서만 가능했습니다.
그 동안 우리가 사용했던 IDirect3DDevice9 같은 인터페이스가 바로 이러한 역활을 했었습니다.
이 인터페이스는 실제로 API 를 통해서 하드웨어에게 명령 수행을 지시한다는 의미보다는,
API 를 하드웨어가 이해할 수 있는 커맨드로 변환해 주는 것에 더 가깝습니다.
그리고 그 커맨드를 통해서, 실제로 하드웨어가 실행
을 할 것입니다.

그런데 바로 이 커맨드를 생성하는 일, 누가 해주는 것일까요?
하드웨어가 이해할 수 있는 커맨드들은 드라이버( Driver )가 알고 있습니다.
드라이버는 Core API 나 런타임과 통신을 합니다.

"텍스쳐를 로딩해주세요~"
"메모리를 생성해주세요~"
"렌더링 스테이트를 설정해 주세요~"
"비디오 메모리에 리소스를 바인딩 시켜주세요~"

뭐 이런 식으로 API 를 사용하면,
이들과 관련된 커맨드를 생성하기 위해서 누군가 바쁘게 움직여줘야 할 것입니다.
그것은 바로 'CPU'
겠죠?

실제로 우리가 생성하는 커맨드들 이외에도 처리에 필요하면 커맨드가 더 많이 생성될 수도 있습니다.
예를 들면, 비디오 메모리가 가득찬 상태에서 어떤 리소스를 로드한다면
상황에 맞게 특정 리소스를 비디오 메모리에서 제거해야 합니다.
결국 커맨드들의 실행 중간에,
DirectX 의 리소스 매니져에 이를 요구할 수 있는 커맨드들이 생겨져서 더 붙여질 수도 있습니다.
만약 이런 경우라면, 처리 시간이 더 길어지게 되며,
뒤에 있는 커맨드들의 대기시간도 더 길어지게 됩니다.
전체적으로 작업 시간이 지연되는 것이죠.



< 멀티코어의 시대 >

바야흐로 멀티코어시대의 시대입니다.
이제는 CPU 의 성능적인 발전은 정체되었고,
PC 에 탑재되는 CPU 의 갯수가 성능을 좌우하는 시대입니다.

지금까지의 DirectX 는 싱글코어 기반으로 설계되었다고 지난 회에 언급했었습니다.
CPU 의 성능 발전이 지속적으로 상승 곡선을 그렸다면,
아마 렌더링을 멀티스레드로 할 위험(?)은 피할 수 있었을지도 모릅니다.^^

그 동안 DirectX 팀이 최적화 작업을 한다는 것은
API 호출에 대한 오버헤드를 줄이는 것이 가장 큰 일 중에 하나였습니다.
되도록이면 적은 CPU 사이클을 통해서 커맨드를 실행하게 하려고,
정말이지 많은 노력들을 해왔습니다.

그런데 CPU 가 여러개 탑재되고, GPU 가 발전하면서
놀고 있는 CPU 가 생겨나기 시작했고,
GPU 도 많은 시간을 놀면서 지내기 시작했습니다.
그래서 이들에 대한 활용에 대안으로 등장한 것이 바로
멀티 스레드 기반의 렌더링 작업과 Compute Shader 입니다.
멀티스레드 기반의 렌더링은 CPU 에게 렌더링 작업을 분산시키는 것이고,
Compute Shader 는 GPU 를 CPU 처럼 활용하는 것입니다.

제가 앞으로 언급할 것이 바로 멀티스레드 기반으로 렌더링하는 것입니다.
즉, CPU를 활용하는 것으로 볼 수 있습니다.
앞서 렌더링을 멀티스레드로 하는 것은 굉장히 위험한 일이라고 제가 언급했었는데,
이것이 어떻게 가능하냐구요?
네, 맞습니다.
사실 이것은 틀린 말입니다.
정확히는 커맨드를 멀티스레드 기반으로 생성하는 것이 
바로 멀티스레드 기반의 렌더링의 핵심
입니다.


< 다음 회에는... >
이번 시간에 커맨드에 대한 개념을 확실히 잡으셨는지 모르겠습니다.
다음 회 부터는 멀티스레드여서 행복(?)한 부분들을 하나씩 살펴보겠습니다.^^


 
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Posted by 조진현


한주만 쉰다는 것이, 죄송스럽게도 긴 휴식을 가지고 다시 등장습니다.
지난 시간까지 DirectX11 의 기본 API 를 간단히 언급했었습니다.
쉬는 사이에 안승근님께서 DirectX11과 관련된 기본적인 API 에 대한 글을 작성하기 시작하셨기 때문에
앞으로 기본적인 API 위주로 내용을 언급하지 않습니다.


< DirectX9 까지의 특징 >

DirectX는 Windows95 운영체제와 함께 처음 공개된 후에, 
어느덧 현세대까지 발전을 거듭했습니다.( 세월 참 빠르죠? ^^ )

너무 오랜 시간 전은 저도 잘 몰라서 DirectX7 버전부터의 가장 큰 변화를 하나씩 언급해 보겠습니다.
DirectX7 버전부터 변환과 라이팅(T&L) 을 위한 GPU가 활용되기 시작했고,
DirectX8 버전부터는 'Shader' 라는 GPU 를 활용한 프로그래밍이 도입되어서
현재 가장 개발자에게 주목받는 영역이 되었습니다.
개인적인 견해차이가 있겠지만, 저는 DirectX9 까지의 발전 과정 중에
가장 큰 변화로 바로 이 'GPU 의 활용'
이라고 생각하고 있습니다.

그러면 DirectX9 까지의 발전에서 변심(?)하지 않고,
저희를 언제나 반갑게(?) 맞이해 주었던 부분은 어떤 것이 있을까요? ^^
그것은 바로 API 를 설계할 때 항상 싱글 코어를 고려하고 만들었다는 것입니다.

또 다른 부분은 API 자체가 GPU 의 정점 처리 기능을 규정하고 있다는 것입니다.
언제나 '정점입력-->래스터라이즈-->픽셀처리-->디스플레이' 라는 큰 단계를
API 레벨에서 정의하고 있었습니다.
그 동안 우리는 이것은 Fixed pipeline 이라고 불렀습니다.
아래 이미지는 DirectX8 & 9 의 파이프라인입니다.



이미지 출처 : http://pc.watch.impress.co.jp/docs/column/kaigai/20090804_306876.html

사실 DirectX8 & 9 는 크게 차이가 없습니다.
파이프라인상에서 굳이 찾아야 한다면, 텍스쳐 메모리를 버텍스 쉐이더 단계에서도
참조할 수 있다는 정도일 뿐입니다.

간단히 제가 하고 싶은 말을 정리해 보자면,
'싱글코어 기반의 DirectX 의 시대는 이제 역사의 뒤로 사라지게 될 것이며,
GPU 는 더욱 복잡한 기능을 요구하게 될 것이다'
정도로 정리하겠습니다.
( 예언은 아니고, 이미 이렇게 되고 있는 상황이죠..^^ )


< 새로운 시대를 여는 DirectX11 >

아래는 DirectX11 의 파이프라인입니다.
잘 보시라고 그냥 크게 띄웁니다..^^


이미지 출처 : http://pc.watch.impress.co.jp/docs/column/kaigai/20090804_306876.html


DirectX11 의 가장 큰 특징을 저는 크게 3가지로 나누어 보았습니다.
  • GPU Tessellation.
  • Compute Shader.
  • Multi-threaded rendering.

사실 DirectX11 의 많은 부분은 X-Box360 에서 이미 사용되고 있습니다.
X-Box360 의 ComputeShader 지원은 모르겠으나, 나머지 두가지는 지원하고 있습니다.
X-360을 언급한 이유는 이들 기능이 이미 어느 정도 검증을 받은 기능이라는 점을 강조하고 싶어서 입니다.
( 물론 최신 버전이기 때문에 더 빨라지고, 더 좋아진 부분이 많습니다.^^ )

DirectX11 의 큰 패러다임은 '놀지마' 입니다.
이제는 멀티코어(CPU)와 GPU에게 최대한 많은 일을 시킬 수 있는 구조로
하드웨어를 구성했고, 그것을 기반으로 해서 런타임과 API 를 설계
하고 만들었습니다.

DirectX9 까지는 API 가 변화를 주도했다면,
이제는 DirectX API 변화를 하드웨어가 주도하고 있다고 볼 수 있습니다.
그러다보니 자연스럽게 API 도 더욱 하드웨어에 가깝게 설계되었습니다.
대표적으로 OutputMerge 를 지칭하는 OMxxxx 계열과
InputAssembler 를 지칭하는 IAxxxx 계열 등이 있습니다.

혹시 DirectX8-->9 로 포팅 작업을 하셨던 분들이 이 글을 읽으시는지 모르겠습니다만,
8-->9 의 변화는 API 차원에서 변화가 컸기 때문에 매우 단순했습니다.
하지만 이제는 하드웨어가 크게 변했기 때문에,
단순 포팅 작업 정도로 생각하시면 큰 오산입니다.
물론 단순히 API 만 교체해도 성능 향상을 얻을 수 있습니다.
DirectX11 은 하드웨어 오버헤드를 줄이기 위해 많은 최적화 작업을 수행했다고 합니다.

새로운 DirectX 의 시대가 왔다는 느낌이 드시나요? ^^

크리에이티브 커먼즈 라이선스
Creative Commons License

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

[DX11_#4]텍스트, 버튼 출력  (0) 2009/11/10
[JumpToDX11-6] 커맨드(Command)...  (0) 2009/11/09
[JumpToDX11-5] 새로운 시대를 여는 DirectX11...  (6) 2009/11/02
[DX11_#3]기본적인 설정  (0) 2009/10/22
[DX11_#2]D3D Buffer( 2 / 2 )  (0) 2009/10/13
[DX11_#1]D3D Buffer( 1 / 2 )  (0) 2009/09/22

댓글을 달아 주세요

  1. DX9가좋아서쓰는건아님 2009/11/03 10:42

    중요한 것은 XP 유저가 더 많은데 XP 유저는 DirectX 10 이상의 게임을 돌릴 수 없다는 것이죠. DX8->9 업그레이드는 해도 그만 안해도 그만이었지만 9->10,11은 하면 XP 유저를 잃습니다. 새로운 DirectX 가 온 것은 맞을지 몰라도 새로운 시대는 아직 아네요.

    • 현재 DX9가 가장 많은 유저를 포용할 수 있다는 것에
      저도 부정하지는 않습니다.^^
      단어 선정에 문제가 있다면,
      앞으로 주의하도록 하겠습니다.^^

  2. DX9가좋아서쓰는건아님 2009/11/03 13:33

    단어 선정의 문제는 핵심이 아니고.. MS 를 압박해서 XP에서 DX11 지원을 요구하는 것이 본질 아닐까요.. 혹은 XP 는 DX11 이 정말 안되는가에 대한 분석 기사 라든지.. 현실은 무시한채 DX11 좋아염~ 하면 또 MS 제품 프로모션하는구나 라고 느낌이 듭니다. (하긴 원래 그러려고 만든 사이트에 와서 헛소리하는 저도 좀 미친놈이네요;;)

  3. 지나가던 행인1 2009/11/13 09:53

    안녕하세요. 유익한 포스팅 잘 보고 갑니다만, 조금 아쉬운 부분이 있어 comment 를 남깁니다. 명색이 '공식 팀 블로그' 인데 조금은 formal 한 용어와 분위기의 포스팅이 되었으면 좋겠습니다. (구체적으로 "이미지 출처 : (아마도)ATI";)

    앞으로도 계속 좋은 포스팅 부탁드립니다.

    • 좋게 봐주셨다니, 감사합니다.
      해당 이미지의 출처는 정확하게 수정했습니다.
      지적해 주지 않으셨다면,
      모르고 지나칠뻔 한 문제가 있었습니다.
      ( ATI 가 아니였더군요..^^ )
      감사드립니다.^^

      포스팅의 분위기에 대한 조언은
      적극적으로 개선하도록 하겠습니다.^^

  4. 대체 왜 DX10 부터 XP에 대한 지원을 하지 않는 것인가?
    뭐 결론부터 따지자면 "지원이 불가능 해서" 가 아니라
    "새 운영체제 판매를 위해서" 가 아닐까 싶다..

    GPU Tessellation.
    Compute Shader.
    Multi-threaded rendering.

    이 세가지 XP에서 충분히 구현 가능하다..
    (안될게 없지 않은가? 있다면... 왜 안되는지 말을 해보라)
    새로운 OS가 나오면 최대한 팔아먹어야 하니까..
    M$는 이래서 안된다.. 물론 나도 회사에서 M$의 노예가 되어
    어쩔수 없이 M$기반의 프로그램을 짜야 하지만..
    솔직히 가끔 아니다 싶을때가 많다..ㅎㅎ

Posted by simon882

 DirectX SDK(August 2009)에 포함된 'EmptyProject11'을 기준으로 기본 적인 DirectX세팅에 대해서 설명 하겠습니다.

 아래와 같이 DXUT의 일반적인 설정을 하면 상황에 이벤트에 따라 해당 함수를 호출합니다.
키보드가 눌리면 Onkeyboard가 호출되고, DXUTCreateDevice로 디바이스를 생성하면 'ModifyDeviceSettings'함수가 호출되는 등 기본적으로 필요한 골격을 제공하고 있습니다.

DXUTSetCallbackFrameMove( OnFrameMove );
DXUTSetCallbackKeyboard( OnKeyboard );
DXUTSetCallbackMouse( OnMouse );
DXUTSetCallbackMsgProc( MsgProc );
DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
DXUTSetCallbackDeviceRemoved( OnDeviceRemoved );

 DX9까지는 DX에 디파인된 버젼으로 어플리케이션에서 실행이 가능한 버젼인지 체크 후 버젼이 맞지 않으면 프로그램을 종료해 버렸습니다.
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
     return E_FAIL;

 그러나 아래와 같이 초기화를 하면 어렵지 않게 각 버젼에 대해 깔끔하게 처리가 가능합니다.
여러개의 버젼을 세팅하는 것에 대해 저처럼 의아하게 생각하시는 분도 계실 것 입니다.
그러나 그저 DX9과 DX11을 같이 소스에 위치를 시키면, 하위 버젼에 대한 것들은 모두 알아서 삭제되고,
DX11의 콜백은 정상적으로 설정이 됩니다.(위 아래 순서를 바꿔도 상관 없이 이와 같이 동작 합니다.)

DXUTSetCallbackD3D9DeviceAcceptable( IsD3D9DeviceAcceptable );
DXUTSetCallbackD3D9DeviceCreated( OnD3D9CreateDevice );
DXUTSetCallbackD3D9DeviceReset( OnD3D9ResetDevice );
DXUTSetCallbackD3D9FrameRender( OnD3D9FrameRender );
DXUTSetCallbackD3D9DeviceLost( OnD3D9LostDevice );
DXUTSetCallbackD3D9DeviceDestroyed( OnD3D9DestroyDevice );

DXUTSetCallbackD3D11DeviceAcceptable( IsD3D11DeviceAcceptable );
DXUTSetCallbackD3D11DeviceCreated( OnD3D11CreateDevice );
DXUTSetCallbackD3D11SwapChainResized( OnD3D11ResizedSwapChain );
DXUTSetCallbackD3D11FrameRender( OnD3D11FrameRender );
DXUTSetCallbackD3D11SwapChainReleasing( OnD3D11ReleasingSwapChain );
DXUTSetCallbackD3D11DeviceDestroyed( OnD3D11DestroyDevice );

 첫번째 인자를 true로 설정하면 커맨드를 설정해 아래와 같은 기능을 사용 할 수 있습니다.
(각 버젼에 특화된 커맨드도 몇 가지 있습니다.)
'-fullscreen'=> 전체화면으로 설정
'-width:800 -height:600' => AppWindow의 넓이, 높이를 800x600으로 설정
(이 커맨드를 설정하면 DXUTCreateDevice의 화면 사이즈는 무시됩니다.)
 두번째 인자는 에러 메세지 박스 출력
 세번째 인자는 여분의 커맨드라인 파라메타세팅(default:NULL)
DXUTInit( true, true, NULL );

첫번째 인자가 true이면 전체 화면 모드에서 커서가 보이도록 설정.
두번째 인자가 true이면 전체 화면 모드에서 커서가 화면을 벗어나지 못하도록 설정.
(테스트 했을때 두개의 인자 모두 true와 false일때의 차이점을 느끼지 못했습니다.)
DXUTSetCursorSettings( true, true );

설정한 타이틀로 윈도우 생성
DXUTCreateWindow( L"EmptyProject11" );

 '하드웨어 레벨10'으로 디바이스를 생성
(Feature level관련은 조진현님의 글을 참고하시기 바랍니다.)
두번째 인자가 true이면 윈도우모드, false이면 풀스크린모드 입니다.
(테스트 했을때 false일때도 풀스크린 모드로 변경이 되지 않았습니다.)
세번째, 네번째 인자로 화면의 넓이 높이를 설정할 수 있습니다.
DXUTCreateDevice( D3D_FEATURE_LEVEL_10_0, true, 640, 480 );

DXUT 메인루프 실행
DXUTMainLoop();

 리턴되는 값으로 D3D초기화, 디바이스 상태 등을 알 수 있습니다.
'0'이면 정상이고, 그 이외의 값일경우 DX help파일에서 에러 내용을 확인하시기 바랍니다.
DXUTGetExitCode();

샘플을 실행하면, 예상대로 그저 윈도우안에 세팅된 타이틀과, 파란 화면만 덩그라니 출력됩니다.

첫 샘플 분석이기 때문에 최대한 간단한 함수까지 설명을 적었습니다.
다음 글 부터는 특이사항이 없는한 이번에 설명했던 함수에 대해서는 넘어가도록 하겠습니다.

 DXUTSetCursorSettings, DXUTCreateDevice함수의 설명과 테스트 결과가 다른 이유는 DirectX Help의 설명을 적었으나 테스트 결과 문서대로 동작하지 않았던 것 입니다.
 문제가 있는 것이 맞다면 다음 버젼에는 수정될 것으로 예상됩니다.

다음 글은 좀 더 화면에 보이는 것이 많은 것으로 올리도록 하겠습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License

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

[JumpToDX11-6] 커맨드(Command)...  (0) 2009/11/09
[JumpToDX11-5] 새로운 시대를 여는 DirectX11...  (6) 2009/11/02
[DX11_#3]기본적인 설정  (0) 2009/10/22
[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

댓글을 달아 주세요

Posted by simon882

3) Constant Buffer

Dx10에서 생긴 ‘Constant Buffer’‘Shader’를 사용할 때 비효율적이고 지속적인 업데이트로 인해 성능을 떨어트리는 현상을 극복하기 위해 도입 되었습니다. 이를 이용해 API와 하드웨어가 상수를 효율적으로 조작해, 귀중한 GPU bandwidth CPU의 사용량을 최소화 할 수 있습니다.


 

Shader Constant들을 일괄적으로 관리해 shader constant data 를 관리하기 위해서 발생하는 bandwidth overhead 를 줄여 줍니다.

예전에는 constant 를 등록할 때 4개의 component 를 가진 상수를 등록하는게 기본이었습니다. float4  costanat 설정의 최소 단위였습니다. 하지만 이제는 float 하나만 등록하는 것이 가능합니다.

 

Shader-constant buffer의 생성은 이전 글에서 언급 했던 것처럼 ‘D3D11_BUFFER_DESC’ BindFlagsD3D11_BIND_CONSTANT_BUFFER’설정하면 됩니다.

 

// Define the constant data used to communicate with shaders.
struct VS_CONSTANT_BUFFER
{
    D3DXMATRIX mWorldViewProj;                              

    D3DXVECTOR4 vSomeVectorThatMayBeNeededByASpecificShader;

    float fSomeFloatThatMayBeNeededByASpecificShader;

    float fTime;                                           

    float fSomeFloatThatMayBeNeededByASpecificShader2;

    float fSomeFloatThatMayBeNeededByASpecificShader3;

};

 

D3D11_BUFFER_DESC BufDesc;

BufDesc.ByteWidth = sizeof( VS_CONSTANT_BUFFER );

BufDesc.BindFlags = D3D10_BIND_CONSTANT_BUFFER

 

그러나 Index, Vertex Buffer는 다른 ‘D3D11_BIND_CONSTANT_BUFFER’의 열거형 변수들과 함께 ‘or연산으로 사용할 수 있으나 D3D11_BIND_CONSTANT_BUFFER’는 다른 flag와 함께 사용 할 수 없습니다.

 

Shader-constant buffer 파이프라인에 bind할 때 아래 중 한 개를 사용합니다.

ID3D11DeviceContext::GSSetConstantBuffers,

ð  ‘Geometry shader pipeline state’ 설정.

ID3D11DeviceContext::PSSetConstantBuffers,

ð  ‘Pixel shader pipeline state’ 설정.

ID3D11DeviceContext::VSSetConstantBuffers

ð  ‘Vertex shader pipeline state’ 설정.

쉐이더가 Shader-constant buffer를 읽게 하기 위해, 고유의 HLSL함수를 사용합니다.

 

 실질적으로 DX10에서 DX11로 넘어오며 바뀐 점을 딱히 찾지는 못하고, Dx Help파일을 번역하고 검색한 내용을 약간 첨부한 정도가 되었습니다.

 추후에 더 알게 되는 것이 있으면 추가하도록 하겠습니다.


크리에이티브 커먼즈 라이선스
Creative Commons License

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

[JumpToDX11-5] 새로운 시대를 여는 DirectX11...  (6) 2009/11/02
[DX11_#3]기본적인 설정  (0) 2009/10/22
[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

댓글을 달아 주세요

Posted by simon882

안녕하세요. Q3에 합류한 안승근입니다.
앞으로 DX11에 와서 변경된 내용 중 기초적인 부분을 담당하여 글을 올리도록 하겠습니다.
 
대부분의 내용은 DX9와 비교해 변경된 사항에 대해 언급 하도록 하겠습니다.

글을 읽으시다 틀린 내용에 대해서는 코멘트를 주시면 감사하겠습니다.

 
 첫 주제는 D3D11 Buffer입니다.
D3D11에는 아래와 같이 총 3개의 버퍼 타입이 있습니다.

Buffer Type: Vertex Buffer, Index Buffer, Constant Buffer

 

1) Vertex Buffer

각 정점의 정보를 직접적으로 저장해놓고 사용하는 방식.

간단히 사용할 수 있으나 중복된 정점의 위치를 여러개 저장하고 사용하는 등 능률은 높지 않다.

 

 



2) Index Buffer

IndexVertex Buffer의 정점 정보를 얻어 사용할 수 있다.

 Vertex Buffer Index Buffer는 같은 정점에 대해서는 Index로 위치 정보를 공유해서 사용하기 때문에 능률이 높아진다.

 


* DX9
DX11의 버퍼 생성시 차이점

IDirect3DDevice9::CreateVertexBuffer

ð  ID3D11Device::CreateBuffer

DX9에서는 아래와 같이 각각의 버퍼를 생성하는 함수가 따로 존재하였습니다.

if( FAILED( g_pd3dDevice->CreateVertexBuffer( 50*2*sizeof(CUSTOMVERTEX),

                                           0, D3DFVF_CUSTOMVERTEX,

                                           D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )

{

    return E_FAIL;

}

 

if( FAILED( g_pd3dDevice->CreateIndexBuffer( 12 * sizeof(MYINDEX),

 0, D3DFMT_INDEX16,
 D3DPOOL_DEFAULT, &g_pIB, NULL ) ) )

{

    return E_FAIL;

}

 

정점을 생성하는 방법이 DX11에 와서는 한개의 함수로 통합해서 사용하고 아래와 같이 D3D11_BUFFER_DESC에 버퍼 생성에 필요한 옵션을 넣었고, 특히 멤버변수인 BindFlags를 변경해 어떤 버퍼를 생성할지 정할 수 있도록 하였습니다.
 위의 함수들을 다시 보면 왜 굳이 나눠 놓았을까 싶을 정도로 함수 이름만 다를뿐 파라미터가 거의 동일합니다.

   // Define the data-type that
   // describes a vertex.
   struct SimpleVertexCombined
   {
       D3DXVECTOR3 Pos; 
       D3DXVECTOR3 Col; 
   };

   // Supply the actual vertex data.
   SimpleVertexCombined verticesCombo[] =
   {
       D3DXVECTOR3( 0.0f, 0.5f, 0.5f ),
       D3DXVECTOR3( 0.0f, 0.0f, 0.5f ),
       D3DXVECTOR3( 0.5f, -0.5f, 0.5f ),
       D3DXVECTOR3( 0.5f, 0.0f, 0.0f ),
       D3DXVECTOR3( -0.5f, -0.5f, 0.5f ),
       D3DXVECTOR3( 0.0f, 0.5f, 0.0f ),
   };

   // Create indices.
  
unsigned int indices[] = { 0, 1, 2 };


   
// Setup constant buffers
    D3D11_BUFFER_DESC BufDesc;
    // Vertex Buffer Size
    BufDesc.ByteWidth        = sizeof( SimpleVertexCombined ) * 3;
    // Index Buffer Size

    // BufDesc.ByteWidth       = sizeof( unsigned int ) * 3;

    BufDesc.Usage = D3D11_USAGE_DYNAMIC;

    BufDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; // D3D11_BIND_INDEX_BUFFER

// , D3D10_BIND_CONSTANT_BUFFER...

    BufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

    BufDesc.MiscFlags = 0;

    pd3dDevice->CreateBuffer( &BufDesc, NULL, &g_pMirrorVertexBuffer );


 정점을 저장하는 버퍼역시 DX9에서는 VertexBuffer, Index Buffer가 각각 나누어져 있었던 것이 DX11에서는 한개로 통합되었습니다.
LPDIRECT3DVERTEXBUFFER9 g_pVB        = NULL; // 정점버퍼
LPDIRECT3DINDEXBUFFER9 g_pIB        = NULL; /// 인덱스버퍼

ID3D11Buffer*               g_pVertexBuffer = NULL;
 

* DX10과 DX11의 버퍼 생성시 차이점
 DX10
DX11은 사실상 함수 이름외에는 변한 것이 없습니다.
 
아래와 같이 사용하는 함수의 이름 중 DX의 버젼을 나타내는 숫자가 '10'에서 '11'이 된 것 외에는 사용되는 모든 함수가 파라미터리턴타입까지 동일하므로 DX11이라고 해서 따로 알아야 할 내용은 없습니다.

ID3D10Device::CreateBuffer

ð  ID3D11Device::CreateBuffer

 

버퍼에 관련된 내용 뿐만 아니라 대부분의 함수들이 DX10 DX11은 거의 비슷한 형태를 지니고 있습니다. 따라서 특별한 경우가 아니면 DX9와 비교해 변경된 것만을 적도록 하겠습니다.

다음 번의 글에는 Constant Buffer에 대해서 올리도록 하겠습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License

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

[DX11_#3]기본적인 설정  (0) 2009/10/22
[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

댓글을 달아 주세요

Posted by 조진현

 

이번 회에서는 일단 소스를 좀 나열해 보겠습니다.
일단 변수들은 다음과 같습니다.




이어지는 상황은 다음과 같습니다


 

BackBuffer 를 설정했습니다. 일단 'View' 라는 키워드에 주목해 주시기 바랍니다.
그리고 다음에 이어지는 상황을 다시 보겠습니다. 

 

 




이어졌던 소스는 Depth-Stencil Buffer 생성을 위한 작업이였습니다.

역시나 낯선 개념이 등장하는데, 바로 'View' 입니다.
우리가 앞서 생성했던 BackBuffer 와 Depth-Stencil Buffer 에는 CreatexxxxxxxView() 형태로
작업을 해주고 있습니다.

하나의 뷰는 렌더링 작업 중에 파이프라인에서 접근할 수 있는 리소스 일부분을 의미합니다.

더 정확한 개념을 위해서 아래의 계층구조를 한번 살펴보시기 바랍니다.






우리가 사용하는 모든 리소스들은 사실 ID3D11Resource 를 상속받아서 구성됩니다.

텍스쳐나 각종 버퍼 등이 이에 속한다고 할 수 있습니다.

( ID3D11Buffer, ID3D11Texture )

 

ID3D11View 를 상속받아서 구현되는 것들은 ID3D11RenderTargetView, ID3D11DepthStencilView, ID3D11ShaderResourceView 가 있습니다.

 

나열하고 보니깐 약간 감이 오시지 않으십니까?

이렇게 분리가 되었는데, 사실 ID3D11View ID3D11View::GetResource() 라는 멤버함수를 가지고 있습니다.

 

결국 ID3D11View ID3D11Resource 와 개념적으로 메모리 데이터는 동일하다고 볼 수 있습니다.

그러다 보니 이 두가지 인터페이스를 구별해서 설명하기가 무척 난해합니다.
비슷하면서도 실질적으로 수행되는 역활에는 엄연한 구분이 있으니 말이죠...

 

이렇게 분리함으로써 역할 구분이 명확해서 코드의 품질이 향상되었다라고 말할 수 있습니다.
조금 더 좋은 방향으로 생각해 본다면,
아마도 내부적으로 조금 더 최적화 된 위치에 메모리를 할당해 주지 않을까? 라는 의심도 해봅니다.
( 왜 그런지 이유를 꽤 오래 생각했지만, 답을 찾지 못했습니다..T.T )

 

 

참고로 얘기드리면,

DirectX9 버전까지 유용하게 사용되던 메모리 풀의 개념이 이제는 CPU 에서 읽기/쓰기 작업,
GPU
에서 읽기/쓰기 작업이 가능여부를 설정하는 개념형태로 변경
되었습니다.

이것에 대한 얘기는 다음 번에 저나 다른 분이 해주실 겁니다.

( DirectX11 파트에는 저 말고도 한분이 더 계십니다…^^ )

 

위의 코드에서 View 계열들은 GPU 만 읽기/쓰기 작업이 가능한 형태로 그래픽 카드상에
메모리를 할당했습니다.


결국 위의 작업은 바로 아래에 나오는 작업을 위해서 필요했던 것입니다.



보시듯이 모두 View 계열의 인터페이스를 설정했습니다.



< 마치면서... >
다음주는 한주 쉬게 될 것입니다.
스터디 내부 발표가 있어서...쿨럭~



 

크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Posted by 조진현



이번에는 실제로 API 를 사용해서 Device와 DeviceContext 를 설정하는 작업을 할 것입니다.
아마 다음과 같이 사용할 수 있을 것입니다.





D3D11CreateDeviceAndSwapChain 은 정말이지 마법 같은 API 입니다.
( 더 언급할 것도 없을 것 같습니다...^^ )


< Feature Level >

이전버전과의 호환을 유지하는 것이 중요한 일이기 때문에,
최신의 버전은 이전 버전들에 대한 호환을 생각하게 됩니다.
그래서 등장한 개념이 바로 'Feature Level' 입니다.
위의 코드에도 이 키워드가 등장했습니다.

약간씩의 차이가 있겠지만,
DirectX9 시절까지 우리가 Device 생성을 위해서 API 에 전달했던 인자들은
SDK 버전과 하드웨어 가속( HAL Device ) 의 사용여부 등의 인자였습니다.
( 이외에도 더 많이 있었지만, 지금은 이 두 가지가 중요해서 이것만 언급했습니다. )
그런 식으로 Device 생성 작업을 하면, 우리는 가속되는 하드웨어로 작업을 할 수 있었습니다.
( 저도 이 글 적기 전까지 별 다르게 생각하지 않았습니다...-_- )

그런데 이제는 API 를 통해서 전달해야하는 정보들 중에 Feature Level 이라는 것을 넘겨주어야 합니다.
각 DirectX 버전마다 각각의 특징을 설명하는 Feature Level 이 존재합니다.
우리가 할 일은 원하는 Feature Level 을 설정하고 Device 생성을 요구하는 것입니다.

Feature Level 은 사실 하드웨어적인 역활과 연관성이 깊습니다. 
우리가 사용하는 그래픽 카드는 제작될때 특정한 DirectX 버전에 포커스를 두고 출시하게 됩니다.
시중에 그래픽 카드를 구입할 때 
'DirectX11 을 완벽히 지원하는 그래픽 카드" 뭐 이런 식의 광고 많이 보셨으리라 생각이 됩니다.

그렇다는 것은 그래픽 카드에도 역시 버전이 있다는 것입니다.
Feature Level 이라는 것이 바로 그런 하드웨어의 버전 역활을 하는 것입니다.

 
현재 DirectX11 을 지원하는 그래픽 카드는 미출시 상태입니다.
그렇기 때문에 사실 DirectX11 프로그램은 하드웨어 가속을 시킬 수가 없습니다. ( 새로 사야하나...-_- )
DirectX9 시절에는 CreateDevice 옵션에 HAL 옵션 주면 자동(?)으로 가속되는 Device 를 생성해 주었지만,
이제는 그래픽 카드에서 구현된 버전을 정확히 얻어서,
그 Feature Level 에 맞는 Device 를 생성
시켜야 합니다.

예를 들면 제가 가진 컴퓨터가 DirectX 10 버전을 지원하는 그래픽 카드인데,
Feature Level 에 DirectX 10.1 버전과 하드웨어 가속 옵션을 주어서
Device 생성을 요구하면, 그것은 실패하게 됩니다.
그렇다는 것은 Feature Level 10 를 설정해 주어서 Device 를 생성 시켜주어야 한다는 의미입니다.

 이렇게 하면 우리는 DirectX 11 API 를 이용해서 낮은 버전의 DirectX 버전을 사용하는
그래픽 카드를 제어할 수 있을 것입니다.
아래의 그림이 이해에 도움이 되었으면 좋겠습니다.



그렇다면 좀 더 정확하게 우리가 할 일을 확장해 보면,
가장 최근의 Feature Level 부터 나열해보면서,
사용하는 그래픽 카드의 Feature Level 을 알아내는 것이 될 것입니다.
( 저는 위의 예제에서는 11버전만 고려해서 작업한 것입니다.^^ )

사실 예전에는 Device Caps 라는 개념이 있었는데,
사실 이것이 기능별로 체크했기 때문에 굉장히 반복적인 작업이였을 것이라 생각이 듭니다.
그것을 Feature Level 이라는 개념으로 단순화 시켰다고 보면 괜찮을 것 같습니다.

< 추가된 내용 >

각 Feature Level 에 대응되는 쉐이더 모델은 다음과 같습니다.

Feature Level 11.0 = Shade 5.0
Feature Level 10.1 = Shader 4.1
Feature Level 10.0 = Shader 4.0
Feature Level  9.3 = Shader 3.0
Feature Level  9.2 = Shader 2.0
Feature Level  9.1 = Shader 2.0







크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Posted by 조진현



지난 회에서 DXGI 에 대해서 잠깐 살펴보았습니다.
DXGI 에 대한 사용방법은 여기서 언급하지 않습니다.
왜냐하면 제가 진행할 코딩에서는 기본적으로 셋팅되어 있는 그래픽 카드를 사용하는 것을 전제로
진행할 것이기 때문입니다.

혹시 호기심이 더 왕성하신 분들은 IDXGIFactory 인터페이스를 살펴보시면 됩니다.
멤버 함수중에 MakeWindowAssociation, EnumAdapters 정도 살펴보시면 도움이 될 것입니다.
( API 함수 이름만 봐도 느낌이 팍팍! )

예상하시겠지만, DXGI는 연결가능한 장치(어댑터)들을 나열해서
그것 중에 하나 선택하는 역활이 필요합니다.
저는 기본적으로 설정된 어댑터를 사용할 것이기 때문에 이들에 대해서는 언급하지 않겠습니다.
 DXGI 계열의 API들도 양이 상당합니다.( 그래서 일단 패스~ )


지난 회에 언급했던 디바이스 초기화를 위한 변수들 기억하시나요?
다시 나열해 보면 아래와 같습니다.




< IDXGISwapChain >

가장 먼저  IDXGISwapChain 에 대해서 살펴봐야하겠지만, 이것에 대한 별도의 설명이 필요할까요?
Front Buffer( 현재 화면에 보여지는 버퍼 )와 Back Buffer( 현재 연산을 해서 기록하는 버퍼 ) 를
준비해서 이것을 Flip 시키면서 번갈아 가면서 보여주는 것을 의미합니다.
( 너무 고전적인 내용이라 더 설명하면 혼날듯...)

우리가 렌더링할 영역(버퍼)에 대한 포맷과 같은 각종 속성들을 설정해 주어서 생성을 요구하고,
포인터를 받으면, 이들 버퍼에 대한 정보를 제어
할 수 있습니다.
나중에 살펴보게 되겠지만, Present() 라는 API 를 기억하시나요?
9.0 에서는 이것이 ID3DDevice9의 멤버함수로써 사용했었습니다.
하지만 현재는 IDXGISwapChain 의 멤버함수로 등록되어 잇습니다.
그래서 이에 대한 포인터가 필요합니다.^^
결론적으로 얘기 드리면 앞으로 화면 출력에 관한 모든 것은 IDXGI 계열의 인터페이스로서 제어할 수 있습니다.





아마 위와 같은 형식이겠죠? ( 각각의 성분에 대한 설명은 생략합니다...저걸 다 어찌 설명해요..-_- )

 

 

 < DeviceContext...넌 누구냣!! >

이상한 인터페이스가 DirectX11 에서 생겼습니다.
Device는 무엇인지 알겠는데, DeviceContext 는 또 무엇일까요?
사실 이것은 그동안 Device 인터페이스들이 해오던 역활을 두가지로 분리한 것에 지나지 않습니다.

즉, ID3D11Deivce 는 주로 리소스( 버퍼나 텍스쳐 등 )의 생성에 대한 인터페이스이며,
ID3D11DeviceContext 는  이들 리소스를 제어하고 관리하기 위한 인터페이스입니다.

그렇다면 왜 이렇게 두 가지로 분리된 것일까요?
먼저 아래의 그림을 살펴보겠습니다.




우리가 렌더링을 수행하기 위해서는 애플리케이션에서는 관련 Core API 와 Runtime을 사용하게 됩니다.
이들 Core API 와 Runtime 은 우리가 필요한 렌더링에 관한 모든 것을 수행합니다.
메모리 할당이나 리소스들의 수정, 메모리 바인딩, 각종 렌더링 스테이트의 제어 등등 굉장히 많죠.
( 물론 쉐이더 코드들을 통해서도 이들을 제어할 수 있는 부분이 있습니다만,
  여기서는 흐름상 고려하지는 않습니다.  ) 

DirectX 시스템은 Application 과의 오버헤드를 최소화 하기 위해서
이들 사이를 매우 얇은 추상화 단계로 디자인 했었습니다.
즉, Core API 나 Runtime 들은 바로 Driver 에 접근할 수 있었습니다.

그래도 약간(?) 존재해 있는 Application 과 하드웨어간의 오버헤드를 줄이기 위해서
기존의 Device 의 역활을 Device 와 DeviceContext 로 분리
하게 된 것입니다. 

그렇다면 여기서 발생되는 오버헤드란 것은 어떤 것일까요?( 의문에 의문 연속입니다..-_- )
우리가 사용하는 각종 API 들은 Runtime 에 전달되어서 하드웨어가 인식할 수 있는
커맨드( Command ) 들로 변환
됩니다.

Runtime 은 이들 커맨드들을 담을 수 있는 메모리 공간을 가지고 있어서, 커맨드들을 저장하게 됩니다.
그러다가 이들 버퍼가 가득차거나, 혹은 렌더링 데이터의 업데이트가 필요한 경우에
이들을 하드웨어로 전송하게 되는 것입니다.
바로 이 커맨드들에 대해서 오버헤드가 발생하는 것입니다.
이 커맨드들이 오버헤드를 발생시키는 이유는 여러가지가 있었습니다.
하드웨어의 경우에는 프로세싱( processing ) 스타일이 매우 다양하기도 했고,
API 와 하드웨어 상에서 커맨드 전달이 잘못 전달되는 경우도 있었다고 합니다.
( 아무래도 하드웨어가 너무 다양해서가 주된 이유였던 듯 합니다. )

이들에 대한 오버헤드를 줄이는 방법을 고민하던 중에 나온 결과물 중에 하나가
바로 'DeviceContext' 라는 것입니다.
( 뒤에 언급할 기회가 있겠지만, 'State Object' 가 바로 이 오버헤드를 줄이기 위해 등장한 개념이기도 합니다. )

Device 의 경우에는 오버헤드를 줄이기 위해 등장한 개념이
리소스의 생성/해제를 담당하는 커맨드들과 그 리소스들을 제어하는 커맨드들로 분리하는 것입니다.

 

분리함으로써 어떤 성능 향상이 있었을까요?
리소스의 생성과 해제는 사실 멀티스레드 형태의 API 호출에도 별 문제가 없습니다.
어차피 명령어들이 큐 형태로 쌓이게 될테니까요.
반면에 렌더링 커맨드들은 멀티스레드 형식으로 구성되면 큰일 나겠죠?

결국 Device 는 Free threaded 형식으로 구성되었고,
DeviceContext 는 그렇지 않다는 것
입니다.
Free threaded 형식으로 구성되었다는 것은 스레드에 안정성을 유지하기 위한
별도의 lock/unlock 작업이 필요없다는 것입니다.
멀티스레드에 안정적이라는 얘기는 스레드 세이프하다는 것입니다.

(정확하게 확신은 아직 드릴 수 없지만, 멀티스레드 관련 렌더링과도 관련이 있는 부분이 여기이지 않을까요.)

사실 리소스의 생성과 해제가 성능에 많은 부분을 차지한다고 볼때,
이렇게 분리되어진 것을 환영해야 할 것입니다.

 

 < 다음 회에는... >

글이 좀 길어지는 것 같아서 일단 여기서 마무리 합니다.
다음 회에는 나머지 초기화 부분에 대해서 계속 언급하겠습니다.^^

 





 

크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 풍아저씨 2009/10/10 21:33

    강의 감사합니다.
    보니 혹시 DeviceContext는 SLI기반의 프로세스를 지원하기 위한 것이
    생각합니다만..