티스토리 툴바


간단한 TCP 에코 서버를 만들었습니다. 서버는 select를 쓰기 때문에 총 63명의 동시 접속자를 받을 수 있고 접속한 유저는 자유롭게 패킷을 보낼 수 있고 보낸 패킷은 자신을 제외한 모든 유저들에게 브로드 캐스팅 되게 되어 있습니다. select 만으로 작동하기 때문에 추가적인 쓰레드는 존재하지 않고 일단 접속/해제에 대한 처리도 대충 해놓은 상태입니다. 클라이언트 테스트 용으로 뚝딱 만들었는데 이런저런 작업에 요긴하게 쓰지 않을까 싶어서 일단 공개해봅니다.

사용법은 echo_server.exe <port> 로 port 값이 없으면 80을 자동으로 사용하고 port 값은 0~65535 값으로 TCP 포트에 맵핑됩니다.

Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/344 관련글 쓰기

댓글을 달아 주세요

  1. hsjung 2011/08/05 22:49  댓글주소  수정/삭제  댓글쓰기

    테스트용 socket server가 하나 필요한 상황인데 짜기는 귀찮고... 유용하게 잘 쓰겠습니다. 감사~

비동기 디스크 I/O 처리에 대해 꽤 오랫동안 고민을 하고 또 했었는데 슥슥 짜다보니 의외로 간단히 풀렸습니다. 그래도 이것을 잘 쓰기위해서 지켜야할 규칙이 몇가지 존재하긴 하지만 규칙 자체는 꽤 오래전부터 생각하던 것이니 큰 문제가 되진 않죠. 일단 규칙부터 나열하면 다음과 같습니다.

1. 리소스 관리자는 파일이 존재하는한 무조건 유효한 포인터를 반환한다.
2. 리소스를 요청한 쪽은 리소스의 알맹이를 확인할 수 없고 포인터로 사용한다.
3. 리소스 요청후 로딩 여부와 관계없이 꼭 필요한 데이터는 메타를 통해 따로 접근한다.

일단 1번에 대해서는 많은 게임들이 적용하고 있는 상태이고 2번과 3번이 비동기에 따라오는 패널티와 비슷한 것입니다. 예를 들어 어떠한 메쉬를 로딩했을때 메쉬에 몇개의 vertex가 있는지 메쉬의 AABB는 어떠한지는 로딩이 끝날때까지 알 수 없습니다. 그렇지만 이러한 정보가 없으면 로딩한 리소스를 처리하기 위해서 나중에 추가적인 이벤트를 받아야 하고 경우에 따라서는 로딩이 끝날때까지 코드 실행 자체가 불가능해질 수도 있습니다.

그리고 비동기란 뜻은 로딩이 끝나는 시점을 알 수 없기 때문에 무작정 기다릴 수도 없는 것이고 이벤트를 받고 나중에 처리하는 방식으로 코딩을 하다보면 이것저것 너무 복잡해지게 됩니다. 따라서 리소스를 사용하는 측은 받은 포인터를 그래픽이나 사운드 모듈에 넘겨서 출력을 요구할 수 있지만 그 내용을 열어봐서는 안됩니다. 그렇지만 메쉬의 AABB나 노드 이름등 꼭 필요한 정보가 있을 수 있기 때문에 그러한 것은 리스소 관리자가 미리 로딩을 해두고 메타 데이터로 제공을 하게 합니다.

template<class T>
struct consumer
{
 OnLoad(T* resource) = 0;
 OnRemove(T* resource) = 0;
}

template<class T>
struct producer
{
 AddConsumer(consumer<T>* consumer);
 RemoveConsumer(consumer<T>* consumer);
 EventLoad();
 EventRemove();
 std::list<T*> m_consumerList;
}

그렇지만 모든 코드가 리소스의 알맹이를 모르고 지낼 수도 없고 매 프레임마다 IsValid() 같은 함수를 호출하게 할 수도 없습니다. 그래서 리소스들은 로딩이 끝나거나 삭제되는 시점에서 이벤트를 발생시키고 이벤트를 받은 객체들은 적절한 타이밍에 필요한 작업을 수행할 수 있습니다. 바로 consumer - producer 의 관계인데 가장 일반적인 예가 바로 texture - file 의 관계입니다.

