[kgc2014] 두 마리 토끼를 잡기 위한 c++ - c# 혼합 멀티플랫폼 게임 아키텍처...

48
두 마리 토끼를 잡기 위한 C ++ - C# 혼합 멀티플랫폼 게임 아키텍처 설계 김성균 ㈜이노스파크 Technical Director

Upload: sungkyun-kim

Post on 07-Jul-2015

3.315 views

Category:

Technology


2 download

DESCRIPTION

이미 많은 개발자들이 C#의 장점을 누리고 있으나, 본 PT에서는 높은 성능과 생산성을 동시에 달성하기 위해 C/C++로 개발된 native 게임 코드에 스크립트 언어로서 C#을 통합 할 수 있는 방법을 제시한다. 이를 위해 오픈소스 .Net 프레임웍인 Mono의 사용방법과 모바일 플랫폼에서의 특이사항들을 자세히 설명한다. 또한, C/C++언어에 C#을 비롯한 다양한 스크립트 언어를 효율적으로 혼합하여 게임을 구현할 수 있는 아키텍처를 제시한다. clang과 reflection을 이용하여 서로 다른 언어 간 인터페이스 노출을 자동화하고, 게임 내 오브젝트의 생명주기를 자동으로 관리할 수 있는 기법에 대해 설명한다.

TRANSCRIPT

Page 1: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

두마리토끼를잡기위한C++ - C# 혼합멀티플랫폼게임아키텍처설계

김성균

㈜이노스파크

Technical Director

Page 2: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

목차

1. 개요

2. C# (.NET) 실행환경

3. 언어간인터페이스결합자동화

4. 언어간개체결합

5. 결론

6. 부록

Page 3: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계
Page 4: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계
Page 5: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

동기

빠른성능

구현의자유성높은이식성

− 코딩의실수는치명적− 낮은생산성

높은생산성 (문법, OOP …)

적절한성능 C++와유사한문법

− 저수준제어어려움− 다른환경과연동어려움

Page 6: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계
Page 7: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

목표

• 성능이중요한기능• 저수준제어• 외부라이브러리연동

• 빠른구현과잦은변경이필요한상위수준의기능들

• 성능이중요하지않은코드

언어간코드연동을위한작업량최소화코드를다른언어로손쉽게변환

Page 8: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계
Page 9: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

Native Code

Script

Lib

Page 10: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

구성요소

C# 런타임

언어간인터페이스자동화

언어간개체결합

Page 11: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C# (.NET) 실행환경

Page 12: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

Mono™ = Microsoft .NET의포팅

≈오픈소스, 공짜아님

Page 13: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

각플랫폼용 Mono 빌드

소스코드

Runtime

(.a .so .lib .dll)

make

make

configure

Compiler

mono

Page 14: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

App

Embedded Mono

Native Runtime

.NET Assembly로드 & JIT 컴파일

컴파일

JIT / AOT Binary

Mono Runtime

실행

.NET Compiler

Objective-C

RuntimeJava VM

Page 15: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

임베딩을위한 C# 코드처리

.NET Compiler

monolinker

mono

Reduced Assembly

.NET Assembly

Stripped Assembly

mov …

push …

call …

Assembly Code

.NET Libraries

AOT 컴파일

Page 16: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

Embedded Mono 유의사항

Ahead-of-Time 컴파일– Just-in-Time 실행을지원하지않는 OS를위함

– 실행될 CPU/OS 마다별도의 Mono 컴파일러와런타임필요

– 실행파일크기문제

– 리소스업데이트로 C# 코드업데이트불가능

디버깅– Custom Command Soft Debugger 사용필요

– 디버깅대상이 client, MonoDevelop이 server로작동

– 쓰레드에서 Mono 코드를실행하지않으면중단점작동안됨

sgen GC 사용시 MonoObject* 대신 gc_handle 에의존

mono_trace_set_level_string 사용추천

Page 17: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

언어간인터페이스결합자동화

Page 18: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

목표: 언어간혼용의자유도

class Character

{

void SetPosition( float );

float position;

};

class Character

{

void Touched() …

void UpdateUI() …

int health;

}

Page 19: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

목표: 언어간혼용의자유도

void Character::Touched()

{

SetPosition( position + 1 );

}

class Character

{

void SetPosition( float );

float position;

};

Page 20: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

목표: 언어간혼용의자유도

