[JumpToDX11-3] Feature Level

DirectX 11 2009. 8. 31. 06:00 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







Welcome to Dynamic C#(5) - Return to Dynamic.

C# 2009. 8. 26. 15:02 Posted by 알 수 없는 사용자

- 그때 너 너무 대충하더라

제가 맨 처음 dynamic에 대한 포스트로 Welcome to Dynamic시리즈를 시작했는데요. 뭐랄까 너무 추상적인 내용위주로 진행했다는 생각이 들더군요. 그리고 공부를 더 하다보니 그런 생각이 더 확실해지더군요. 없는 input에서 output이 나올수는 없는거니 당연한 이야기 겠지요. 캬캬캬. 앞으로는 dynamic과 DLR에 대해서 이야기를 조금 진행해보려고 합니다. 그래봤자 여전히 별 내용없거나 다른분들이 주는 insight를 그대로 전해주는 역할 이상은 못할지도 모르지만 일단 늘 그래왔듯이 노력해보겠습니다. 따쓰한 피드백을. ㅋㅋㅋㅋ


- dynamic?

dynamic d = ....;
d.Foo();

여기서 지역변수인 d는 dynamic타입을 가집니다. dynamic은 엄연히 컴파일러가 지원하는 타입이고, 타입이름이 들어갈 수 있는 곳에는 어디든지 dynamic이라고 명시해줄 수 있습니다. 즉, 실제타입이 동적으로 결정되는 것을 의미하는 정적인 타입인거죠. 차이점이라고 한다면, Foo라는 메서드를 호출하는 IL코드를 바로 만든다기 보다는 DLR과 C# 런타임 바인더를 통해서 dynamic call site라는 걸 호출되는 지점에서 생성합니다. 

이런 기능을 통해서 여러분이 기존에 써오던 친숙한 방법과 모습으로 파이썬이나 루비, 혹은 "스스로 어떻게 실행해야 하는지 알고있는" 객체들을 사용할 수 있게 해줍니다. 개인적으로는 이 부분이 꽤나 중요한 부분이라고 생각합니다. 다른 동적언어들이 있는데, 굳이 C#에 이런 기능이 들어가는건 앤더스 헬스버그의 철학답게, 기존에 잘 사용해오던 언어에 새로운 기능을 잘 통합시켜서 한 부분에만 특화된 언어보다 기존의 언어에서도 새로운 기능을 사용할 수 있게 해주는 거겠죠. 그래서 기존에 잘 사용해오던 언어가 새로운 요구에 발맞추는 새로운 표현법을 계속해서 잘 통합시켜 나가면서 생명력을 유지할 수 있게 말이죠.

위에서 말씀드렸듯이 dynamic은 분명히 존재하는 타입이고 컴파일러도 잘 알아듣는 타입이지만, 현재까지의 모습으로 봤을땐 실제로는 존재하지 않는 타입입니다. DLR과의 연동을 통해서 가능한 동적인 프로그래밍을 문법적으로 편리하게 만들어주는 syntatic sugar같은 역할이라고 볼 수 있을까요? 일단 아래코드를 보시져.

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

namespace ConsoleApplication2
{
    class Program
    {
        public dynamic DynamicCall(dynamic d)
        {
            object obj = 5;
            return d.Foo();
        }

        static void Main(string[] args)
        {           
        }
    }
}


그리고 이 코드에서 타입위에 마우스를 가져가 보시져. object와 dynamic을 비교해보겠습니다.




object위에 마우스를 올렸을때는 "class System.Object"라고 나오는데, dynamic에는 그런 표시가 없죠? 그럼 우리의 심증을 물증으로 굳혀보겠습니다. 위의 코드를 컴파일한 코드를 리플렉터에서 보면 아래와 같습니다.

----- 리스트 1 -----