텍스트 리소스 관리자는 어떠한 텍스쳐가 요청되면 파일 관리자에게 파일 로딩을 요청하고 texture 를 하나 만들어서 리턴해주게 됩니다. 이 과정에서 texture - file 은 consumer - producer 의 관계를 맺게 되고 file 객체는 로딩이 끝나는 시점에 texture 에게 OnLoad 이벤트를 호출해주게 됩니다. 여기서 주의할 점은 texture - file 이 포인터를 통해 호출을 하기 때문에 어떠한 이유에서 한쪽이 삭제될때 결합을 꼭 끊어줘야 한다는 점 입니다.

consumer - producer 의 연결 관계는 1:1 또는 n:m이 될 수도 있고 특히 consumer 는 1:n 의 상황에 처할 경우가 많기 때문에(material - texture, actior-mesh) consumer 측에서의 producer 의 관리는 템플릿 내에서 처리하는 것이 불가능합니다. 그렇지만 이러한 처리는 이전의 리소스 관리자에서도 리소스의 Create/Destroy 호출등으로 익숙해져 있기 때문에 큰 문제가 되지는 않으리라 생각됩니다.

일단 게임에서는 texture, mesh, wave 이외는 게임중 큰 로딩을 유발하거나 비동기 처리 대상이 되지는 않는다고 보지만 위에 언급된 consumer - producer 의 관계에 대해서는 계속 발전시켜 나가면서 더 적절한 모델이 있는지 조사해볼 계획입니다.
Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/320 관련글 쓰기

댓글을 달아 주세요

제목은 거창하지만 그냥 C/C++ 유저들이 편하게 SIMD 명령들을 쓸 수 있도록 제안된 Intrinsic 들을 사용해서 코딩을 좀 해봤습니다. 인텔이 제일 먼저 제안했던 것 같고 VS 6.0 에서는 따로 설치를 해줘야 했지만 VS 2003, 2005 에서는 바로 사용할 수 있도록 준비되어 있습니다. 아래 인텔 링크에서 간략한 설명을 볼 수 있습니다.

(Intel Home > Intel Software Network > Introduction to Intrinsics)
http://softwarecommunity.intel.com/articles/eng/3369.htm

저는 Kd-Tree 에 적용해서 테스트를 해봤는데 일단 스탠포드 토기를 이용한 800 * 800 화면 렌더링에서는 딱 195%의 효율을 보이더군요. 문서들에 따라서는 320~370% 까지 속도 향상이 있다고 하는데 그렇게까지 올라가진 않고 Ray 간격을 좀더 좁히면 300% 가까이 속도가 나기도 해서 Ray 간격대비 폴리건 수가 적은 환경에서는 지금보다 효율이 더 올라갈듯 싶긴 하지만 일단 테스트 베드에서 95% 증가라도 감지덕지 하고 있습니다.

SSE Intrinsic 에 대해서 간단히 소개를 해보면 다음과 같습니다.

float a = 1;
float b = 2;
float c = a + b; // 3

__m128 a = _mm_set_ps(1,2,3,4);
__m128 b = _mm_set_ps(2,3,4,5);
__m128 c = _mm_add_ps(a, b); // 3,5,7,9

여기서 쓰인 __m128 이란 명령어는 float[4] 에 해당하는 128비트 레지스터로서 SSE 기능이 CPU에 추가되면서 새로이 추가된 레지스터입니다. 128 비트 크기로 SSE 에서는 float[4] 데이터에 대해 동일한 연산을 연산을 한꺼번에 하거나 첫번째 컴포넌트만 따로 계산할 수도 있습니다.

FPU 에 비교해서 더 나은 점은 뭣보다 SIMD 연산으로 한번에 4개까지 동일한 계산을 수행할 수 있다는 점이 첫번째이며 두번째 장점은 FPU가 Stack 연산 방법을 취한 것과 달리 8개의 레지스터를 자유롭게 접근할 수 있기 때문에 FPU 코드에 비해 코드 분량이 훨씬 적다는 점 입니다. 직접 테스트 해보지 않았지만 FPU쪽 연산 유닛 속도가 훨씬 빠르다는 언급도 있었는데 직접 실험해보지는 않았습니다.