void Character::SetPosition( float position )

{

health = …

UpdateUI();

}

class Character

{

void Touched() …

void UpdateUI() …

int health;

}

Page 21: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

목표: 언어간혼용의자유도

• 구현의특성에가장적합한언어에서코딩

• C# 모듈을 C++ 처럼, C++ 모듈을 C# 처럼사용

• 인터페이스용코드필요– 각타입에대한메타정보등록코드

– 호출언어를위해정의된선언(declaration) 코드

• 언어간인터페이스를자동화한다면?!

Page 22: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

언어간인터페이스자동생성

C++ 소스코드

(.h)

C# 소스코드

C++ 인터페이스C# 코드

C# 인터페이스C++ 코드

2

3

4

1

CppSharp

CXXI

Page 23: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C#을위한 C++ 인터페이스자동생성

1. C++ 헤더파일들을파싱(!)하여인터페이스들분석

2. (C++ 인터페이스를 Mono에노출하는 C++ 코드)와이를호출할수있는 C# 코드생성

3. C# 코드에서인터페이스코드를호출

4. C# 코드컴파일시인터페이스코드를함께컴파일

+ Clang

CppSharp

CXXI

Page 24: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C++ 파싱 - Clang

LLVM 컴파일러의 C/C++ 파서

상용제품수준

Visual C++ 구문들도인식

불완전한구문에의해파싱이실패하지않음

libclang을이용하여스크립트언어에서도사용가능,

Python 모듈기본제공 – AST 생성

− libclang은 clang의극히일부의기능만을노출하므로커스터마이제이션필요

Page 25: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C++ 인터페이스생성예제 (CppSharp)

// C++ 소스코드class Foo

{

int variable;

int DoSomething ( int arg1, std::string arg2 );

};// 자동생성된 C#용인터페이스코드class Foo

{

// C++의메모리레이아웃선언struct Internal { … }

// 사용자를위한편리한인터페이스int variable {

get { return _Instance.ToPointer()->variable; }

set { _Instance.ToPointer()->variable = value; }

}

int DoSomething( int arg1, string arg2 )

{ return Internal.DoSomething( _Instance, arg1, arg2 ); }

}

Page 26: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C++ 인터페이스생성예제 (Embedded)

// C++ 소스코드#define EXPORT __attribute__((annotate(“ExportToMono”)))

class EXPORT Foo

{

int variable;

int DoSomething ( int arg1, std::string arg2 );

};

// 자동생성된클래스등록코드RegisterNativeClass<Foo>();

RegisterNativeClassVariable<Foo,int>( “variable”, offsetof(Foo, variable) );

RegisterNativeClassMethod<Foo,int,int,std::string>( “DoSomething”, &Foo::DoSomething );

+ Clang

Page 27: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C++ 인터페이스생성예제 (Embedded)

// 자동생성된클래스등록코드에의한실제내부작동RegisterNativeClass<Foo>();

// Foo 타입에대한 RTTI등각종기본정보들등록

RegisterNativeClassVariable<Foo,int>( “variable”, offsetof(Foo, variable) );

// C#에서호출할 accessor 메소드등록int NativeGetInt( void* nativeObject, int offset );

mono_add_internal_call( “NativeGetInt”, &NativeGetInt );

void NativeSetInt( void* nativeObject, int offset, int value );

mono_add_internal_call( “NativeSetInt”, &NativeSetInt );

RegisterNativeClassMethod<Foo,int,int,std::string>( “DoSomething”, &Foo::DoSomething );

// C#에서호출할메소드등록static int Foo_DoSomething( Foo* nativeObject, int arg1, std::string arg2 )

{ nativeObject->DoSomething( arg1, arg2 ); }

mono_add_internal_call( “Foo.NativeCall_DoSomething”, &Foo_DoSomething );

Page 28: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C++ 인터페이스생성예제 (Embedded)

// 자동생성된 C#용인터페이스코드class Foo

{

// C++의메소드들등록[MethodImplAttribute(MethodImplOptions.InternalCall)]

extern int NativeCall_DoSomething( IntPtr nativeObject, int arg1, string arg2 );

// 사용자를위한편리한인터페이스int variable {

get { return NativeGetInt( nativeObject, 4 ); }

set { NativeSetInt( nativeObject, 4 ); }

}

int DoSomething( int arg1, string arg2 )