[return: Dynamic]
public object DynamicCall([Dynamic] object d)
{
    if (<DynamicCall>o__SiteContainer0.<>p__Site1 == null)
    {
        <DynamicCall>o__SiteContainer0.<>p__Site1 =
            CallSite<Func<CallSite, object, object>>.Create(
                new CSharpInvokeMemberBinder(
                    CSharpCallFlags.None, "Foo", typeof(Program), null,
                    new CSharpArgumentInfo[] {
                        new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));
    }

    return <DynamicCall>o__SiteContainer0.
                    <>p__Site1.Target(<DynamicCall>o__SiteContainer0.<>p__Site1, d);
}

dynamic이라는 타입은 싹 사라지고 object만 덩그러니 있는걸 확인할 수 있습니다. 그리고 dynamic이라고 알려주는 지시자같은게 붙어있는걸 보실 수 있습니다. 즉 beta1기준으로 현재에는 dynamic은 엄연히 하나의 타입이지만, 닷넷 프레임워크 내부적으로는 dynamic이라는 타입이 존재하지 않는다는 말입니다. 즉, 저렇게 dynamic이라고 표시가 된 객체는 컴파일러가 런타임에게 동적으로 처리되어야 한다는 걸 알려주는게 되겠죠.

그리고 위에서 말씀드렸듯이 컴파일러는 dynamic과 관계된 연산을 만나게 되면, DLR을 통해서 DLR의 call site를 이용하는 코드를 생성합니다. 코드에 보시면 'SiteContainer', 'CallSite'같은게 보이시죠? DLR에 기반한 동적언어에서 어떤 동적 연산을 호출하면 그 코드는 DLR이 이해할 수 있는 기본적인 연산으로 번역되고, 그 기본적인 연산을 적용할 대상에 따라서 Python객체에 하려면 Python Binder로, 기본적인 .NET 객체에는 Object Binder로, C#은 C#런타임 바인더로 적용하게 됩니다. 그 기본적인 연산의 목록은 현재 아래와 같습니다.

----- 리스트 2 -----

namespace System.Dynamic
{
    public class DynamicObject : IDynamicMetaObjectProvider
    {
        protected DynamicObject();

        public virtual IEnumerable<string> GetDynamicMemberNames();

        public virtual DynamicMetaObject GetMetaObject(Expression parameter);

        public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result);

        public virtual bool TryConvert(ConvertBinder binder, out object result);

        public virtual bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result);

        public virtual bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes);

        public virtual bool TryDeleteMember(DeleteMemberBinder binder);

        public virtual bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result);

        public virtual bool TryGetMember(GetMemberBinder binder, out object result);

        public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result);

        public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result);

        public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value);

        public virtual bool TrySetMember(SetMemberBinder binder, object value);

        public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result);
    }
}


위의 목록에서 보시면 TryInvokeMember메서드가 있고 인자로는 InvokeMemberBinder를 받는게 보이시져? 그리고 위의 리플렉터에서 뽑은 리스트1을 보시면, Create메서드의 인자로 CSharpInvokeMemberBinder를 생성하고 있습니다. 그리고 CSharpInvokeMemberBinder를 따라가보면, base클래스가 InvokeMemberBinder가 나옵니다. 즉, DLR이 이해할 수 있는 기본연산이 C# 바인더를 통해서 실행되고 있을음 유추해볼 수 있습니다.

그리고 리스트1에서 "<>p_Site1" 이라는 걸 따라가보면 선언이 아래와 같습니다.

public static CallSite<Func<CallSite, object, object>> <>p__Site1;

즉, static 필드인데요. 델리게이트도 담고 있습니다. 이게 Foo메서드 호출에 대한 dynamic call site를 가지고 있는 필드입니다. 위의 리스트1의 코드를 보시면, 이 <>p_Site1에 저장된 내용을 Target메서드를 통해서 호출하고 있는 모습을 보실 수 있습니다. 생긴게 좀 복잡하긴 한데요, 이걸 뭐 직접 짜야하는건 아니고 컴파일러가 작업해주는 거니까요. 이 이야기는 나중에 더 자세하게 다루도록 하구요. 일단 dynamic이 어디에 어떤 모습으로 쓰일 수 있고, 그게 뭘 의미하는지 더 알아보도록 하겠습니다. 우선, 아래의 코드를 보시져. 