SSE가 도입되면서 주의해야 할 점중 하나가 byte align 문제로 접근할 메모리와 캐쉬 라인을 맞춰줘야 한다는 것 입니다. _mm_load_ps(), _mm_store_ps() 같은 명령이 대표적으로 올리고 내리는 주소들이 충분히 align 이 되었는지 확인해야 합니다. 게다가 테스트중 align 이 틀렸을때 바로 해당 코드가 아니라 근처에서 크래쉬가 발생해서 정확한 위치를 찾는데 고생하기도 했습니다. Out-of-Order 문제가 아닌가 싶은데 일단 명령들과 반응이 좀 다릅니다.

두번째는 masking 에 대한 요령입니다. SSE 는 기존 명령들과 다르게 4개를 한꺼번에 계산하기 때문에 분기가 필요할때는 상당히 고약해집니다. 그래서 사용하는 것이 masking 으로 다음과 같은 방식을 취하게 됩니다.

// FPU
float value;
if(value < 0)
 value *= -20.0f;
else
 value *= 20.0f;

__m128 value; // -2, -1, 1, 2
__m128 zero; // 0,0,0,0
__m128 mask = _mm_lt_ps(value, zero); // 0xffffffff,0xffffffff,0,0
value = _mm_add_ps(_mm_and_ps(mask,_mm_mul_ps(value,_mm_set1_ps(-20.0f))), _mm_andnot_ps(mask,_mm_mul_ps(value,_mm_set1_ps(20.0f))));
// value = (mask & (value * -20.0f)) + (!mask & (value * 20.0f))

이렇게 쓰는 첫번째 이유는 4개의 연산을 한꺼번에 해야한다는 것이고 두번째 이유는 SSE 에서 flag 결과물을 CPU 에서 바로 사용할 수 없기 때문입니다. SIMD 이기 때문에 두가지를 분리해 놓은 것이라고 볼 수 있지만 4개의 연산이 모두 일치할 경우 CPU 연산을 쓸 수 있었으면 좋겠단 생각이 약간씩 들긴 했습니다. ^_^

어쨌거나 이런 masking을 사용한 연산의 장점은 CPI 가 올라간다는 것입니다. 예전의 Triangle - Ray 충돌 체크 루틴이 0.91 언저리를 맴돌았는데 SSE를 적용한 다음 0.45 까지 떨어진 것을 살짝 충격을 받았으니까요. 어쨌거나 이렇게 분기가 필요하거나 결과에 따라 다른 계산이 필요할때는 여러가지 속도 저하가 일어나기 때문에 가능한 이런 연산을 피하는 쪽으로 SSE를 적용하는게 좋습니다.

중요한점은 SSE 를 적용을 해서 속도가 늘어나는가? 안늘어나는가? 입니다. 20%라도 속도 향상이 있다면 적용을 해보는 것이 좋고 그 반대로 속도가 나지 않는다면 빼버리는게 맞는 것이죠. MMX 와 달리 대부분의 FPU 명령이 구현되어 있기 때문에 일반 C 코드로 작성한 것을 그대로 SSE 로 옮기는 것도 상당히 수월하니까요. 앞으로 SSE 사용을 차곡차곡 늘려서 좀더 익숙해질 수 있도록 해봐야죠. 이제는 SSE 쓴다고 안돌아가는 PC도 거의 없을테니까요.
Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/317 관련글 쓰기

댓글을 달아 주세요

  1. cretom 2009/12/11 15:56  댓글주소  수정/삭제  댓글쓰기

    intrinsic 함수라는게 SIMD로 최적화한 함수를 말하는 거였네요..
    대부분 책에선 '인라인화한다' 라고만 되어 있어서
    그저 inline 과 무슨 차인가 싶었었는데 이번 기회에 배워갑니다

    gpg에서라던지 간접적으로
    많은 도움 받고 있습니다.. 감사합니다 (_ _)

사용자 삽입 이미지
http://www.mysticgd.com/site2007/

오랫만에 Mystic Game Development 사이트에 구경갔습니다. EMotionFX 란 라이브러리로 유명한 업체인데 최근에도 계속 업데이트를 하고 있나 보더군요. 회사에서 이런저런 얘기를 하던중 나온게 개발중인 게임에 대한 정보가 퍼블리셔들이나 각종 외부 라이브러리 업체등을 통해서도 유출된다는 얘기를 하더군요. 이 업체는 그래도 입이 든든한 편이지만 여기 나와있는 협력 업체 목록만 봐도 국내 어디어디에서 쓰고 있는지를 알 수 있으니 이것도 나름 정보 유출은 정보 유출이겠죠. ^_^
Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/339 관련글 쓰기