{ return NativeCall_DoSomething( nativeObject, arg1, arg2 ); }

}

Page 29: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C++를위한 C# 인터페이스자동생성

1. .NET reflection을사용하여 DLL 안의인터페이스들추적

2. DLL 인터페이스를호출할수있는 C++ 코드생성

3. C++ 게임코드에서인터페이스코드를통해 DLL의코드를 C++ 처럼호출

4. C++ 게임코드컴파일시인터페이스코드를함께컴파일

Page 30: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C# 인터페이스생성예제 (Delegate)

// 자동생성된 C# 인터페이스코드partial class Foo

{

void ExposeToNative() {

RegisterMonoMethod( 0, () => { return variable; } ); // Get_variable

RegisterMonoMethod( 1, (int value) => { variable = value; } ); // Set_variable

RegisterMonoMethod( 2, DoSomething ); // DoSomething

}

}

// 자동생성된 C++ 인터페이스코드class Foo

{

int Get_variable() { monoMethods[0](); }

void Set_variable( int value ) { return monoMethods[1]( value ); }

int DoSomething( int arg1, std::string arg2 )

{ return monoMethods[2]( arg1, arg2 ); }

};

delegate를 native 함수포인터로전달

RegisterMonoMethod에의해등록된함수포인터

Page 31: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C# 인터페이스생성예제 (Embedded)

// C# 소스코드[Export]

class Foo

{

int variable;

int DoSomething( int arg1, string arg2 ) { … }

}

// 자동생성된 C++용인터페이스코드class Foo

{

int Get_variable() {

void* ret = mono_runtime_invoke( monoClass, monoObject, “get_variable” );

return *(int*)ret;

}

void Set_variable( int value ) {

mono_runtime_invoke( monoClass, monoObject, “set_variable”, [&value] );

}

int DoSomething( int arg1, std::string arg2 ) {

void* ret = mono_runtime_invoke( monoClass, monoObject, “DoSomething”,

[&arg1, mono_string_new_wrapper(arg2)] );

return *(int*)ret;

}

};

Page 32: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

언어간개체결합

Page 33: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

언어간결합의깊이

C++ Class

C++ variables

C++ methods

C# Class

C# variables

C# methods

Hybrid Class

C++ variables

C# variables

C++ methods

C# methods

오브젝트참조클래스형변환

C++ 기능모듈

C# 기능모듈

표준호출단순타입

모듈간결합 다른객체간결합 객체수준결합

개체생명주기관리

Page 34: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

객체수준결합

단일메모리의구역분할

• C# 클래스선언에서 C++ 클래스의영역미리선언필요

• 개체의수명은 C#에의해서만관리되어야함

• C++ new/delete 사용불가

Page 35: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

객체수준결합

개체분할, 논리적결합

• 독립적인 C++개체와 C#개체• 필요할때에만상대개체생성가능

• 자동생성된소스코드빌드에유리(partial class)

• 개체수명관리가핵심

Page 36: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

Garbage Collection vs new/delete

mono_gchandle_new

mono_gchandle_free

C++ 개체가소유자

new / delete

C# 개체가소유자 상호참조

gchandle refcount

Page 37: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

상대개체동적생성예제

// 자동생성된 C++용인터페이스코드class FooProxy