dynamic d = ...;

d.Foo(1, 2, 3); // (1)

d.Prop = 10; // (2)

var x = d + 10; // (3)

int y = d; // (4)

string y = (string)d; // (5)

Console.WriteLine(d); // (6)
(d.Foo(); 에서 d는 Foo의 실행요청을 받는 receiver이고, d의 타입이 dynamic이라면 d는 dynamic receiver가 되는거임!)

1. Foo메서드의 호출요청을 받은 객체의 타입이 dynamic이므로 컴파일러는 런타임에게 이 코드에서 d의 실제 runtime type이 뭐든지에 상관없이 "Foo"라는 메서드를 매개변수{1, 2, 3}를 적용해서 바인드해야 한다는걸 알려줍니다.

2. 역시 1번과 마찬가지로 dynamic receiver가 있으므로 컴파일러는 런타임에게 이 코드에서는 "Prop"이라는 프로퍼티비스무리한(필드나 프로퍼티)걸 바인드해야 하고 거기에 10이라는 값을 할당해야 한다고 알려줍니다.

3. 여기서는 +연산자는 동적으로 바인드되는 연산인데요, 매개변수중에 하나가 dynamic이기 때문이죠. 런타임은 실제 d의 runtime type에 대해서 일반적인 연산자 오버로딩 규칙을 따라서 적합한 연산을 찾습니다.

4. 여기서는 암시적인 형변환이 있는데요, 컴파일러는 int와 d의 runtime type에 대한 모든 형변환을 고려해본뒤에 d에서 int로의 형변환이 가능한지 판단하도록 런타임에게 알려줍니다.

5. 이번에는 명시적인 형변환 인데요, 컴파일러는 이 변환을 컴파일하고 런타임에게 이 형변환에대해서 검토해보도록 알려줍니다.

6. 비록 컴파일타임에서 볼 수 있는 메서드를 호출하지만, 인자가 dynamic이므로 컴파일타임에서는 오버로딩 판별을 할 수 없습니다. 그래서 어떤 Console.WriteLine을 호출할지도 역시 런타임에 결정하게 됩니다.


- 마치면서

오늘은 dynamic타입에 대해서 이야기 해봤습니다. 재주가 부족해서 잘 설명한거 같지 않네요;;; 생각보다 dynamic배후의 이야기가 많은데요, 다음시간부터 거기에 대해서 하나씩 하나씩 이야기 해보겠습니다~.


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/10/29/dynamic-in-c.aspx
2. http://blogs.msdn.com/cburrows/archive/2008/10/27/c-dynamic.aspx
3. http://channel9.msdn.com/pdc2008/TL10/

SQL Azure - CTP1

Cloud 2009. 8. 26. 09:51 Posted by 알 수 없는 사용자


8월 25일 화요일에 SQL Azure CTP에 대한 초대 메일을 받아 아래 그림처럼 클라우드 데이터베이스를 구성해보았습니다. 기존과 달리 T-SQL이 많이 지원이 되어 ADO.NET 만 보면 개발자에게 익숙한 내용이라고 보여집니다. 데이터베이스를 로컬이나 서버에 두지 않고 클라우드에 놓겠다는것이며 프로그래밍은 거의 기존과 동일하게 가능하니깐요.


SQL Azure 데이터 액세스 구조는 아래 그림을 참조해주세요. ADO.NET 등의 응용 프로그램이나 Windows Azure를 통해 SQL Azure 데이터베이스를 액세스할 수 있습니다.



클라우드 데이터베이스는 1개 서버에 5개 데이터베이스까지 현재는 지원하고 있으며 SQL Server Management Studio에서는 아직 아래 그림처럼 연결이 되지 않고 있습니
다. 조만간 될 예정이라고 하니 그때는 좀 더 편하게 작업이 가능해져 SQL Azure에 대한 부분이 더 활성화 될것으로 보입니다.