댓글을 달아 주세요

  1. 이쁜감자 2008/01/02 14:10  댓글주소  수정/삭제  댓글쓰기

    Epic 사이트에만 가도..
    국내 어느 회사가 몇 카피 샀다..
    프로젝트 몇개 진행중이다 까지 알수 있다고..
    (뭐 물론 NDA 겠지만 서도.. 일단 맺은 시점에서는 어느회사가 뭘하는지 정도는 대략 알수 있으니.. 흐음..)

사용자 삽입 이미지

http://www.lumonix.net/shaderfx.html

예전 같은팀에 있던 3D 디자이너 아저씨가 알려준 3DS Max 용 플러그인 입니다. 최근 기어즈 오브 워 캐릭터 모델 데이터를 구경할 기회가 있었는데 3DS Max 에서 사용할 수 있는 fx 파일이 같이 따라오는 것을 보고 나름 감동을 받았었습니다. 모델링을 수정하면서 게임에서 사용하는 쉐이더를 뷰 포트에서 볼 수 있다는건 이런저런 이유로 개발 효율을 높일 수 있을테니까요. 그래서 그 얘기를 했더니 ShaderFX 를 소개해주더군요.

일단 3DS Max 가 없는지라 테스트는 못해보고 동영상만 받아봤는데 몇가지만 확인하면 바로 팀에서 사용해도 괜찮을 것 같더군요. 일단 뷰포트에서 에서 쉐이더를 완벽하게 지원하는 것과 더불어 디자이너가 직접 쉐이더를 조합하거나 수정할 수 있고 그것을 배로 게임에서 사용할 수 있기 때문에 게임 렌더러와 3DS Max 간의 갭을 줄일 수 있고 더해서 디자인쪽에서도 GPU 를 사용하는 쉐이더의 장단점을 직접 몸에 익힐 수 있기 때문이죠.
Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/314 관련글 쓰기

댓글을 달아 주세요

사용자 삽입 이미지

http://www.codeproject.com/KB/graphics/MyPSD.aspx

xNormal 이란 프로그램을 받았다가 툴에서 PSD 파일을 읽어올 수 있다는 것을 발견하고 구글 검색을 좀 해봤습니다. 의외로 빨리 나왔는데 Code Project 쪽에 누군가가 PSD 임포터를 만들어서 올려 놨더군요. 코드도 바로 사용 가능하고 약간만 정리하면 속도도 꽤 빠르기 때문에 직접 이런저런 정리/테스트를 해놓았습니다.

찾는김에 PSD 에 대한 파일 포맷도 찾아봤는데 현재는 공개된 것이 없고 Adobe 쪽 개발자 사이트에가서 Photoshop CS3 SDK 사용을 신청하면 SDK 와 더불어 문서를 보내주는데 HTML 로 된 도움말 구석에 파일 포맷에 대해 잘 정리되어 있더군요. 그리고 위의 라이브러리가 바로 그 파일 포맷 문서를 보고 그대로 작성한 것도 확인했습니다. (구조 및 용어까지 깨끗히 정리해 놨더군요.)

툴에 파일 변경 추적기능과 함께 PSD 파일 로딩기능까지 넣으면 포토샵에서 작업한 파일을 저장해서 바로바로 확인할 수 있을 것 같아 여러가지로 편리하게 쓸 수 있을 것 같습니다. 우후훗~ 시간나면 3DS 파일도 접근하게 만들어볼까? -_-a
Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/306 관련글 쓰기

댓글을 달아 주세요

사용자 삽입 이미지

http://www.codejock.com/products/toolkitpro/



ToolkitPro 2007은 UI 라이브러리로 MFC/ATL 로 프로그램을 작성할때 기존 어플리케이션에서 쓰는 Propoerty Grid 나 스킨등을 컨트롤로 구현해놓았습니다. GPGStudy 에서 언급된게 기억나서 이번 툴을 만들때 한번 끼워넣어 봤는데 역시 상용이라 그런지 codeguru 류와는 퀄리티가 다르군요. Property Grid 덕분에 다이얼로그 만드는 귀찮음이 많이 줄어든 것도 좋고 여러가지로 대충대충 낑궈넣는데 속도를 내고 있습니다.