{

FooProxy( Foo* nativeObject )

{ // C#측의 Foo 개체가없으면생성 }

int DoInCS( int arg )

{ // C#측의 Foo.DoInCS 호출 }

};

// C# 소스코드class Foo : public NativeBound

{

int DoInCS( int arg ) { … }

}

// C++ 소스코드class Foo : public MonoBound

{

int DoInCPP( int arg )

{

return FooProxy(this).DoInCS(arg);

}

};

성능주의

Page 38: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

3 1

Mono GC에의해제거가능

Mono GC에의해제거방지

GC handle

Mono GC

Native GC

0

Mono GC에의해제거됨

삭제됨

참조카운트

살아있는쌍 GC 활성화 Native 삭제

삭제 - Garbage Collector 연동

GC handle 삭제

Page 39: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

최종형태

Data Engine

부분최적화

과거의게임코드

Page 40: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

결론

Roslyn

.NET Native, IL2CPP

CppSharp, Script#

거의모든플랫폼에서 .NET과 C# 사용가능

언어간혼합의자동화 → 더욱높은생산성과이식성

.NET과 C#은여전히진화중

Page 41: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

Q&A

감사합니다

Page 42: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

부록

Page 43: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

참고자료

.NET– Reflection: http://msdn.microsoft.com/en-us/library/f7ykdhsy(v=vs.110).aspx

– Garbage Collection: http://msdn.microsoft.com/en-us/library/0xy59wtx(v=vs.110).aspx

Mono– http://www.mono-project.com

– Compiling Mono: http://www.mono-project.com/docs/compiling-mono/compiling-from-git/

– Embedding Mono: http://www.mono-project.com/docs/advanced/embedding/

– InterOp with Native: http://www.mono-project.com/docs/advanced/pinvoke/

– CppSharp: https://github.com/mono/CppSharp

– CXXI: http://tirania.org/blog/archive/2011/Dec-19.html

– Mono for Unreal Engine: http://mono-ue.github.io/

Clang– http://clang.llvm.org

– Python with Clang: http://eli.thegreenplace.net/2011/07/03/parsing-c-in-python-with-clang

Page 44: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

iOS 시뮬레이터용 Mono 런타임 configure

• --build=i386-apple-darwin13.0.0

• CC, CXX=<Xcodepath>/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin 안의 gcc와 g++

• CFLAGS, CXXFLAGS– -arch i386 –miphoneos-version-min=<최소지원버전>

– -isysroot=<Xcodepath>/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator<버전>.sdk

• LD, AS, AR, LIBTOOL, STRIP, RANLIB=<iPhoneSimulator SDK>/usr/bin 안에있는툴들사용

configure

Page 45: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

iOS 기기용 Mono 런타임 configure

• --host=arm-apple-darwin10

• --target=arm-apple-darwin10

• CC, CXX=<Xcodepath>/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin 안의 clang과 clang++

• CFLAGS, CXXFLAGS

– -arch armv7

– -isysroot=<Xcodepath>/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS???.sdk

• LD, AS, AR, LIBTOOL, STRIP, RANLIB=<Xcodepath>/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin 안에있는툴들사용

configureMono 3.2의경우 XCode 4.x를사용해야함

Page 46: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

Android 기기용 Mono 런타임 configure

• --host=arm-linux-androideabi

• --target=arm-linux-androideabi

• CC, CXX=<Xcodepath>/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin 안의 clang과 clang++

• CFLAGS, CXXFLAGS– -march=armv7-a

– -mfloat-abi=softfp

– -mfpu=neon

– --sysroot=<NDK_ROOT>/platforms/android-<version>/arch-arm

• LD, AS, AR, LIBTOOL, STRIP, RANLIB 설정불필요• --libdir <NDK_ROOT>/platforms/android-<version>/arch-

arm/usr/lib

configure

Page 47: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

C++ → Lua 예제

// 자동생성된클래스등록코드에의한실제내부작동RegisterNativeClass<Foo>();

// 클래스를 lua table로생성lua_createtable( “Foo” );

RegisterNativeClassVariable<Foo,int>( “variable”, offsetof(Foo, variable) );

// lua table에 getter/setter 멤버함수들등록lua_pushcclosure( “Get_variable”, &NativeGetInt );

lua_pushcclosure( “Set_variable”, &NativeSetInt );

RegisterNativeClassMethod<Foo,int,int,std::string>( “DoSomething”, &Foo::DoSomething );

// lua table에멤버함수호출자등록lua_pushcclosure( “DoSomething”, &Foo_DoSomething );

// 자동생성된 lua용인터페이스코드 - 주석또는 Lua Checker 용도외에는필요없음Foo = {

Get_variable = function(),

Set_variable = function( int_value ),

DoSomething = function( int_arg1, string_arg2 )

}

명시적 accessor 대신 __index,

__newindex로 구현가능

Page 48: [KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계

Lua → C++ 예제

// lua 소스코드Foo = {

variable = 0,

int_DoSomething = function( int_arg1, string_arg2 )

end

}// 자동생성된 C++용인터페이스코드class Foo

{

int Get_variable() { return lua_tointeger( “variable” ); }

void Set_variable( int value ) { lua_pushinteger( “variable”, value ); }

int DoSomething( int arg1, std::string arg2 )

{

lua_pushinteger( arg1 );

lua_pushstring( arg2 );

lua_pcall( “DoSomething” );

return lua_tointeger( STACK_TOP );

}

};