SSMS 외에 SQL Azure에 접근할 수 있는 방법은 ADO.NET, SqlCmd를 이용한 방법이 있으며 이를 통해 대부분의 T-SQL 구문을 실행해볼수 있습니다.


아래는 SQL Azure 데이터베이스를 생성하기 위해 ADO.NET 으로 구현한 내용입니다. 아래 주소의 소스를 참고했습니다.

http://msdn.microsoft.com/en-us/library/ee336243.aspx

생성하려면 SQL Azure 계정이 있어야 하고 이를 통해 ADO.NET을 호출해야 합니다. 계정이 없는 분을 대신해서 제가 호출해보았습니다. 제 환경은 Visual Studio 2010 에서 c# Windows 응용 프로그램으로 접근합니다. 

아래 소스는 클라우드 데이터베이스를 생성해주는 구문입니다. 다 알고 있는 것처럼 ADO.NET으로 CREATE DATABASE 구문을 실행해주는 내용입니다. 연결 문자열은 위에서 ConnectionStringBuilder 클래스를 통해 미리 생성해두었습니다. 사전에 정의된 계정으로 연결 문자열이 생성되며 실제 실행시켜 볼때는 계정이름을 'hongju@ServerName' 으로 하셔야 합니다.

Windows 응용 프로그램에서 접근해서 클라우드 데이터베이스가 생성됩니다. 

using (SqlConnection conn = 
new SqlConnection(connString1Builder.ToString()))
{
 using (SqlCommand command = conn.CreateCommand())
 {
conn.Open();
// 테스트 데이터베이스 생성
string cmdText = String.Format("CREATE DATABASE {0}", sampleDatabaseName);
command.CommandText = cmdText;
command.ExecuteNonQuery();
conn.Close();
}
}
데이터베이스가 생성되었으니 테이블을 만들고 INSERT, SELECT는 아래처럼 기존 ADO.NET 코드와 동일하게 접근이 가능합니다. SqlCommand 로 작업하는 것은 다 아시니 불필요한 코드는 지면상 생략하겠습니다.~
// 테이블 생성
command.CommandText = "CREATE TABLE T2(Col1 int primary key, Col2 nvarchar(20))";
command.ExecuteNonQuery();

// INSERT
command.CommandText = "INSERT INTO T2 (col1, col2) values (8, N'한글 1'), (2, N'한글 2'), (3, N'한글 3')";
int rowsAdded = command.ExecuteNonQuery();
// SELECT
command.CommandText = "SELECT * FROM T1";
using (SqlDataReader reader = command.ExecuteReader())
{
 // MessageBox로 결과 확인
 while (reader.Read())
{
 MessageBox.Show(reader["Col1"].ToString().Trim() +reader["Col2"].ToString().Trim());
}
}
유니코드로 한글을 입력하면 한글 결과를 확인할수 있습니다. 다 아시는 구문이며 SQL Azure라고 큰 차이는 없다는것을 확인 가능합니다. 아래 구문이 새로운 것은 아니며 SQL Server 2008에서 제공되는 구문이랍니다.

INSERT INTO T2 (col1, col2) values (8, N'한글 1'), (2, N'한글 2'), (3, N'한글 3')

기존 T-SQL과의 차이점 등은 아래 링크를 참조해주십시오.

이상으로 SQL Azure CTP1에 대한 첫 소개를 말씀드렸습니다. 다음에는 보다 더 구체적인 예제로 SQL Azure를 접근해보겠습니다.
감사합니다.


SQL Azure Documentation:
http://msdn.microsoft.com/en-us/library/ee336279.aspx

SQL Azure Forum:
http://social.msdn.microsoft.com/forums/en-US/ssdsgetstarted/threads/


Connecting to SQL Azure Using ADO.NET:
http://msdn.microsoft.com/en-us/library/ee336243.aspx