30일 데모판을 받아서 디버그 모드로 굴릴 수 있는데 나름 쓸만하니까 좀 써버고 정품 사달라고 졸라야 겠습니다. 400$ 밖에 안하는데 컨트롤들 때문에 고생하는 것보다 이렇게 사다 끼고 없는건 '그냥 대충' 넘어가는게 시간상 빠를 것 같으니까요. (먼산..)
Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/290 관련글 쓰기

댓글을 달아 주세요

사용자 삽입 이미지

새 맵 툴을 짜야하는 관계로 API로 이런저런 테스트 해보던 코드를 '툴에 적합한 라이브러리' 로 옮기고 있습니다. '툴에 적합한 라이브러리' 가 사실 문제였는데 일단 D3D 와 친해야 하고 게임 코드와 같이 컴파일이 가능해야하며 왠만큼 저렙이 만지더라도 슥삭슥삭 필요한 부분을 고칠 수 있는 그런 툴을 위한 프레임 워크가 필요한 것입니다.

네... 또다시 MFC 께서 당선 되셨습니다. 혹자는 C++ Builder 를 써보라고 하지만 D3D 와 궁합이 안좋은 부분도 있고 일단 저부터 익숙하지 않아서 쓰기가 힘든 상태입니다. 개인적으론 .NET 시도를 해보고 싶었지만 이런저런 시간이나 비용대비 효율은 제로에 가깝기 때문에 가볍게 무시해버렸고 WTL 같이 수상한 녀석도 있지만 검색해도 쓸만한 자료가 너무 안나와서 바로 GG 쳐버렸습니다. OTL

흑흑.. 정녕 C++ 사용자에게 미래는 없는 것인가요? OTL

p.s.그래도 이런거라도 있는게 어디야.. 끄덕끄덕끄덕~~ 땡스빌~!!
Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/294 관련글 쓰기

댓글을 달아 주세요

사용자 삽입 이미지

이미지 오브 더 데이~!! 입니다. 오랫만에 듣는 말인데 어쨌거나 위의 이미지는 Kd-Tree 를 이용한 Ambient Occlusion 시뮬레이션을 돌려본 것으로 맵핑을 한 것은 아니고 Ray-Tracing 기법을 사용해서 모든 폴리건에 대한 차폐 테스트를 실행한 결과물입니다. 위의 이미지 만드는데 걸린 속도를 물어보시면 여러가지로 부끄러우니 묻지 말아주시고(T_T) 위의 이미지는 아무 쉐이딩도 들어가지 않고 단지 Ambient Occlusion 계산만 들어간 3D Mesh 입니다.

AO 결과물과 기존 라이팅 기법을 도입한 결과물을 보면 확실히 입체감이 잘 살아 있으며 자연스러움이 넘친다는 것 입니다. 어떻게보면 Normal/Specular Map 으로 떡칠한 것보다 훨씬 더 양감이 잘 살기는 하는데 분야가 약간 다르니 두가지를 모두 조합해서 쓰는게 점점 대세가 되지 않을까 싶습니다. 어쨌거나 Normal Map 을 제대로 찍어내는 기술은 여러가지로 단가가 쎄기 때문에 많은 개발팀들이 긴 시간동안 여러가지 저울질도 하고 효율성을 높이려고 노력할테고 AO 도 그런 발버중치는 도입하게 되는 또하나의 기법이 아닐까 싶습니다.

어쨌거나 이 작업 마치면 당분간 툴이랑 지형으로 고민해야 하니 렌더링 관련 작업은 바바이가 되겠군요.
Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/274 관련글 쓰기

댓글을 달아 주세요

  1. jalnaga 2007/11/19 11:34  댓글주소  수정/삭제  댓글쓰기

    사실 저 엠비언트 어클루션은 아주옛날 고리짝부터 게임그래픽디자이너들이 텍스쳐의 양감을 살리기위해 디퓨즈맵에 멀티플라이오 얹으면서 썼던기술인지라...
    물론 성환씨나 근우씨같은 굇수들이 등장하면서 AO없이 뽕숑뽕숑한 양감을 그리는 사람들이 생겨서리...
    다이렉트 엠비언트 어클루션이라는게 뭡니까? 크라이시스에 들어가는 거라고 하던데...

Kd-Tree 를 구축하는 동안 힘들 었던 부분 몇가지를 지적해보고자 합니다.

1. SAH - Surface Area Heruistic

Kd-Tree 를 구축하는데 있어 중요한 것은 "효율적인 파티션 선택" 입니다. 공간을 x,y,z 축으로 나눌때 어느 축으로 나누는 것이 Ray-Test 를 했을때 가장 효율적인지 추측해서 결정하는 것이죠. 그런데 이 SAH 에 대한 수식은 곳곳에 나타나지만 실제 구현코드는 찾아보기 힘들고 각각 코드마다 모양이 다양하기 때문에 딱 짤라서 어떤 코드를 참고하기 좋다고 하기 힘듭니다. 일단 제가 사용한 SAH 함수를 psudo code 로 표현을 하자면 다음과 같습니다.

k_t = 0
k_i = 1

function sha(p_l, p_r, n_l, n_c, n_r)
 sah = k_t + k_i * min(p_l*(n_l+n_c) + p_r*n_r, p_l*n_l + p_r*(n_r+n_c))
end

for sorted vertex list inside the partition
 p_l = vertex.axis - partition.minimum.axis
 p_r = partition.maximum.axis - vertex.axis
 n_l = number of triangles left side of vertex.axis
 n_r = number of triangles right side of vertex.axis
 n_c = number of triangles on vertex.axis
 if sah(p_l, p_r, n_l, n_c, n_r) is minumum
  set vertex as best candidate
 end if
end for

SAH 에 관한 논문들을 몇가지 찾아보면 상수들이 나오는데 위에서 k_t 와 k_i 가 있어서 각각 트리를 1칸 내려갈때마다 들어가는 비용과 Ray-Test 1회를 할때마다 들어가는 비용을 계산에 넣도록 되어 있습니다. 이 상수는 각 시스템마다 다른 비용이 들기때문에 논문들에서 공개하고 있지 않기 때문에 모든 수치를 무시할 수 있는 숫자로 바꿔버렸습니다. 적절한 상수들은 Kd-Tree 의 depth 를 조절하는 기능을 갖고 있지만 위와 같이 각각 0, 1 로 해버리면 tree는 최대 depth 까지 만들어지게 됩니다.

2. 빠른 Kd-Tree 생성

Kd-Tree 의 경우 생성하는 속도에 대한 연구도 꽤 진행 되어서 최근 몇몇 논문에서는 0.1초내에 100만 폴리건이 넘는 공간을 빌드하는 것도 발표되어 있습니다. Realtime Ray-Tracing 을 위해서 연구가 되는듯 싶은데 그정도까지는 아니라도 무식하게 접근할 경우 빌드하는데 너무 많은 시간이 소비되게 됩니다. 그중 한가지가 AABB 의 시작과 끝에 대한 event list 를 작성해서 접근하는 방식으로 다음 논문을 보면 해당 내용이 나와 있습니다.

http://www.cgg.cvut.cz/members/havran/ARTICLES/ingo06rtKdtree.pdf

제 경우 Kd-Tree 에 사용할 모든 삼각형들의 aabb 목록을 만든다음 각 axis 에 대해서 시작-끝에 대한 event list 를 세개 만든다음 맨 처음 소트를 시킨다음 사용하고 있습니다. event 는 float 값의 aabb 시작 및 끝에 해당하는 값과 30 bit 값으로 어느 AABB의 시작과 끝인지를 나타내는 triangle number, 그리고 시작/끝/시작+끝 3가지를 상태를 갖고 있는 enum 값을 2비트로 사용하고 있습니다.

이 event list 는 각 axis 별로 만들어지고 가장 적절한 SAH 값을 사용하기 위해 for 루프를 돌면서 처음부터 끝까지 한번 훑어가면서 가장 적절한 split candidate 를 찾게 됩니다. 최적의 split 이 정해지면 split 에 의해서 나눠진 left-right partition 에 필요한 event list 를 다시 작성해야 합니다.

이 부분에서 꽤 헷갈렸는데 정확히 언급한 자료가 없지만 일단 부모로 부터 넘겨받은 event list 을 splt 기준으로 왼쪽-오른쪽에 포함되는 녀석들만 적절히 골라 나눈다음 자식에게 넘겨줘야 합니다. 여기가 꽤 까다로운데 x 축을 기준으로 나눴을 경우 x 축의 event list 는 기존의 것을 그대로 사용할 수 있지만 y, z 축을 기준으로 만들어진 event list 들은 x 축에 대한 정보가 없기 때문에 aabb 번호를 참고해서 각 event 들이 적절하게 다시 생성되도록 해야하는 것이 여러가지 까다롭습니다.

3. 빠른 Kd-Tree vs Ray 테스트

Kd-Tree 의 멋진 점은 수백만개의 객체가 흩어져 있는 공간에 대해 빠른 Ray-Test 가 가능하다는 점 입니다. Stoll 의 PT 자료 맨 마지막에 “Rays per Second” is measured in millions. 라고 언급 했듯이 Kd-Tree 는 빠른 Ray-Test 을 하기 위해서 모든 수단을 가리지 않고 동원해야 합니다. 여기서부터는 최적화에 깊이 들어가는 부분인데 일단 제 최종 버젼의 Kd-Tree 의 Kd-Tree Traverse 루프는 브라켓을 포함해서 24줄밖에 되지 않습니다.

최적화된 Kd-Tree는 임의의 x,y,z 축을 기준으로 공간이 나눠지게 되는데 각각 축에 대해서 if 문을 통해 if x ~ if y ~ if z 식으로 코드를 작성하지 않고 축을 int  axis 타잎으로 사용한다음 값에 대한 접근도 이 axis 단위로 해야한다는 것입니다. 예를 들어 Kd-Tree 를 돌아다닐때 Ray 의 방향의 역수인 inv_dir 값이 필요한데 D3DXVECTOR3 inv_dir; 를 사용하지 않고 float inv_dir[3]; 를 사용해서 계산해야 한다는 것입니다. 물론 계산에 사용하는 모든 값들은 D3DXVECTOR3 와 같이 x,y,z 접근하는 형태가 아니라 float[3] 형으로 선언해야겠죠?

이러한 코드적인 것에 더해서 VS 2005 의 컴파일 옵션, Intel vTune 등을 통한 최적화 작업도 꾸준히 찾아보고 시도해봐야 한다는 것입니다. VS 2005 가 생성하는 코드의 경우 FPU 연산이 상당히 비효율적인데 몇몇 C++ 쪽 컴파일 옵션을 변경하고 강제로 SSE를 켜줄경우 옵션 변경만으로도 상당한 성능 향상이 있습니다. 제가 이렇게 작성한 코드조차 IPC 값이 1.4에 가깝게 나와서 상당히 느린 코드로 나오고 있기 때문에 아직 많은 부분에서 최적화의 여지가 남아 있습니다.

Kd-Tree 의 경우 Ray-Test 코드가 모두 읽기 전용 계산이기 때문에 멀티쓰레드화에 매우 적합하고 Packet Traversing 이라고 해서 SSE를 통해 한번에 4개씩 Ray 를 쏘는 방식을 통해 수배에 가까운 효율을 높이기도 합니다. 저도 여기까지는 아직 접근하지 않았지만 현재 2.4Ghz CPU에서 랜덤한 Ray 를 생성해서 테스트해본 결과 1.4M Ray/s 까지 속도가 나온 상태입니다. 여기 SSE 와 Multi-Tread 를 더해면 적게는 2~3배에서 많게는 8~10배정도의 속도 향상을 기대하고 있습니다. ^_^

---------------------------------------------------------------------------------

Kd-Tree 는 만능 트리는 아니지면 일단 Static 한 공간에 대해서는 상당히 효율적인 공간 구성이 가능하게 됩니다. Dynamic 한 것에 대해 여러가지 연구가 되고 있지만 일단 구축하는데 많은 시간과 메모리를 소비한다는 문제로 당분간은 비현실적이지만 depth 를 제한하고 미리 tree 를 구축할 경우 다양한 방법으로 활용이 가능하다고 생각합니다.

일단 일주일이 넘게 Kd-Tree 코드를 만들면서 고민했던 것을 후루룩 털어 놓았는데 혹시 비슷한 코드를 작성하는 분들께 도움이 될 수 있었으면 좋겠습니다.

Posted by zupet

트랙백 주소 :: http://zupet.tistory.com/trackback/272 관련글 쓰기

댓글을 달아 주세요