블로그 이미지
Every unexpected event is a path to learning for you.

카테고리

분류 전체보기 (2093)
Unity3D (467)
Programming (453)
Unreal (2)
Gamebryo (56)
Tip & Tech (157)
협업 (26)
3DS Max (3)
Game (12)
Utility (98)
Etc (87)
Link (29)
Portfolio (18)
Subject (90)
iPhone (30)
Android (2)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (1)
Memories (16)
Interest (36)
Thinking (34)
한글 (23)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (15)
Exercise (3)
나만의 맛집 (2)
냥이 (7)
육아 (2)
Total1,078,402
Today41
Yesterday386
Statistics Graph

달력

« » 2017.08
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

공지사항

태그목록

Tistory Cumulus Flash tag cloud by BLUEnLIVE requires Flash Player 9 or better.

문서들을 여기 저기 따로 공유하려느 번거로워서 유니티 최적화 관련 문서들을 모아두었습니다. 정리는 두서 없이 했고,다만 출처가 유니티 크루인지 아닌지로만 분류하였습니다. 추가적으로 발견하는 대로 지속적으로 갱신 할 예정입니다.


From Unity


from Developers


출처 : http://ozlael.tistory.com/32

저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by blueasa

댓글을 달아 주세요



  • 1. Unity 엔진 최적화 테크닉 총정리 http://cafe.naver.com/devrookie 2014.12.08
  • 2. 1. 스크립트 최적화 2. 리소스 최적화 3. 그래픽스 최적화 4. 물리 엔진 최적화 5. 기타
  • 3. 들어가기에 앞서 본 내용은 인터넷에 공개된 다양한 참고 자료들을 바탕으로 작성 되었습니다. 유니티의 버전업에 따라서 본내용이 다를 수도 있습니다. (ex. Unity 5.x) 보다 자세한 내용은 마지막 참고자료 링크를 참조해 주세요.
  • 4. 병목지점을 파악하라
  • 5. 5 최적화의 시작은 병목 파악부터 CPU • 너무 많은 DP CALL • 복잡한 스크립트나 물리 연산 Vertex Processing • 너무 많은 버텍스들 • 버텍스당 너무 많은 연산 (버텍스 셰이더) Fragment Processing • 너무 많은 픽셀, 오버 드로우(OverDraw) • 프래그먼트당 너무 많은 연산 (프래그먼트 셰이더/픽셀 셰이더) Band Width • 크고, 압축되지 않은 텍스쳐 • 고해상도 프레임 버퍼
  • 6. 스크립트 최적화
  • 7. 7 스크립트 최적화 (1) • 유니티의 핵심 기능은 모두 C++로 제작되어 있다 • 예) Transform.position 에서 Transform은 C# 속성, position은 C++ 영역 • 유니티 객체들을 변수에 저장해서, 캐싱해서 사용하는것이 좋다. • FindObject 계열 함수들은 매우 느리다. (미리 찾아서 캐싱) • Instanitate 와 Destory 함수를 이용한 프리팹의 생성/해제는 비용이 크다 • 활성화/비활성화를 활용 -> 오브젝트 풀링 • Update 함수보다는 CoRoutine을 활용한다. • 나눗셈 보다는 곱셈이 몇 십 배 빠르다.
  • 8. 8 스크립트 최적화 (2) • 박싱과 언박싱은 부하가 큰 작업이다 • 나눗셈보다는 곱셈이 몇십배 빠르다 • magnitude 보다는 sqrMagnitude를 사용해서 비교한다. (제곱근 계산 x) • 삼각함수의 값은 상수로 저장하고, 사용하는 것이 좋다. • 문자열은 readonly 혹은 const 키워드를 사용하여, 가비지 컬렉션으로부터 벗어나도록 한다.
  • 9. 9 만흉의 원인 : 가비지 컬렉터 가비지 컬렉터(GC)는 언제 일어날지 모른다. • Mono의 동적 메모리 관리 때문에, 메모리 해제를 위해 GC가 자동 호출 된다. • GC는 언제 일어날지 모른다. • GC가 일어나면, 게임이 멈추는 현상(랙)이 발생하게 된다. • 동적 메모리 해제가 가능한 일어나지 않도록 하는것이 GC 관리의 핵심 오브젝트 풀링 • 오브젝트(or 프리팹)의 동적 생성과 해제는 부하가 크다. • 오브젝트가 해제되면, 언젠가는 GC가 동작하여 정리 한다 = 랙이 발생 • 오브젝트를 풀을 만들어 미리 많이 만들어 두고, 활성화/비활성화로 사용한다. • 풀에서 가져와서 사용하고, 사용이 끝나면 비활성화 상태로 풀에 반환 • 오브젝트 풀링 사용은 선택이 아닌 필수!!
  • 10. 10 GC에게 먹이 주지 않기 문자열 병합은 StringBuilder • 일반 String + String을 쓰면 임시 문자열이 생성됨 • StringBuilder.Append() 함수를 사용하여 병합 foreach 대신에 for문 사용 • 일반 array에 한해서… • Foreach는 한번 돌때마다 24byte의 쓰레기 메모리를 생성 • 10번만 돌아도 240byte의 GC 수거 메모리를 남김 태그 비교에는 compareTag() 사용 • If(go.tag == “enemy”) 대신에 go.compareTag(“enemy”) 사용 • 객체의 tag 프로퍼티를 호출하는것은 추가 메모리를 할당하고, 복사
  • 11. 11 GC에게 먹이 주지 않기 (2) 데이터 타입에는 Class 대신 구조체 사용 • 구조체는 GC에 들어가지 않는다. Stack에 할당 즉시 해제할 때는 Dispose 수동 호출 • 오브젝트의 dispose를 수동으로 호출하여, 즉시 cleanup 임시 객체들을 만들어내는 API들을 조심 • Ex. GetComponents<T>, Mesh.Vertices, Camra.allCameras, etc…
  • 12. 12 C++ <-> C# 오버 헤드 객체의 변경 사항을 캐싱 • 객체의 이동과 변형에 대한 처리를 캐싱하고, 매프레임에서 딱 한번만 처리한다. • Ex. Move() 컴포넌트 참조를 캐싱 • Getcomponent()는 한번만 호출하여, 객체를 캐싱해두고 사용 한다. 빈 콜백 함수는 제거 • Start()나 Update() 같은 콜백함수는 비어있어도, 성능에 영향을 끼친다.
  • 13. 리소스 최적화
  • 14. 14 권장 압축 텍스쳐 사용하기 • 아이폰(PowerVR) : PVRCT • 안드로이드(Tegra) : DXT • 안드로이드(Adreno) : ATC • 안드로이드(공통) : ETC1
  • 15. 15 텍스쳐 • 텍스쳐 사이즈는 무조건 2의 제곱이어야 한다. • POT(Power of Two) • POT가 아닌경우 POT 텍스쳐로 변환되어 로딩 된다. • 900 x 900 -> 실제로는 1024 X 1024로 변환 • 텍스쳐 아틀라스를 활용 하라. • 텍스쳐 아틀라스로 최대한 묶음 • UI 만이 아니라, 같은 재질의 오브젝트들을 묶음
  • 16. 16 텍스쳐 • 압축된 텍스쳐와 밉맵을 사용하자. (대역폭 최적화) • 32bit가 아닌 16bit 텍스쳐 사용도 상황에 맞게 고려
  • 17. 텍스쳐 메모리 사용량 프로파일링 17 //로그를 서버에 남기면 프로파일링에 도움이 된다
  • 18. 18 Mesh Import시에 언제나 “Optimize Mesh” 옵션 사용 • 변환 전/변환 후 버텍스 캐쉬를 최적화 해준다 언제나 Optimize Mesh Data 옵션을 사용한다 • Player Setting > Other Settings • 사용하지 않는 버텍스 정보들을 줄여 준다(tangents, Normal, color, etc…)
  • 19. 19 오디오 • 모바일에서 스테레오는 의미 없다. • 모두 92kb, 모노로 인코딩 • 사운드 파일을 임포트하면 디폴트로 3D 사운드로 설정 • 2D 사운드로 변경 • 압축 사운드(mp3, ogg), 비압축 사운드(wav) 구별 • 비압축 사운드 : 순간적인 효과음, 이펙트 등 • 압축 사운드 : 배경 음악
  • 20. 20 폰트 리소스 최적화 NDC2013 - ‘갤럭시S1’ 에서 풀프레임 퍼즐주주 만들기
  • 21. 21
  • 22. 22 메모리 용량을 ¼로 절약
  • 23. 23 NDC2013 - ‘갤럭시S1’ 에서 풀프레임 퍼즐주주 만들기
  • 24. 24 리소스 기타 ResourceLoadAsync() 함수는 엄청 느리다 • 게임 레벨 로드시에 사용했을 경우, 일반 함수에 비해 수십배나 느렸다.
  • 25. 그래픽스 최적화
  • 26. 26 Culling 프러스텀(Frustum) 컬링 • 각 Layer 별로 컬링 거리를 설정하는 것이 가능하다!! • 멀리 보이는 중요한 오브젝트(ex. 성, 산맥...)은 거리를 멀게 설정하고, 중요도가 낮은 풀이나 나무등은 컬링 거리를 짧게 설정
  • 27. 27 Culling 오클루젼(Occlusion) 컬링 • Window->Occlusion Culling 메뉴에서 설정 가능
  • 28. 28 오브젝트 통합(Combine) • 드로우콜은 오즈젝트에 설정된 재질의 셰이더 패스당 하나씩 일어난다. • 렌더러에 사용된 재질의 수만큼 드로우 콜이 발생 Combine (통합) • 성질이 동일한 오브젝트들은 하나의 메쉬와 재질을 사용하도록 통합 • Script 패키지 – CombineChildren 컴포넌트 제공 • 하위 오브젝트를 모두 하나로 통합 • 통합하는 경우 텍스쳐는 하나로 합쳐서, Texture Atlas를 사용해야 된다.
  • 29. 29 Batch Static Batch • Edit > Project Setting > Player 에서 설정 • 움직이지 않는 오브젝트들은 static으로 설정해서, 배칭이 되게 한다. • Static으로 설정된 게임 오브젝트에서 동일한 재질을 사용 할 경우, 자동으로 통합 • 통합되는 오브젝트를 모두 하나의 커다란 메쉬로 만들어서 따로 저장(메모리 사용량 증가) Dynamic Batch • 움직이는 물체를 대상으로 동일한 재질을 사용하는 경우, 자동으로 통합 • 동적 배칭은 계산량이 많으므로, 정점이 900개 미만인 오브젝트만 대상이 됨
  • 30. 30 라이팅 라이트 맵을 사용하자 • 고정된 라이트와 오브젝트의 경우(배경) 라이트 맵을 최대한 활용 한다. • 아주 빠르게 실행된다 (Per-Pixel Light 보다 2~3배) • 더 좋은 결과를 얻을 수 있는 GI와 Light Mapper를 사용할 수 있다. 라이트 렌더 모드 • 라이팅 별로 Render Mode : Important / Not Important 설정 가능 • 게임에서 중요한 동적 라이팅만 Important로 설정 (Per-Pixel Light) • 그렇지 않은 라이트들은 Not Important로 설정
  • 31. 31 Overdraw 화면의 한 픽셀에 두번 이상 그리게 되는 경우 (Fill rate) • DP Call의 문제만큼이나, Overdraw로 인한 프레임 저하도 중요한 문제 • 특히 2D 게임에서는 DP Call 보다 더욱 큰 문제가 된다. 기본적으로 앞에서 뒤로 그린다 • Depth testing으로 인해서 오버드로우를 방지 한다 • 하지만 알파 블렌딩이 있는 오브젝트의 경우에는 알파 소팅 문제가 발생 반투명 오브젝트의 개수의 제한을 건다 • 반투명 오브젝트는 뒤에서부터 앞으로 그려야 한다. -> Overdraw 증가 • 반투명 오브젝의 지나친 사용에는 주의해야 한다
  • 32. 32 Overdraw (2) 유티니의 Render Mode를 통한 overdraw 확인 가능
  • 33. 33 유니티 셰이더 기본 셰이더는 모바일용 셰이더 사용 • 기본 셰이더를 사용할 경우, 모바일용 셰이더를 사용. • Mobile > VertexLit은 가장 빠른 셰이더 복잡한 수학 연산 • pow, exp, log, cos, sin, tan 같은 수학 함수들은 고비용이다. • 픽셀별 그런 연산을 하나 이상 사용하지 않는 것이 좋다. • 텍스쳐 룩업테이블을 만들어서 사용하는 방법도 좋다. • 알파 테스트 연산(discard)는 느리다. • 기본적인 연산보다는 최적화 시키고 간략화시킨 공식들을 찾아서 사용할 수 있다
  • 34. 34 유니티 셰이더 (2) 실수 연산 • float : 32bit - 버텍스 변환에 사용. 아주 느린 성능 (픽셸 셰이더에서 사용은 피함) • Half : 16bit – 텍스쳐 uv에 적합. 대략 2배 빠름 • fixed : 10bit – 컬러, 라이트 계산과 같은 고성능 연산에 적합. 대략 4배 빠름 라이트 맵을 사용하자 • 고정된 라이트와 오브젝트의 경우(배경) 라이트 맵을 최대한 활용 한다. • 아주 빠르게 실행된다 (Per-Pixel Light 보다 2~3배) • 더 좋은 결과를 얻을 수 있는 GI와 Light Mapper를 사용할 수 있다.
  • 35. 물리엔진 최적화
  • 36. 36 Fixed Update 주기 조절 • FixedUpdate()는 Update와 별도로 주기적으로 불리며, 주로 물리 엔진 처리 • 디폴트는 0.02초. 즉 1초에 50번이나 호출됨 • TimeManager에서 수정 가능 • 게임에 따라, 0.2초 정도(혹은 이상)로 수정해도 문제 없음
  • 37. 37 물리 엔진 설정 Static Object • 움직이지 않는 배경 물체는, static으로 설정 충돌체의 이동 • 리지드 바디가 없는 고정 충돌체를 움직이면, CPU 부하 발생 • 이럴 경우 리지드 바디를 추가하고, IsKinematic 옵션 사용 Maximum Allowed timestep 조정 • 시스템에 부하가 걸려, 지정되 시간보다 오래 걸릴 경우, 물리 계산을 건너뛰는 설정
  • 38. 38 물리 엔진 설정 (2) Solver Iteration Count 조정 • 물리 관련 계산을 얼마나 정교하게 할지를 지정. (높을수록 정교) • Edit > Project Setting > Physics Sleep 조절 • 리지드 바디의 속력이 설정된 값보다 작을 경우, 휴면상태에 들어감 • Physics.Sleep() 함수를 이용하면, 강제 휴면 상태를 만들 수 있음
  • 39. 39 물리 엔진 스크립트 래그돌 사용 최소화 • 랙돌은 물리 시뮬레이션 루프의 영역이 아니기 때문에, 꼭 필요할때만 활성화 한다 태그 대신 레이어 • 물리 처리에서 레이어가 훨씬 유리. 성능과 메모리에서 장점을 가진다 메쉬 콜라이더는 절대 사용하지 않는다. 레이캐스트와 Sphere Check 같은 충돌 감지 요소를 최소화
  • 40. 40 Tilemap Collision Mesh 2D 게임에서 타일맵의 Collison Mesh를 최적화하라 • Tilemap을 디폴트로 사용해서, 각 타일별로 충돌 메쉬가 있는 경우, 물리 부하가 커진다 • 연결된 Tilemap을 하나의 Collision Mesh로 물리 연산을 최적화 하라
  • 41. 참고 자료 • Unity 3D 최적화 http://www.uzoo.in/?mid=master_codesnippet&order_type=desc&document_srl=539&listStyle=viewer • 유니티3D 게임 리소스 최적화? 3가지만 기억해라 HTTP://TECHHOLIC.CO.KR/ARCHIVES/17907 • Optimizing Graphics Performance on Unity : 유니티에서 그래픽 성능 최적화 하기 http://smilemugi.net/wordpress/archives/227 • "14일만에 0에서 60프레임 만들기" Unity3D를 사용하여 우리의 게임을 최적화시키면서 배웠던 것들 • 유니티가 당신에게 알려주지 않는 것들 http://www.slideshare.net/MrDustinLee/ss-27739454?related=3 • NDC2013 - ‘갤럭시S1’ 에서 풀프레임 퍼즐주주 만들기 http://www.slideshare.net/olambdao/unity3d-2d-20130424?related=1 • Unite 2013 optimizing unity games for mobile platforms http://www.slideshare.net/hnam7/unite-2013-optimizing-unity-games-for-mobile-platforms-37933478 • Optimizing unity games (Google IO 2014) • Unity Internals: Memory and Performance



저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by blueasa
TAG 최적화

댓글을 달아 주세요

유니티 – 그래픽 성능 최적화

원문 - http://docs.unity3d.com/Documentation/Manual/OptimizingGraphicsPerformance.html

자주 보는 문서라 한글로 보는 게 편할 것 같아 번역해서 적어놓습니다. 오역이 있을 수 있습니다. T_T

 

2012년 7월 29일 자 버전 번역본

 

그래픽 성능 최적화

대다수 게임의 성공에 있어 좋은 성능은 가장 중요한 부분입니다. 아래 내용은 여러분 게임의 그래픽 렌더링 속도를 최대화하는 몇 가지 요약 지침입니다.

 

그래픽 비용이 어디서 많이 드는가

여러분 게임의 시각적 부분은 컴퓨터의 두 장치 GPU나 CPU에서 주로 비용을 사용합니다. 무슨 최적화든지 간에 첫 번째로 볼 것은 성능 문제가 있는 곳을 찾는 것입니다. 왜 그러냐 하면 GPU와 CPU의 최적화 방법은 상당한 차이(도 있고 반대로 GPU일 때도 그렇지만, CPU를 최적화하는 동안 GPU를 좀 더 일하게 하는 일이 상당히 흔하기)가 있기 때문입니다.

 

발생 빈도가 높은 병목과 그를 확인하는 방법

 

- GPU는 보통 fillrate나 메모리 대역폭에 의해 제약을 받습니다.

- 게임을 저해상도로 돌리면 더 빨라진다고요? 그렇다면, 아마도 GPU상의 fillrate에 의해 제약받고 있을 겁니다.

- CPU는 보통 draw calls라고 부르는 렌더링을 해야 하는 물체 수에 의해 제약을 받습니다.

렌더링 통계 창에 나오는 draw calls를 확인해 보세요. 이게 몇천(PC에서)이나 몇백(모바일에서)을 넘는다면 물체 수를 최적화해야 합니다.

 

물론, 이건 보통 이렇다는 겁니다. 병목은 다른 곳에 있을 수도 있습니다. 발생 빈도가 낮은 병목은 다음과 같습니다.

 

- GPU와 CPU 둘 다 확인해도 렌더링이 문제가 아닌 경우! 예를 들어, 직접 작성한 스크립트나 물리가 실제 문제를 일으키고 있을 수 있습니다. 분석기를 사용해 문제를 찾아보세요. (하지만 난 베이직이지.)

- GPU가 처리할 정점이 너무 많은 경우. 얼마나 많은 수의 정점이 “괜찮은지”는 GPU와 정점 셰이더의 복잡도에 의해 좌우됩니다. 모바일에서는 “십만 개를 넘지 않고”, PC에서는 “몇백만 개를 넘지 않는 것”이 적당합니다.

- CPU에서 처리하는 정점이 처리하기엔 너무 많은 경우. 골격 메시(skinned meshes), 피복 시뮬레이션, 입자(particle) 등이 될 수 있습니다.

 

CPU 최적화 – draw call 수

어떤 물체를 화면에 표현하기 위해 어느 조명이 물체에 영향을 주는지 알아내기, 셰이더와 셰이더 매개변수를 설정하기, 그리기 입력을 그래픽 드라이버로 보내기, 그래픽 카드로 보낼 명령을 준비하기 같은 몇 가지 작업을 CPU가 합니다. “물체당 한 번씩”하는 앞에 나온 모든 CPU 작업은 그렇게 많이 저렴하지 않기 때문에 보이는 물체가 많다면 비용이 증가할 것입니다.

 

예를 들어서, 삼각형(보통 국내에서는 폴리곤이라고 부르는데 원문 그대로 번역합니다.) 천 개를 가지고 있다고 합시다. 삼각형 하나로 구성된 천 개의 개별적인 메시가 있을 때보다 모든 삼각형이 한 개의 메시 안에(로 구성되어) 있을 때가 비용이 더 많이 많이 저렴할 겁니다. GPU상에서 두 경우의 비용은 아주 비슷하지만, CPU에서 천 개의 물체를 표현 완료하는 것은(한 개에 비해) 비용이 상당할 것입니다.

 

CPU가 일을 더 적게 하도록 보이는 물체 수를 줄이는 것이 좋습니다.

 

- 근접해 있는 물체들을 유니티의 draw call 일괄처리를 사용하거나 직접 합칩니다.

- 개별 텍스처를 큰 텍스처 지도(texture atlas)에 넣거나 다른 방법으로 물체의 재질(materials)을 적게 사용합니다.

- 물체를 여러 번 렌더하게 하는 기능을 적게 사용합니다. (반사, 그림자, 픽셀당 조명 등등, 아래 참조.)

 

각각의 메시가 최소 몇백 개의 삼각형을 갖도록 물체들을 함께 합치고 합친 메시당 한 개의 재질만 사용합니다. 재질을 공유하지 않는 두 물체를 합치는 것은 성능 향상이 전혀 없다는 것을 이해하는 것이 중요합니다. 두 메시가 같은 텍스처를 사용하지 않는 것이 다중 재질을 사용하는 가장 흔한 이유이기 때문에 CPU를 최적화하려면, 반드시 합치려는 물체가 같은 텍스처를 사용하게 해야 합니다.

 

하지만 전방 렌더링 패스에서 픽셀 조명을 많이 사용하면 아래에 설명한 대로 물체 결합을 할 수 없는 상황이 생길 수 있습니다.

 

GPU: 모델의 지오메트리 최적화

모델의 지오메트리(기하 구조, 외형, 외견, 폴리곤)를 최적화하려면 기본적으로 두 가지 해야 할 것이 있습니다.

 

- 필요하지 않은 경우 삼각형을 더 사용하지 말 것

- UV 매핑 이음새(재봉선, uv가 분리되어 버텍스를 공유하지 못하기 때문에 처리해야 할 데이터가 늘어납니다.)와 심한 모서리(겹쳐있는 정점) 수를 가능한 한 적게 유지하도록 노력할 것

 

그래픽 하드웨어에서 처리하는 실제 정점 수는 삼차원 모델링 프로그램에서 알려주는 수와 대개 같지 않다는 것을 주목하세요. 모델링 프로그램은 보통 기하학적 정점 수, 즉 모델을 구성하는 꼭짓점 수를 표시합니다. 하지만 그래픽 카드는 렌더링을 위해 어떤 기하학적 정점을 두 개나 그 이상의 실제 정점으로 분리해야 할 때가 있습니다. 정점이 다중 법선, 다중 UV 좌표나 다중 정점 색상을 가지고 있다면 반드시 분리해야 합니다. 그 결과 유니티에서 표시되는 정점 수는 언제나 삼차원 프로그램이 알려주는 것보다 많습니다.

 

모델의 지오메트리 양은 GPU와 관련이 가장 많지만, CPU에서 모델을 처리하는 메시 스키닝과 같은 유니티의 몇몇 기능도 관련이 있습니다.

 

조명 성능

전혀 계산하지 않는 조명이 항상 가장 빠릅니다! (당연한 거 아닌가?!) 프레임마다 계산하는 대신 정적 조명을 딱 한 번 “굽는” 라이트맵을 사용해보세요. 유니티에서는 단순히 장면(scene) 안에 조명을 설치하는 것보다 라이트맵이 적용된 주변 환경(지형, 배경 등)을 생성하는 과정이 살짝 할 게 더 많지만,

 

- 이렇게 하면 더 많이 빨라집니다. (픽셀당 조명 2개의 경우 2~3배 정도)

- 전역 조명을 구울 수 있고 라이트매퍼가 결과물을 부드럽게 할 수 있으므로 시각적으로 더 많이 좋아집니다.

 

대개 곳곳에 조명을 더 추가하는 대신 셰이더와 콘텐츠로 간단하게 속임수가 가능합니다. 예를 들어 “뒷조명(rim lighting, 역광)” 효과를 얻기 위해 카메라 쪽을 곧게 비추는 조명을 추가하는 대신 전용 “뒷조명” 계산을 셰이더를 안에 직접 추가하는 방법을 고려해보세요.

 

전방 렌더링의 조명

픽셀당 동적 조명은 영향을 받는 모든 픽셀에 큰(significant) 렌더링 부담(overhead)를 추가로 주고 물체들을 다중 패스 렌더링을 하게 합니다. 모바일과 최저사양 PC의 GPU처럼 사양이 낮은 기기에서는 한 개의 물체에 하나 보다 많은 픽셀 조명이 비추는 것을 피하고, 정적 물체를 밝히기 위해 조명 계산을 모든 프레임마다 하는 대신 라이트맵을 사용해보세요. 정점당 동적 조명은 정점 변형 시 큰 비용이 추가될 수 있습니다. 어떤 물체든지 여러 조명이 비추는 상황을 피하려고 노력하세요.

 

만약 픽셀 조명을 사용하고 있다면 각각의 메시는 픽셀 조명이 메시를 비추는 만큼 여러 번 렌더링 될 것입니다. 아주 멀리 떨어진 두 메시를 합쳤다면 합쳐진 물체의 영향을 받는 크기는 증가할 것입니다. 이 합쳐진 물체를 비추는 모든 픽셀 조명은 렌더링 중 (조명 빛이 닿지 않는 멀리 떨어진 부분도) 계산에 들어가기 때문에 렌더링해야 할 렌더링 패스 수가 증가할 것입니다. 일반적으로 합쳐진 물체를 반드시 렌더해야 되는 패스 수는 (합치게 될) 각 개별적 물체의 패스 수의 합이기 때문에 물체를 합쳐서 추가되는 것은 없습니다. 이런 이유로 다른 위치에 있는 픽셀 조명의 영향을 받게 될 너무 멀리 떨어진 메시들을 합쳐서는 안 됩니다.

 

렌더링 중, 유니티는 메시를 둘러쌓고 있는 모든 조명을 찾고 이중 가장 영향을 주는 조명을 계산합니다. 얼마나 많은 조명이 픽셀 조명과 정점 조명이 될 건지 조정할 때 품질 설정을 사용합니다. 각각의 조명은 메시로부터 얼마나 멀리 떨어져 있는지에 기반을 둔 중요도와 빛이 얼마나 강할지를 계산합니다. 그뿐만 아니라 몇몇 조명은 순전히 게임 맥락(개발자의 의도)에 따라 다른 조명보다 더욱 중요하므로 모든 조명은 Important또는 Not Important를 설정할 수 있는 표현 방법(Render Mode) 설정을 가지고 있고 조명이 Not Important로 설정된 조명은 일반적으로 낮은 간접 처리를 하게(갖게) 됩니다.

 

예를 들어, 플레이어의 자동차가 전조등을 켜고 어둠 속에서 달리고 있는 자동차 게임을 생각해보세요. 전조등은 게임에서 시각적으로 가장 중요한 조명이기 때문에 표현 방법을 아마도 Important로 설정할 것입니다. 반면에 게임의 (다른 자동차의 후면등 같은) 다른 조명들은 덜 중요하고 픽셀 조명의 시각 효과 향상이 없을 것입니다. 이런 조명들이 작은 이익을 줄 수 있는 곳에서 렌더링 비용이 버려지는 것을 피하고자 렌더 모드를 안전하게 Not Important로 설정할 수 있습니다.

 

픽셀당 조명 최적화는 CPU와 GPU 양쪽 모두 자원을 절약하게 합니다. CPU는 draw call이 적게끔 하고 GPU는 처리할 정점이 적게끔 하며 이 모든 추가적인 렌더링의 레스터라이즈될 픽셀을 적게 만듭니다.

 

GPU: 텍스처 압축과 밉 맵

압축된 텍스처를 사용하면 텍스처의 크기를 줄일 수(그 결과 로딩 시간이 더 짧아지고 메모리가 차지하는 공간이 더욱 작아집니다.) 있고 렌더링 성능을 극적으로 올릴 수 있습니다. 압축된 텍스처는 압축되지 않은 32bit RGBA 텍스처가 필요로 하는 메모리 대역폭의 일부만 사용됩니다.

 

텍스처 밉 맵 사용하기

(mipmaps 또는 MIP maps, 앞에 있는 MIP는 라틴어 multum in parvo에서 온 말로 작으면서 효율적(much in little)이라는 뜻이 있다네요…그렇다고요 ;ㅁ;)

경험상 말하자면, 삼차원 장면에서 사용되는 텍스처는 밉 맵 생성하기(Generate Mip Maps)를 항상 켜두세요. 같은 식으로 텍스처 압축도 적용하면 GPU가 렌더링 중일 때 전송되는 텍스처 데이터양을 제한하는 데 도움이 됩니다. 밉 맵이 적용된 텍스처는 GPU가 작은 삼각형에 저해상도 텍스처에 사용할 수 있게 합니다.

 

위 내용의 한 가지 예외는 텍셀(텍스처 픽셀)이 2D 게임상이나 UI 요소로써 화면 픽셀에 1:1로 적용되어 렌더할 때입니다.

 

LOD와 레이더당 배제 거리

몇몇 게임에서는 CPU와 GPU가 사용되는 것을 줄이기 위해 큰 것보다 작은 물체를 더욱 공격적으로 배제(cull)하는 것이 적절할 수 있습니다. 예를 들어서 먼 거리에서 큰 건물이 아직 보일 때 작은 돌과 파편은 (화면 픽셀보다 작아져서) 보이지 않을 수 있습니다.

 

위 내용은 Level Of Detail 기능(하지만 난 베이직이지.)을 사용하거나 카메라의 레이어당 배제 거리를 직접 설정해서 적용 할 수 있습니다. 작은 물체를 개별 레이어에 넣고 Camera.layerCullDistances 스크립트 함수를 사용해서 레이어당 배제 거리를 설정할 수 있습니다.

 

실시간 그림자

실시간 그림자는 멋지긴 하지만 CPU에는 draw calls를 추가하고 GPU에는 추가 처리를 하게 하여 성능에 상당한 비용이 들게 합니다. 더 상세한 내용은 그림자 항목을 보세요.

 

GPU: 높은 성능의 셰이더 작성법

최고사양 PC의 GPU와 최저사양 모바일 GPU는 성능이 말 그대로 수백 배 차이가 날 수 있습니다. 단일 플랫폼에서도 마찬가지입니다. PC 상에서 빠른 GPU는 느린 통합형 GPU보다 수십 배 빠르고 모바일 플랫폼에서도 똑같이 GPU 간에 큰 차이를 볼 수 있습니다.

 

그러므로 모바일 플랫폼과 최저사양 PC의 GPU 성능은 여러분이 개발하고 있는 장비보다 매우 낮을 거란 것을 기억해야 합니다. 보통 셰이더는 좋은 성능을 얻기 위해 계산과 텍스처 읽기를 줄여 손수 최적화해야 합니다. 예를 들어 몇몇 내장(built-in) 유니티 셰이더는 더 빠른(지만 어떤 제약을 받거나 비슷한 효과를 사용하기 때문에 더 빠른) “모바일” 대용 셰이더를 갖고 있습니다.

 

아래 내용은 모바일과 최저사양 PC의 그래픽 카드에 가장 중요한 몇 가지 지침입니다.

 

복잡한 수학적 연산

수학의 (pow, exp, log, cos, sin, tan, 등등 같은) 초월함수는 비용이 상당하기 때문에 경험상 볼 때 저런 픽셀당 연산은 하나를 넘으면 안 됩니다. 하나를 넘었다면 대용으로 룩업 텍스처를 사용하는 것을 고려해 보세요.

 

어떻게 하든 간에 직접 normalizedotinversesqrt 연산을 작성하려는 것은 권장하지 않습니다. 내장 함수를 사용하면 드라이버가 더 나은 코드를 만들어 낼 것입니다.

 

알파를 테스트하는(버리는) 연산은 프라그먼트(픽셀) 셰이더를 느리게 만든다는 것을 기억하세요.

 

부동 소수점 연산

직접 셰이더를 작성할 경우 부동 소수점 변수의 정밀도(precision)를 반드시 적어야 합니다. 최상의 성능을 얻기 위해 가능한 낮은 정밀도의 부동 소수점 형식을 고르는 것은 중요합니다. 연산의 정밀도는 대게 데스크톱 GPU에서는 완전히 무시되지만, 대부분의 모바일 GPU 상에서의 성능에 있어서는 중요합니다.

 

셰이더가 Cg/HLSL로 작성되어 있다면 정밀도는 다음과 같이 명시되어 있습니다.

 

float : 32-bit를 전부 사용하는 부동 소수점 형식으로 정점 변형에 적합하지만, 성능에 있어 가장 느립니다.

half : 16-bit로 줄인 부동 소수점 형식으로 텍스처 UV 좌표에 적합하고 대략 float보다 두 배정도 빠릅니다.

fixed : 10-bit 부동 소수점 형식으로 색상, 조명 계산과 다른 고비용 연산에 적합하며 대략 float보다 네 배정도 빠릅니다.

 

셰이더가 GLSL ES로 작성되어 있다면 부동 소수점 정밀도는 각각 highpmediumplowp로 명시되어 있습니다.

 

셰이더 성능에 대해 더 자세한 내용은 셰이더 성능 항목을 읽어보세요.

 

게임을 더욱 빠르게 만들기 위한 요약 확인 항목

- PC 게임을 만들고 있다면 목표로 하고 있는 GPU에 맞춰 정점 수를 프레임당 2십만에서 3백만 아래로 유지하세요.

- 내장 셰이더를 사용하고 있다면 모바일이나 Unlit 범주에 있는 것을 고르세요. 이 셰이더들은 기존 복잡한 셰이더를 간략화하고 비슷한 효과를 내게 만든 형태로써 모바일이 아닌 플랫폼에서도 잘 동작합니다.

- 장면 하나당 재질 종류 수를 적게 유지하세요. 다른 물체끼리 재질을 가능한 한 많이 공유하세요.

- 움직이지 않는 물체에 Static 속성을 적용해서 Static batching 같은 내부 최적화를 할 수 있게 하세요. (하지만 난 베이직이지.)

- 필요하지 않다면 픽셀 조명을 사용하지 말고 지오메트리에 영향을 주는 픽셀 조명(될 수 있으면 방향 조명)을 하나만 있게끔 하세요.

- 필요하지 않다면 동적 조명을 사용하지 말고 대신 라이트맵을 사용하세요.

- 가능하다면 압축된 텍스처 형식을 사용하세요. 사용할 수 없다면 32bit보다 16bit를 주로 사용해보세요.

- 필요하지 않다면 안개를 사용하지 마세요.

차폐 배제(Occlusion Culling)의 이점을 알고 차폐가 많은 복잡한(물체가 많은) 정적 장면의 경우 보이는 지오메트리와 draw-calls 양을 낮추기 위해 차폐 배제를 사용하세요. 차폐 배제로 인한 이점에 맞춰 레벨을 제작하세요. (하지만 난 베이직이지.)

“가짜” 원거리 지오메트리를 표현하기 위해 스카이박스를 사용하세요.

- 여러 텍스처를 섞기 위해 다중 패스 방식 대신 픽셀 셰이더나 텍스처 합성기를 사용하세요.

- 직접 셰이더를 작성하고 있다면 가능한 항상 가장 정밀도가 낮은 부동 소수점 형식을 사용하세요.

fixed / lowp : 색상, 조명 정보와 법선용.

half / mediump : 텍스처 UV 좌표용.

float / highp : 픽셀 셰이더에서는 피할 것. 정점 셰이더에서 위치 계산용으로 사용하는 것이 좋습니다.

- 픽셀 셰이더에서 powsincos 등등과 같은 복잡한 수학 연산 사용을 최소화하세요.

- 프래그먼트(화면 픽셀)당 텍스처를 적게 사용하세요.



출처 : http://fetchinist.com/blogs/?p=712

저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by blueasa
TAG 최적화

댓글을 달아 주세요

출처 - http://docs.unity3d.com/Documentation/Manual/iphone-playerSizeOptimization.html -


iOS 플레이어 빌드 사이즈 최적화 하기.

빌드 사이즈를 줄이는 방법은 크게 두 가지가 있습니다. Xcode에서 Active Build Configuration을 조절하는 방법과 Unity에서 Stripping Level을 조절하는 방법입니다.

 

Relese Mode로 빌드 하기.

Xcode에서 빌드 할 때Active Build Configuration 드롭다운 메뉴에서 Debug 와 Release 옵션을 선택할 수 있습니다. Debug Mode 대신 Release Mode로 빌드를 하면 게임에 따라서 2-3MB 정도의 빌드 크기가 줄어듭니다.

1.jpg

The Active Build Configuration drop-down -

Release Mode로 빌드를 하게 되면 Debug 정보 없이 빌드를 하기 때문에게임이 크래쉬가 발생하거나 다른 문제가 생겼을 때 Stack Trace 등의 정보를Output으로 확인할 수가 없습니다

제작이 완료돼서 배포를 하는 게임의 경우에는 문제가 되지 않겠지만개발 중에는 Debug Mode가 유용합니다.


iOS Stripping Level (Advanced License feature)

Stripping Level 을 조절해서 빌드 크기를 최적화 하는 방법은 아래와 같은 방법으로 진행이 됩니다.

1. Strip assemblies level: 스크립트의 Bytecode를 분석해서해당 클래스와 메소드가 참조가 되는지 여부를 확인하고참조되지 않는 부분은 DLL에서 제거되고, AOT 컴파일 단계에서 제외 됩니다이 최적화 방법은 Main binary의 크기와 수반되는 DLL을 줄이는 방법으로리플렉션을 사용하지 않는 한 안전한 방법입니다.

2. Strip ByteCode level: (Data 폴더에 저장되는)모든 .Net DLLmetadata strip되어 크기가 줄어듭니다모든 코드가 AOT 단계에서 미리 전처리(precompiled)가 되고, main binary에 링크가 되기 때문에 이 방법이 가능하게 됩니다.

3. Use micro mscorlib: 특별한 경우로, mscorlib smaller 버전을 사용하는 방법입니다.

이 라이브러리에서는 일부 컴포넌트가 제외된 버전입니다.

예를 들어, Security, Reflection.Emit, Remoting, non Gregorian calendars 등이 제외됩니다.

또한 내부 컴포넌트간의 상호의존성이 줄어들게 됩니다이 방법은 main binary mscorlib.dll의 크기를 줄이는 방법이지만 일부 System 및 System.Xml 어셈블리 클래스에서는 호환이 되지 않으니 주의를 해야 합니다.

 Level들은 축적이 됩니다다시 말해서, Level 3 최적화 방법은 Level 2  Level 1을 내포하고, Level 2 최적화 방법은 Level 1을 포함합니다.

주의: Micro mscorlib  core 라이브러리의 크기를 많이 줄인 버전입니다. micro mscorlib 를 사용할 때 가장 좋은 방법은 제작중인 어플리케이션에서 사용하지 않는 .Net 클래스와 기능들을 사용하지 않는 것입니다제외할 예로 GUID가 가장 좋은 예시가 될 것 같습니다. GUID를 직접 만들어서 대체 사용이 쉽고직접 제작해서 사용하는 경우 성능 면에서 앱 사이즈를 줄이는 면에서도 모두 좋은 결과를 낼 수 있습니다.


Tips

리플렉션을 사용할 때 Stripping 조절하는 방법

Stripping static code analysis에 많이 의존하는 방법이기 때문에효율적으로 진행이 되지 않는 경우가 종종 발생합니다.

한 예로 리플렉션과 같은 dynamic 기능이 사용되는 경우입니다이런 경우사용하거나 참조해야 하지 않는 클래스 등을 명시 하는 것이 필수적입니다.

유니티는 프로젝트 별로 따로 custom stripping blacklist를 지원합니다. blacklist를 사용하면 link.xml 파일을 생성하고 Assets 폴더에 두어야 합니다. link.xml 파일의 예제를 아래에서 확인할 수 있습니다보존(preservation)되어야 한다고 명시된 클래스들은 stripping에 영향을 받지 않게 됩니다.

<linker>

       <assembly fullname="System.Web.Services">

               <type fullname="System.Web.Services.Protocols.SoapTypeStubInfo" preserve="all"/>

               <type fullname="System.Web.Services.Configuration.WebServicesConfigurationSectionHandler" preserve="all"/>

       </assembly>

 

       <assembly fullname="System">

               <type fullname="System.Net.Configuration.WebRequestModuleHandler" preserve="all"/>

               <type fullname="System.Net.HttpRequestCreator" preserve="all"/>

               <type fullname="System.Net.FileWebRequestCreator" preserve="all"/>

       </assembly>

 </linker>

주의하지만 어떤 클래스가stripping 되어야 하고 어떤 클래스가 사용이 되어야 하는지를 판별하는 방법이 어려울 수 있기 때문에, Xcode 콘솔의 에러 메시지를 확인하고시뮬레이터에서 앱을 실행시켜서 그런 정보들을 확인하는 것이 필요할 수 있습니다.

최적화된 크기의 앱 배포를 위한 체크리스트

1. 에셋 줄이기텍스처에 PVRTC 압축 옵션을 활성화 하고 가능한 범위 내에서 해상도를 줄입니다또한압축되지 않은 사운드 에셋의 수를 줄입니다이 방법에 대한 추가 팁을 아래 링크에서 확인할 수 있습니다. (링크 - http://docs.unity3d.com/Documentation/Manual/ReducingFilesize.html)

2. iOS Stripping Level Use Micro mscorlib로 설정합니다.

3. 스크립트 호출 최적화Level Fast but no exceptions로 설정합니다.

(Script call optimization level)

4. System.dll System.Xml.dll 에 있는 기능들은 사용하지 않습니다

이 라이브러리들은 micro mscorlib에 호환되지 않는 것 들입니다.

5. 불필요한 code dependencies를 제거합니다.

6. API Compatibility Level .Net 2.0 subset으로 설정합니다.

하지만 .Net 2.0 subset은 다른 라이브러리들과의 호화성에 제한적인 부분이 있다는 점을 주의해야 합니다

7. Target platformarm6로 설정합니다. (OpenGL ES 1.1)

8. JS Arrays를 사용하지 않습니다.

9. 구조체를 포함해서 Value 타입과 조합해서 generic container를 사용 하는 것을 자제합니다.



저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by blueasa
TAG ios, 최적화

댓글을 달아 주세요

Unity3D iOS 프로젝트 포팅/최적화 팁

원저 : Itay Keren, Untame Games - 31 Dec 2010


아래의 글은 유니티3D로 만들었던 제 게임 'Rope Racket'을
iOS(iPhone/iPad)로 이식하면서 얻은 팁들에 대해 대략적으로 정리해놓은,
미완성된 리스트입니다.
아래 리스트를 필요하면 그때그때 업데이트할 예정이니,
종종 들러서 확인해보시기 바랍니다.


물리엔진 관련 Physics


유니티3D는 매우 잘 최적화된 PhysX 물리엔진을 사용합니다.
일반적으로 물리에 관련된 모든 기능들은 이 엔진에 의해 자동적으로 처리됩니다.
하지만 iOS에 적용할 경우 고려해 볼 만한 점이 좀 있습니다 :
  • 충돌자Colliders
    불필요한 충돌자들을 없애고, 하나로 묶으세요.  Ex) 적 오브젝트는 한개의 충돌자만 쓰게끔

  • 겹쳐진 충돌자들Overlapping colliders은 매 프레임당 (많으면)3번까지의 반복 처리를 유발할 수 있으므로,
    충돌자가 붙은 오브젝트의 위치를 강제로 배치할 경우 특별히 유념하시기 바랍니다.
    (씬 에디터 상에서의 수동 배치나, transform값 수정에 의한 배치 등 직접 position값을 주는 경우를 말함 - 역주)

  • 좀 더 간단한 형태의 충돌자를 사용
    메쉬mesh 충돌자보다는 입방체cube, 입방체 보다 구체sphere 충돌자가 더 간단합니다.

  • 조인트Joints의 연쇄 반응에 주의
    조인트 사용시, 한쪽 끝의 움직임은 항상 반대쪽 끝까지의 연쇄적인 움직임을 유발합니다.
    서로 연결된 몇개의 조인트를 사용하게 되면 필연적으로 연쇄 반응하는 움직임을 양산하므로,
    사용에 주의하시기 바랍니다.

일반적인 최적화 General Optimization


코드 최적화는 성능을 두드러지게 향상시킬 수 있습니다.
대부분의 경우, 최적화가 안돼서 성능이 떨어지는 주된 원인은 프로그래밍이 (애초에) 부적절하게된 점에서 발생합니다.
  • 몇가지 작업들은 굳이 매 프레임마다 돌릴 필요가 없는 경우가 있습니다.
    Ex) 버튼의 상태 체크, 거리 체크, AI 전략 판단 등등
    이런 동작들 대부분은 초당 10번 정도만 하게끔 바꿔도 아무도 모를 겁니다.

  • Get...()류의 함수들은 개념적으로는 좋습니다만, 성능적으로 (처리 비용이) 비쌀 경우가 있습니다.
    public 변수 정의로 값을 빼놓는 방법은 지저분해보이긴 합니다만, 외부에서 값을 억세스할 경우 더 빠릅니다.

  • transform도 알고 보면 Get...()류의 함수를 쓰고 있습니다.  
    반복해서 사용할 경우 변수값에다 따로 빼놓으세요.

  • 종종 CPU 사용량이 비정상적으로 튀는(CPU spikes) 현상을 방지하려면,
    System.GC.Collect()를 사용, 적절히 정해진 간격으로 가비지 컬렉터를 호출하세요.

  • sin/cos/atan/sqrt등의 수학 함수는 가능한 한 미리 계산된 테이블값을 사용하거나,
    최소한 자주 변하지 않는 값들을 찾아 변수에 담아놓고 불러다 사용하세요.


그래픽 Graphics


iOS에서 사용되는 GPU는 PC에서 사용되는 것과는 많이 다릅니다.
잘 알려진 제약 사항들 몇 가지를 살펴보자면 :
  • 폴리곤 수
    7,000개가 HW 한계라던지(3GS 기준인 듯 - 역주) 같은 글들을 많이 읽어봤습니다만,
    경험적으로는 폴리곤 수가 다른 요인에 비해 특별히 더 중요한 영향을 끼친다고 생각되진 않습니다.
    한 씬에서 10,000개 이하로 유지하기 위해 가능한 한 노력해보세요.

  • 메모리 최적화
    iOS 플랫폼에서 사용할 최대 텍스쳐 사이즈값Maximum Texture Size for iOS지정을,
    디테일을 잃어버리지 않는 한도에서 최대한 줄여보시기 바랍니다.
    (해당 Texture의 Inspector 메뉴 하단에서, 
     각 빌드 플랫폼별로 Override for... 체크박스를 켜고 최대 텍스쳐 사이즈 및 포맷 지정 가능.
     자세한 사항은 http://unity3d.com/support/documentation/Manual/Textures.html
      위 링크에서 Per-Platform Overrides 항목 및 플랫폼별 사용 가능한 텍스쳐 포맷 설명을 참조 - 역주)
    제 경우는 대부분의 오브젝트에서 최대 256x256, 또는 그 이하로도 충분했었습니다.
    또한 이것을 각각의 대상 플랫폼별로도 지정하실 수가 있는데 (위의 역주 참고)
    PC에선 디테일하게, iOS에선 메모리 사용을 줄이는 식으로 지정할 수도 있습니다.

  • iPhone의 GPU상에서 복잡하고 여러번의 패스multi-pass를 거쳐야 하는 셰이더를 쓰면 느려집니다.
    예를 들어 Toon 계열 셰이더라든지, Transparent/Cutout 계열 셰이더는 가능한 한 사용을 피해야 합니다.

  • 라이팅은 (처리 비용이) 매우 비쌉니다.
    일반적으로 조명이 추가될때마다 (영향받는 모든 오브젝트들에 대한) 
    그리기 패스drawing pass 또한 추가가 됩니다.
    밝은 지향성 조명directional light 하나만 사용하세요.  
    다른 조명, 특히 점point 광원이나 스포트라이트를 사용할 땐 주의하시기 바랍니다.
    조명을 추가하는 데엔 두 가지 문제점이 있음을 기억하세요 -
    그 조명을 받는 모든 물체에 대해 렌더링 작업이 늘어난다는 점과,
    그 조명이 켜지는 시점에서  조명 초기화를 위해 몇밀리초를 더 잡아먹게 된다는 점.

  • 정점vertex의 위치/노말/탄젠트 값을 바꾸는 것은 (처리 비용이) 비싸며, 반복적으로 해선 안됩니다.
    반면, UV(텍스쳐 내에서의 픽셀 위치) 값을 바꾸는 것은 빠릅니다.

  • OnGUI()는 매 프레임마다, 추가적으로  매 이벤트마다 호출됩니다.
    게다가, 연속되는 OnGUI() 내에서 같은 리소스, 매터리얼을 반복 사용하더라도
    이를 공유해서 최적화해준다던가 하지 않습니다.
    (예를 들어, 텍스트와 그 그림자를 그리면 그대로 2번의 그리기 호출2 calls이 발생)
    그러니 가능한 한 사용을 피하세요.


그리기 호출Draw Calls


그리기 호출Draw Call 은 (카메라를 통해 한 프레임의 화면을 만들어내는 동안)
하나의 셰이더 패스, 하나의 매터리얼로 오브젝트를 한 번 그리는 작업의 단위를 말합니다.
(같은 셰이더/같은 매터리얼을 갖는 여러 개의 오브젝트라도 한번의 draw call 작업에서 그릴 수 있음 - 역주)

iPad같은 하이엔드급 모바일 장치에서조차, 이 그리기 호출Draw Call 수를 줄이는 일이
제 경험상 최적화에서 가장 중요한 요소라고 생각이 됩니다. 
확실히 모바일 장비는 비-모바일 장비non-mobile devices에 비해 
CPU와 GPU간의 파이프라인이 그리 효율적이지가 않습니다.
  • 동적 배칭Dynamic Batching 기능이 켜져있는가 확인하세요
    (File 메뉴 > Build Settings... 메뉴 > Player Settings 버튼 >
     Inspector창에서 Other Settings 항목 > Rendering 항목 밑에 Dynamic Batching 체크박스 선택.
     자세한 설명은 http://unity3d.com/support/documentation/Manual/iphone-DrawCall-Batching.html
     위 링크에서 Dynamic Batching 부분을 참조 - 역주)

  • 이것이 켜져있으면, 게임 엔진은 모든 오브젝트를 공유된 매터리얼 단위로 묶습니다.
    이 경우, (스크립트에서 건드릴 땐) material 멤버 대신 sharedMaterial 멤버를 사용하도록 해야 합니다.

  • 나아가, 배치 처리될 모든 오브젝트all batched object는 같은 셰이더 큐 패스queue pass를 공유해야 합니다.
    각각의 카메라는 여러 개의 그리기 패스drawing pass를 가지고 있습니다.
    몇몇 매터리얼이 같은 그리기 패스를 공유하고 있다 하더라도,
    같은 매터리얼을 사용하는 모든 오브젝트가 한번에 배치 처리된다는 보장은 없습니다.
    그러므로, 매터리얼의 셰이더 패스 순서pass order를 직접 지정해줌으로써
    그 매터리얼이 특정 그리기 패스 안에서 동작하도록 강제할 수 있습니다.
    예를 들자면, 셰이더 명령어 Tags {"Queue" = "Transparent-1" } 을 사용하면
    해당 셰이더가 Transparent pass 직후(*주1*)에 동작하도록 강제할 수 있습니다.
    이렇게 Transparent-1로 셋팅하게 되면, 이 새로운 매터리얼은 기존의 Transparent 셰이더와 분리되며,
    이 새로운 셰이더를 사용하는 모든 오브젝트를 묶어 한 패스 안에서 배치batch 처리하게 됩니다.
    주 : 이 방법은 깊이값 소팅이나 Z값 관련 문제를 해결하는데도 도움이 됩니다.
    (이 항목의 내용은 제가 셰이더를 공부 안해서 제대로 번역이 되었는지 확신이 없네요 - 역주)

  • 가장 중요한 사항: 가능한 한 많은 오브젝트를 한 매터리얼(한 장의 텍스쳐)로 묶어야 합니다.
    예를 들어, 한장의 아이콘 맵icon atlas을 만들고 필요한 상황에 따라 UV위치값을 바꿔가며 쓰는 방법이 있습니다.

  • 대안책: 폰트를 하나 읽어들이고 이 텍스쳐를 export합니다.  
    찍고싶은 이미지를 조각내서 이 export된 폰트 텍스쳐 위치에 갖다 붙입니다.
    그리고, Text Mesh를 사용해서 이것들을 가져다 적절한 아이콘으로 사용합니다.


그 외의 팁들More Tips

최적화와는 관계없지만 몇가지 편리한 팁들이 더 있습니다 :
  • OnGUI()나 GUITexture를 사용하는 대신, 별도의 카메라로 모든 GUI를 그려주기

    • GUI를 위한 별도의 카메라를 만듭니다
      평행isometric 카메라로 지정하고, 별도로 만든 GUI 오브젝트 레이어 이외의 모든 다른 레이어를 끄고,
        (메인 카메라보다 나중에 찍도록) Depth값을 1로 주고, Clear flag를 Don't Clear로 맞추세요.

    • 텍스트를 찍기 위해선 Text Mesh 와 Mesh Renderer를 사용합니다.
      만들어진 GUI용 GameObject들은 별도로 만든 GUI 오브젝트 레이어 쪽으로 넣어줍니다.
      이렇게 하면 유니티에서 현지화된 텍스트를 사용할때 매우 빠르고 융통성있게끔 만들 수 있습니다.
      이런 덱스트는 변경 가능하고, 회전, 확대/축소할 수도 있으며 
      기본 폰트대신 사용자 정의된 매터리얼로 대치할수도 있습니다.

    • 비슷한 형태로, 로고나 이미지는 박스를 이용해 찍을 수 있습니다.
      평행isometric 카메라 상에서 상각 폴리곤을 찍는 처리비용은 거의 미미하며, 
      불필요한 옆면 등은 알아서 무시됩니다.
      투명값을 가진 이미지를 사용할때는, 뒷면이 없어지게끔 컬링이 적용된 셰이더를 사용하거나
      박스 자체를 카메라에 아주 가깝게 가져와서, 박스 앞면은 카메라 평면 뒤로 보내고 박스 뒷면만 보이게 합니다.

  • (투명값을 갖는 텍스쳐의 경우) 텍스쳐 테두리의 흰색 띠가 보일 경우는
    알파값 0를 가진 픽셀 색이 주변으로 일부 번지기 때문에 발생합니다.
    이 경우, 테두리의 주변 RGB값들이 영향을 미치고 있는 것인데, 포토샵에서 이를 수정하려면 :
    • 알파값 0 인 배경 픽셀들을 전부 선택한다
    • 이미지 테두리를 따라 원하는 경계선 색을 다 칠해준다 (이미지 안쪽은 선택 영역이 보호해준다)
    • Layar Mask를 만들어, 방금 칠해준 선택 영역을 hide 시킨다

결과적으로 알파값이 0인 픽셀들도 원하는 테두리 색을 가지게 되어 보기 좋게 섞여진다.

More tips to follow soon.

Enjoy,
— Itay

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

*주1*
원문에는 
will make sure that this shader is drawn right after Transparent pass. 라고 되어 있어서
원 저자의 의도대로 '
Transparent pass 직후' 라고 번역이 되었습니다만...
http://unity3d.com/support/documentation/Components/SL-SubshaderTags.html 을 보시면,
각 렌더 패스마다 내부적으로 1000단위의 숫자가 매겨져 있고, Transparent의 값이 3000이니
저 Transparent-1 패스의 경우는 2999가 되어, 실제론 'Transparent pass 직전'이 맞습니다.

하지만 원문 내용은 Transparent pass 앞이냐 뒤냐가 중요한게 아니고, 저런 식으로 태그를 부여해서
원하는 drawing 작업을 분리된 하나의 drawing pass로 통합하는 방법을 보여주는 것이니
틀린 내용이라 하더라도 원 저자의 문맥대로 일단 번역하고 별도로 각주를 남깁니다.
* 원문에 이렇게 애매모호(?)한 부분이 두어 군데 더 보입니다만, 마찬가지로 패스... *

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

저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by blueasa
TAG 최적화

댓글을 달아 주세요

성능 최적화

Unity3D/Tips / 2012.11.18 16:41

1. Use Static Typing

When using JavaScript the most important optimization is to use static typing instead of dynamic typing. Unity uses a technique called type inference to automatically convert JavaScript constructs to statically typed code without you having to do any work.

var foo = 5;

In the above example foo will automatically be inferred to be an integer value. Thus Unity can apply a lot of compile time optimizations, without costly dynamic name variable lookups etc. This is one of the reasons why Unity's JavaScript is on average around 20 times faster than other JavaScript implementations.

The only problem is that sometimes not everything can be type inferred, thus Unity will fall back to dynamic typing for those variables. By falling back to dynamic typing, writing JavaScript code is simpler. However it also makes the code run slower.

Let's see some examples.

function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}

Here foo will be dynamically typed, thus calling the function DoSomething takes longer than necessary - because the type offoo is unknown, it has to figure out whether it supports DoSomething function, and if it does, invoke that function.

function Start ()
{
var foo : MyScript = GetComponent(MyScript);
foo.DoSomething();
}

Here we're forcing foo to be of specific type. You will get much better performance.

2. Use #pragma strict

Now the problem is of course, that you don't usually notice when you are using dynamic typing. #pragma strict to the rescue! Simply add #pragma strict at the top of a script and Unity will disable dynamic typing in that script, forcing you to use static typing. Wherever a type is not known, Unity will report compile errors. So in this case, foo will produce an error when compiling:

#pragma strict
function Start ()
{
var foo = GetComponent(MyScript);
foo.DoSomething();
}

3. Cache component lookups

Another optimization is caching of components. This optimization unfortunately requires a bit of coding effort and is not always worth it. But if your script is really used a lot and you need to get the last bit of performance out of it, this can be a very good optimization.

Whenever you access a component through GetComponent or an accessor variable, Unity has to find the right component from the game object. This time can easily be saved by caching a reference to the component in a private variable.

Simply turn this:

function Update () {
transform.Translate(0, 0, 5);
}

Into this:

private var myTransform : Transform;
function Awake () {
myTransform = transform;
}

function Update () {
myTransform.Translate(0, 0, 5);
}

The latter code will run a lot faster since Unity doesn't have to find the transform component in the game object each frame. The same applies for scripted components, where you use GetComponent instead of the transform or other shorthand property.

4. Use Builtin arrays

Builtin arrays are fast, very fast, so use them.

While the ArrayList or Array classes are easier to use since you can easily add elements they don't have nearly the same speed. Builtin arrays have a fixed size but most of the time you know the maximum size in advance and can just fill it out later. The best thing about builtin arrays is that they directly embed struct data types in one tightly packed buffer, without any extra type information or other overhead. Thus iterating through is very easy on the cache as everything is linear in memory.

private var positions : Vector3[];
function Awake () {
positions = new Vector3[100];
for (var i=0;i<100;i++)
positions[i] = Vector3.zero;
}

5. Don't call a function if you don't have to

The simplest and best of all optimizations is to perform less work. For example , when an enemy is far away it is most of the time perfectly acceptable to have the enemy fall asleep. That is do nothing until the player comes close. The slow way of handling this situation would be:

function Update ()
{
// Early out if the player is too far away.
if (Vector3.Distance(transform.position, target.position) > 100)
return;
perform real work work...
}

This is not a good idea since Unity has to invoke the update function and you are performing work every frame. A better solution is to disabling the behaviour until the player comes closer. There are 3 ways to do this: 1. Use OnBecameVisible and OnBecameInvisible. These call backs are tied into the rendering system. As soon as any camera can see the object, OnBecameVisible will be called, when no camera sees it anymore OnBecameInvisible will be called. This is useful in some cases, but often for AI it is not useful because enemies would become disabled as soon as you turn the camera away from them.

function OnBecameVisible () {
enabled = true;
}

function OnBecameInvisible ()
{
enabled = false;
}

2. Use triggers. A simple sphere trigger can work wonders though. You get OnTriggerEnter/Exit calls when exiting the sphere of influence you want

function OnTriggerEnter (c : Collider)
{
if (c.CompareTag("Player"))
enabled = true;
}

function OnTriggerExit (c : Collider)
{
if (c.CompareTag("Player"))
enabled = false;
}

3. Use Coroutines. The problem with Update calls is that they happen every frame. Quite possibly checking the distance to the player could be performed only every 5 seconds. This would save a lot of processing power.


출처 : https://sites.google.com/site/cotranslation/munseo-beon-yeog-jalyo/unity3d/seukeulibteu-lepeoleonseu/seongneung-choejeoghwahagi

저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by blueasa
TAG 최적화

댓글을 달아 주세요

아무리 재미있고 뛰어난 그래픽의 게임을 만들어도 너무 고사양의 PC를 요구하거나 고사양의 PC로 돌리고 있음에도 최적화 되지 않은 문제로 유저가 제대로 게임을 즐길수 없다면 시장의 외면을 받기 쉬워지죠.

하지만 어떻게 최적화가 잘 되어있는지 판단하는 것도 문제입니다.

요즘 잘나가는 사양의 PC를 전산팀에 요청해 받은 뒤 게임 클라이언트를 실제로 돌려보며 확인하는 방법도 있겠지만 좀 더 스마트한 방법을 찾아보도록 하겠습니다.(정말 스마트한 방법은 프로그래머가 클라이언트 내부에 메모리 사용량과 Frame을 기록해주도록 한 뒤 Bot이 돌아다니며 데이터를 수집하는 방식을 사용하더군요. -_-b)

아마도 이 방법은 개발팀의 지원이 어려울 경우 Test팀이 사용 할 수 있는 최선이지 않을까 싶습니다;

준비물
Fraps -  Frame 측정 및 기록하는데 꼭 필요합니다.



perfmon - 윈도우 성능 모니터링 도구. XP부터 7까지 모두 윈도우에 기본으로 탑재되어 있습니다.

 
Excel - 최고의 테스트 Tool 이며 친구입니다. 테스트를 한다면 Excel은 꼭 배워둡시다.


Test 진행

a. Fraps 준비

1. Fraps에는 동영상 녹화로 많이 쓰이지만 그 외에 스크린샷 촬영, Frame 기록 기능이 붙어 있습니다. Test 시에 사용되는 것은 Frame 기록 기능과 일정 시간마다 스크린샷을 촬영하는 기능입니다.
2. FPS 탭페이지에서 FPS 체크해 줍니다.


3. Screenshot 탭페이지에서 파일 포맷을 jpg로 선택해주고 일정 시가마다 스크린샷을 남겨주는 기능을 체크해줍니다. 시간은 10초 정도로 주면 됩니다.


4. 참고로 fraps는 구매해야 위 기능을 모두 쓸수있습니다. 가지고 있음 유용한 프로그램이니 떼를 써서라도 회사에 구매해 달라고 해봅시다.
5. 주의사항 단축키로 주로 쓰이는 F11, F10 같은 펑션키는 게임 클라이언트내에서도 무언가의 단축키로 쓰일 수 있습니다. 최대한 게임 내 단축키와 겹치지 않도록 설정합니다.
 
B. perfmon 준비

1. 우선 게임 클라이언트를 실행시켜 둡니다. 그 이유는 나중에 나옵니다. 그 다음에 Win - R 단축키를 누르면 실행 창이 뜹니다. perfmon 이라 쓰고 확인을 눌러주면 성능 모니터 도구가 실행됩니다.


2. 우선은 Win7 기준으로 설명 드리겠습니다. 메뉴 이름은 XP나 Vista나 비슷비슷하니 찾아보시면 금방 아실 수 있습니다. "데이터 수집기 집합 > 사용자 정의" 를 들어가신 뒤 오른쪽 공백페이지에서 오른 클릭을 하시면 "새로 만들기 > 데이타 수집기 집합"을 선택 하실 수 있습니다.


3. 선택하시면 새 데이터 수집기 집합을 만들기 위한 실행창이 뜹니다. 우선 이름을 정해줍니다. 이름은 직관적으로 적습니다. "게임명_메모리사용량_확인" 정도면 될 것 같습니다. 템플릿과 수동을 선택 할 수 있습니다만 수동을 선택합니다.

4. 다음을 선택하시면 "데이터 로그 만들기" 에서 "성능 카운터"를 체크하고 다음으로 넘어갑니다.

5. 성능 카운터를 추가 하는 페이지입니다. 추가 버튼을 누르면 윈도우에서 사용하는 리소스들의 리스트를 볼 수 있습니다. 이중에서 선택할 것은  "Process > Private Bytes" 입니다. 맨처음 화면에는 Processor 만 보이는데 그 바로 위에 Process 가 있습니다. 헤메지 마세요.

6. Private Bytes를 선택했으면 그 바로 아래 개체 인스턴스를 선택하는 곳이 있습니다. perfmon을 실행시키기 전에 클라이언트를 먼저 실행시키는 이유는 그래야 이곳에 client의 인스턴스가 나오기 때문입니다. 클라이언트 인스턴스를 선택한 후 추가 버튼을 누르면 오른쪽에 클라이언트의 메모리 사용량 카운터가 추가 됩니다.


7. 확인을 누르면 다시한번 카운터가 추가된 것을 확인 할 수 있습니다. 하단에 샘플간격 단위를 정할 수 있는데 1초로 합니다.

8. 다음을 누르면 로그 데이터를 남길 폴더를 선택합니다. 적당히 좋아하는 폴더를 설정해주고 마침을 누릅니다.

9. perfmon 의 준비도 완료되었습니다.

C. 시나리오 준비

● 테스트를 시작하기전 시나리오를 준비하는 것이 좋습니다. 시나리오에는 다음과 같은 요소를 정리합니다.

a. 테스트 PC Spec을 정리합니다. 가능하다면 고사양/중사양/저사양 PC와 게임 내에서 설정 가능한 그래픽 옵션을 맞춰서 정리해주는 것이 좋습니다. PC Spec의 고려는 다나와 사이트의 PC방 PC 스펙과 STEAM의 통계 페이지를 참고합니다. (http://store.steampowered.com/hwsurvey )

b. 테스팅 시간 : 30분에서 1시간 정도의 진행 시간을 잡습니다. 다른 요소에 맞춰 정하는게 가장 좋습니다만 1시간 이상 넘어가면 데이터 가공 및 확인에 어려움이 생기니 최대 1시간 정도가 좋습니다. 그 이상 해야될 경우에는 1시간 단위로 테스트를 끊어서 중간중간 데이터를 취합해가며 진행합니다. (왠만하면 쉬면서 합시다...)

c. 진행 루트 : 게임 내에서 이동할 필드의 루트를 정합니다. 오브젝트가 많은 곳, NPC가 많은 곳등 특징적인 장소를 정하거나 사전 프로그램팀의 의견을 얻어 확인되면 좋을 장소를 선정합니다.

d. 시나리오 : 테스팅 동안 수행할 액션들을 시간별로 정리합니다. 비쥬얼이 화려한 스킬을 사용한다던지 각자 따로 움직이던 테스터들을 정해진 시간에는 모여서 PK를 한다던지 클라이언트에 부하가 될만한 액션들을 놓으면 됩니다.

e. 짝짓기 : 게임 내에서 같이 이동할 테스트 파트너를 정합니다. 최소한 2명 이상은 같은 지역을 이동하며 시나리오를 진행해야 서로 다른 PC에서의 데이터 비교가 가능합니다. 같은 지역을 다니더라도 누구는 과부하가 생길 수 있고 누구는 안생길 수 있는 법입니다. 비교 대상이 있어야만 이와 같은 문제 상황을 찾을 수 있습니다.

D. 테스팅 진행
 
1. 이제 테스트 준비가 되었다면 시작합니다. 시나리오 시작 전 프랩스의 FPS 기록과 스크린샷 기록 기능을 단축키로 활성화 해준 뒤 perfmon 도 데이터 수집기 집합에서 만들어 놓은 카운터를 상단의 실행 버튼으로 활성화 시켜 줍니다.

 
2. 자 이제 테스트가 시작되었습니다. 준비해 놓은 시나리오대로 잘 진행하면 됩니다.

3. 몇 가지 주의사항
a. 테스트란게 만사형통으로 잘되었으면 좋겠지만 그게 쉽지만은 않죠. 분명 테스트 진행 중 클라이언트 혹은 서버 crash 가 발생할 수 있습니다. 진행된 곳까지의 기록은 남으니 해당 기록물들을 한폴더 모아 압축해 정리해 놓고 집착하게 다시 테스트를 진행합니다.
b. 서버 crash나 클라이언트 crash 상황이 특정한 재발생 스텝이 있을 경우 최적화 테스트를 하는 동안에는 해당 액션을 피하도록 합니다. 물론 해당 문제는 따로 보고가 되야 합니다.

4. 테스트가 완료되면 각 테스터들은 perfmon, fps, 스크린샷 파일들을 모아 압축 파일로 만들어 진행자에게 전달합니다. 압축 파일명은 사전에 통일 시켜주는 것이 좋습니다. "홍길동_시나리오1.zip" 혹은 crash가 있던 결과물은 "홍길동_시나리오1_crasf.zip" 이런식으로 말이죠.

E. 결과물 정리하기

1. 드디어 결과물들을 정리할 시간이 되었군요. 테스터들은 모두 가버리고 진행자인 당신만 남았습니다. 그럼 기분을 가라 앉히고 파일들을 열어 봅시다. 우선 perfmon 입니다. 설정에서 선택한 폴더에 가면 DataCollector01.blg 파일이 있을 겁니다. 실행시키면 자동으로 모니터링 도구가 실행되며 보고서 그래프를 볼 수 있습니다. 엑셀에서도 이 그래프를 볼 수 있을 것입니다. 그래프 화면에서 오른 클릭 후 "데이터를 다른 이름으로 저장"을 선택 합니다. 그리고 파일형식을 쉼표 구분의 csv 파일로 선택하여 저장 합니다.

2. 저장된 csv 파일을 열어봅시다.

A열이 시간이고 B열은 사용된 메모리 값입니다. 표만봐도 시간이 지날 수록 메모리 사용량이 늘었음을 알 수 있군요.
자 이제는 Frame 입니다.

3. fraps의 저장 폴더를 따로 설정하지 않았다면 fraps 설치 폴더 내에 Benchmarks 라는 폴더가 생겼을 겁니다. 그 폴더 내에 역시 csv 확장자 파일이 있습니다. 열어보도록 하겠습니다.

떨렁 A열에 숫자만 잔뜩 있는 문서가 열릴 것입니다. 저 숫자들이 초당 Frame 값입니다. A열 선택 후 복사한 뒤 perfmon 결과 문서에 합칩니다.

엑셀 내용을 좀 종리했습니다. 시간은 년월일은 제거하고 시분초만 남겼고 FPS를 C열에 붙여넣었습니다. 이제 그래프로 만들겠습니다.

4. Excel 2007 부터 그래프 만들기가 참 쉬워졌습니다. 위의 A,B,C 열을 모두 선택한 뒤에 리본 메뉴의 삽입에서 그래프 "꺾은선형"을 선택하면 바로 그래프가 만들어 집니다.

예제의 Excel은 2010 같기도 하지만 어차피 리본메뉴는 같습니다. 리본메뉴 없는 Excel을 쓰신다면 네이버에서 그래프 만드는 법을 검색해보시기 바랍니다. 아니면 회사에 Excel 2007 이상은 사달라고 쫄라봅시다.

5. 그래프는 만들어 졌는데 메모리 사용량은 나오는데 FPS는 그래프에 보이지 않습니다. 메모리 사용량과 FPS의 값 단위가 너무 나서 그렇습니다. 조정을 좀 해주겠습니다.

축 옵션에 들어가서 최대값과 주 단위를 좀 조정해 준 뒤 표시단위는 백만으로 선택합니다. 그러면 단위값이 변경되며 하단에 FPS 그래프가 바닥에 붙어서 나오게 됩니다. 

바닥의 껌딱지를 오른 클릭한 뒤 데이터 계열 서식을 선택 합니다. 계열옵션에 보면 보조 축을 선택할 수 있는데 선택화면 오른쪽에 FPS 축이 따로 생기면 껌딱지가 발딱 일어납니다. 기운차군요(.....)

6. 너무 자세하게 쓰느라 슬슬 힘들기 시작하므로 그래프의 미세한 조정은 엑셀책을 보고 알아보시길 권하고 이제 문제점 찾기로 가보겠습니다.

F. 문제점 찾아보기



좀 정리된 그래프를 보도록 하겠습니다.
나름 깔끔한 그래프입니다. 메모리 사용량에서 특출나게 많이 잡아 먹는 경우도 없고 중간중간 누수없이 반환도 잘 되고 있습니다. Frame은 굴곡이 너무 심해보입니다. 잦은 Frame 변화는 유저의 시각적 피로도를 증가시키는 요소 입니다. 수직 동기화 옵션을 제공해서 최대한 60fps 정도를 유지해주는 것이 좋습니다.

갑작스럽게 Frame이 떨어지는 곳이 보입니다. 왜 그런지 유추해야겠죠. 먼저 집에간 테스터들을 괴롭혀 봅시다. 문제가 있는 그래프의 테스터에게 전화를 걸어 테스트 할 때 몇시 몇분에 무슨 상황이었냐며 물어봅시다. 기억을 못하면 그런것도 기억을 못하냐고 해봅시다. 자신이 쿨가이임을 느끼며 전화를 끊습니다. 는 농담이고.. 이제 스크린샷을 사용합니다. 스크린샷은 10초 단위로 찍도록 설정했기 때문에 해당 시간에 맞는 스크린샷을 열어서 확인해봅니다.

스크린샷을 통해 Frame이 떨어지는 곳의 장소와 상황을 알 수 있습니다. 어떤 스킬을 사용했더니 파티클로 인해 프레임이 떨어 질 수도 있고 해당 장소에 유저들이 잔뜩 모여 렉이 발생했을 수도 있습니다. 아니면 오브젝트가 잔뜩있는 장소일 수도 있구요.
Frame 뿐 아니라 메모리 사용량에 있어서도 그래프에 극심한 변화를 볼 수 있을 경우 스크린샷으로 해당 상황을 유추할 수 있습니다.

문제의 확인 시에는 다른 테스터들의 결과물도 도움이 됩니다. 꼭 비교 참조해서 전체적으로 발생하는 현상인지 하나의 PC에서만 나오는 현상인지도 확인하도록 합니다.

어떤 류의 문제가 생기는가에 대해 모든 상황을 언급해주기는 어렵습니다. 문제라 생각하고 얘기했는데 실은 아니더라라는 경우도 충분히 있을 수 있거든요. 가장 좋은건 그래프와 스크린샷을 통해 알아낸 결과물들을 정리 한 뒤 개발팀과 같이 확인 하는 것이라고 봅니다. 좀 더 확실한 문제의 이유를 찾을 수도 있고 자신의 경험 축적에도 도움이 될수 있을 것입니다.


p.s 오랫만에 긴 글을 작성할려니 힘들군요. 나눠쓰고 싶은 욕구가 강하였으나 테스트 진행 과정이 중간에 뚝 끊겨있으면 욕먹을것 같아서 그러지 않았습니다; 하지만 내용량 대비 시간을 들이지 못해서 부족하거나 이해 안가는 부분이 있을 수도 있을것 같습니다(......) 그런 부분은 질문 주시면 답변을 드리도록 하겠습니다. 감사합니다.

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

클라이언트 최적화 테스트하기 아프다 서비스(A/S) 입니다.


일요일에 클라이언트 최적화 테스트하기에 대한 글을 작성해 올렸습니다.

해당 내용 중 잘못된 내용이 있어 수정과 함께 제 마음이 아픈 아프다 서비스(A/S)를 드립니다.

잘못된 내용 부분은 Fraps의 fps 기록과 스크린샷 촬영 기능을 같이 켜준다. 라는 부분인데 같이 안된다라는 제보가 있어 확인해보았습니다.

안되더군요. =_=;;;

우선 변명을 드리자면 해당 내용은 제 경험과 함께 웹에서 조사한 툴 조합 시 얻을 수 있는 결과물에 대해 나름 고민해서 만든 테스팅 메뉴얼 입니다. 그런데 솔직하게 얘기드리자면 제가 대부분 15분,30분 단위의 테스팅을 진행해서  이때까지 1시간 가량의 장시간 테스팅을 할 기회는 없었습니다. (MMORPG 계열 보다는 FPS와 같은 단시간 게임 테스팅을 주로했다고 생각해주시면 될 것 같습니다.)

그러다보니 보통은 스크린샷보다 테스터의 피드백 수집으로 리포트 작성이 되더군요. (15분 30분 정도는 상황에 대해 테스터들도 기억을 합니다.)

스크린샷에 대한 내용은 1시간 이상 테스트가 지속될 경우를 예상해 도움이 되고자 넣은 내용인데 FRAPS에서 2가지 기능을 같이 사용 못한다는 제약이 있다는 걸 사전에 파악하지 못하고 글을 작성해 잘못된 내용을 올린것 같습니다. 이 부분 해당 게시물을 보신 모든 분들께 사과 드립니다.

그럼 A/S 나갑니다.

생각보다 특정 시간마다 스크린샷을 찍어주는 캡쳐 프로그램이 정말 없더군요. 저는 주로 리포트 작성용으로 오픈 캡쳐를 사용하고 있습니다만 오픈 캡쳐도 해당 기능은 없었습니다. (요청 피드백을 보내봐야 겠습니다. ^^)

우선 찾아낸 캡쳐 프로그램으로 안카메라라는 캡쳐 프로그램이 있었습니다.



인터페이스 참 단순하더군요. 저 중앙창을 스크린샷 찍으려는 영역에 맞춰 창크기를 조절하면 캡쳐 준비 끝입니다.
옵션에서 ms 단위로 시간을 조절 할 수 있으며 상단 2번째 아이콘으로 시간 캡쳐를 시작 할 수 있습니다.

프랩스와 안카메라3를 같이 사용하면 1시간 이상 테스트시 상황 기록에 대한 이슈는 해결 할 수 있을 것으로 보입니다. :)

프랩스에서 동시 기능을 지원해주는게 가장 깔끔할텐데 저도 아쉬움이 많이 남네요 :(

그럼 도움 되었기를 바라며 이만 글 줄이겠습니다.
감사합니다.

저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by blueasa

댓글을 달아 주세요


Debugging Applications For Microsoft Windows 책에 보면 O1최적화 (크기 최소화)가 O2최적화(속도 최대화)보다 더 빠르다고 나와있다..

정말????


책에 보면 대략 실행파일의 이미지 크기가 작으면 page fault를 더 적게 일으키므로 더 빠르다라고 나와있다..

음.. 맞는말이다.. page fault가 일어날 경우에는 OS가 프로그램 실행에 필요한 데이터를 page file에서 실제 메모리에 load하기 위해서 엄청난 오버헤드가 발생한다..

그렇지만 고작 몇백킬로 차이밖에 안나는 것이 얼마나 눈에 띄게 속도 차이가 날까? 라는 의문이 마구마구 들었다..

그래서 테스트를 해봤다..

우선 테스트 프로그램을 만들어야하는데.. 대략 만들기는 귀찮고 해서 GUI를 이용하기로 했다.. GUITester에서 상단에 프레임률을 표시하기 때문에 이를 확인해보면 된다.

그래서 우선 프로젝트 세팅에서 아래와 같이 O2 최적화로 세팅을 했다.



그리고 컴파일.................................................


그리고나서 바이너리의 크기를 확인했다.



대략 1메가 정도 한다..
그럼 이제 실행을 해보자..



음.. 약 262프레임정도 나온다..



그럼 이제 O1로 컴파일을 해보자... 우선 프로젝트 세팅을 하고..



그리고 컴파일.............................

그리고나서 바이너리 크기 확인



약간 줄었다.. 70kb정도 ㄱ- 이정도로 정말 속도 차이가 날까???

그리고 실행...



엇!!!!! 이게 무슨일인가.. 미약하지만 정말 O2보다 프레임률이 올라갔다...

혹시나 하여 두세번 반복해서 재컴파일을 하여 비교를 해봤지만 확실히 차이가 보였다..

그렇지만 내가 생각하기에 page fault때문에 성능 차이가 나는것 보다는 바이너리의 크기가 작아지면서 CPU의 명령어 캐시히트율이 올라가서가 아닌가 하는 생각이 든다..

아마 항상 O1이 O2보다 빠르진 않을거라 본다.. 실제로 어떤게 더 빠른지는 테스트를 통하여 최적의 최적화 옵션을 선택해야 할 것이다.


저작자 표시 비영리 동일 조건 변경 허락
신고
Posted by blueasa
TAG /O1, /O2, 최적화

댓글을 달아 주세요

출처 : http://www.gamecode.org/tt/entry/1851

1. 도입

게임을 만드는 데 있어서 높은 프레임 레이트는 모든 프로그래머들의 공통적인 관심사가 아닐까 싶다. 이러한 관심사에 대한 아이디어와 이미 많이 적용되고 있는 것들을 정리하고자 한다.
(사실 제 관심사는 이런 거보단 '어떻게 해야 빨리 개발할 수 있나'입니다만, 항상 실패만 해서... T_T;;)

2. 하드웨어 가속

하드웨어의 발전과 함께 최적화에 대한 접근도 시시각각 변하고 있는 데, 최근 가장 큰 화제는 3차원 가속기를 얼마나 활용하느냐 하는 문제이다.

비디오카드가 최적화된 아키텍처로 렌더링의 많은 부분을 처리해 주면서 얻은 이점 중에 두 가지에 주목할 필요가 있다.

첫 번째로 기존의 CPU에서 해주어야 했던 일거리를 더욱 빠르고 효율적으로 처리해준다는 점이다. 즉 프로젝션, 클리핑등의 일을 CPU보다 GPU에서 더욱 빠르게 수행한다는 얘기다. (GPU가 CPU보다 코어 스피드는 느릴 수도 있지만, 최적화된 메모리 억세스, 다수의 계산 유닛을 통한 병렬적인 계산등을 통해 더 많은, 즉 더 빠른 계산을 하도록 설계되기 때문이다.) 얼마나 많은 일을 CPU가 아닌 GPU가 하느냐가 최적화의 포인트라고 할 수 있다.

두 번째로 병렬성에 있다. 즉, 비디오카드의 GPU와 메인CPU와는 완전히 별개로 수행된다는 점이다. 즉, 비디오카드가 열심히 렌더링하는 시점에도 CPU는 다른 일을 할 수 있다는 점이다. 즉, 비디오카드가 렌더링 할 동안 CPU가 더욱 많은 일을 하는 것이 최적화의 포인트겠고, 비디오카드가 한순간도 쉬지 않고 렌더링 작업을 하는 것이 가장 이상적이라고 할 수 있다. (여담으로 XBOX 개발킷 중에 카르마란 프로파일링 도구를 본 적이 있는 데, 그 툴의 기능은 원하는 타이밍의 프레임에 비디오카드가 idle 이 되는 구간을 - 즉 CPU가 일을 못 주고 혼자 뭔가 연산하는 타이밍 구간- 측정해서 리포트해주는 것이다. 이러한 관점에서 프로그래밍 허점을 찾도록 도와주는 도구인데 하드웨어 성능을 살리는 데 상당히 도움이 될 꺼란 생각이 든다.)

어떻게 해야 더욱 빠르게 렌더링 되느냐 같은 문제는 다음 글에서 다루기로 하고, 더 이상 렌더링 속도를 개선할 수 없는 상황이라고 가정하면, 위의 두 가지가 그것을 극복할 수 있는 방법이 될 것이다.

첫 번째의 경우에는 사실상 최적화할 수 있는 부분이 정해져 있기 때문에 [1], 하드웨어의 T&L과정을 완전히 GPU에 넘긴 후라면, 연산을 GPU로 넘긴다는 관점에서는 더 이상 해줄 수 있는 것이 없다.

하지만, 두 번째의 경우에는 특별한 무언가만 해준다면 쉽게 큰 효과를 얻을 수도 있는 데, 차근 차근 생각해보도록 하자.

3. 일반적인 CPU 와 GPU 의 작업 타이밍

메인 루핑은 렌더링과 전혀 상관없는 _process() 와 렌더링만 하는 _display() 함수로 구분해보자. (만약 인공지능 루틴 등에 렌더링 루틴이 삽입되어 있다면 원하는 대로 렌더링 배치를 하기가 힘드므로, 여기서 얘기하는 것을 적용하기는 쉽지 않다.)

만약 이와 같은 구성이라면 일반적인 프로세스의 처리 그래프는 [그림3.1]과 같을 것이다.


(그림 3.1)

간단히 그림을 살펴보면 먼저 process 가 수행되는 동안에는 GPU가 놀고 있다. 그러다. DISPLAY 가 시작되면 본격적으로 GPU 가 활동하게 된다. DISPLAY 루틴 상 부하가 있다면 위처럼 비디오 카드가 놀고 있는 타이밍이 있을 수 있다. 그리고 DISPLAY 루틴이 끝나는 지점 -일반적으로 flip 을 하거나 present 를 하는 순간- 부터 CPU는 비디오카드의 내부 큐(리스트)에 있는 작업들이 끝날 때까지 대기를 한다. (파란색 선 사이가 한 프레임을 나타낸다.)

[그림3.1]의 모양은 나타내는 프로그램은 비효율적으로 프로그래밍을 한 것이다. 하나씩 체크해보면서 이 구성을 최적화해서 비디오카드가 전혀 쉬지 않고 작동할 수 있도록 구성해본다.

4. 렌더링 루틴과 타이밍의 관계

먼저, GPU의 타이밍에 대해서 확인하고 넘어가자.


(그림 4.1)

[그림4.1]과 같은 경우를 생각해본다. CPU에서 매트릭스 트랜스폼하는 시간을 1 이라고 표시했다. A라는 오브젝트를 DrawPrimitive 하게 되면 거의 딜레이 없이 CPU에게 프로세스가 넘어오며, 그와  동시에 CPU와는 별개로 GPU는 렌더링을 한다. 여기서 CPU는 다시 매트릭스 트랜스폼 등의 과정을 거친후에 B 오프젝트를 렌더링하게 되며 GPU는 B를 렌더링 하게 된다.
여기서 관심있게 볼 것은 A보다 B의 오브젝트가 폴리곤이나 렌더링 부하가 더 많다고 가정했지만 DrawPrimitive 호출후 CPU에 프로세스 권한이 넘어오는 타이밍은 A나 B나 비슷하다는 것이다. GPU에게 명령을 주는 것은 폴리곤 수에 따라 하는 일에 큰 차이가 없다는 얘기다.

여기서 눈에 띄는 부분은 GPU가 A의 렌더링을 끝내고 대기하는 타이밍이다.
만약 B를 먼저 렌더링 한다면 어떻게 될까 ?


(그림4.2)

[그림4.2]처럼 된다. 즉 A의 작업은 비디오카드의 큐에 추가되고 비디오카드는 B의 렌더링이 끝나는 시점에 A를 렌더링하게 된다.

단순히 렌더링 타이밍에 맞춰서 배치만 조절해도 GPU가 대기하는 시간을 제거할 수 있다는 얘기다. 물론 위의 예처럼 GPU 타임을 예측해서 배치하는 것은 불가능하지만, [그림 4.3]처럼 단지 GPU가 부담을 느끼는 일을 먼저 던져준다면 별다른 처리 없이도 어느 정도의 효과를 얻을 수 있다.


(그림 4.3)

특히 CPU의 부담이 생길 수밖에 없는 배경 렌더링의 경우에는 배경 렌더링하기 전에 적절히 GPU에게 (좀 부담스러운 녀석으로) 할 일을 준다면 효과를 볼 수 있을 것이다. 항상 위의 그래프처럼 향상될 수도 있다는 얘기는 아니지만, 그렇지 않더라도 완충 효과를 줄 수 있어 대기 상태가 나타날 확률을 훨씬 줄일 수 있다. (배경보다 먼저 렌더링해도 전체적인 필레이트에 변화가 적다면 절대 손해 안보는 장사가 될 것이다. - 저라면 작게 렌더링 되면서, 폴리곤이 많은 녀석을 제물로 삼겠습니다.)

다만, 고려해야 할 것은 비디오의 큐 메모리도 한계가 있다는 것이다.
렌더링 타임이 긴 것들을 연속으로 넘기면 뒤의 처리들이 계속 큐게 쌓여서, 큐의 한계까지 차버리게 되는 데, 이 경우 큐에 넣기 위해 대기하는 시간이 생길 수 있다. (큐를 빨리 비우는 방법은 렌더링 타임이 작은 오브젝트를 찍는 것이므로 대기 시간이 안 생기고 큐에 무리를 주지 않는 범위에서 매니징을 해야 한다.)

한가지 주의할 것은 여기서 얘기하는 것은 순수하게 하드웨어의 힘으로 렌더링을 하는 경우여야 효과를 발휘할 수 있다는 점이다. 아닐 경우에는 GPU가 렌더링하는 시간이나 CPU가 처리하는 시간이나 비슷해져서 어떻게 해도 큰 차이가 없게 된다.

5. 최종 렌더링 타이밍

렌더링시 부하가 될만한 부분을 분리하여 구현하고 배치가 적절히 되었다면 [그림 5.1]처럼 나타낼 수 있다.


(그림 5.1)

한가지 짚고 넘어가면 보통 DISPLAY의 마지막에 호출되는 present나 flip 관련함수는 GPU 가 끝나기 전에 프로세스 권한을 넘겨주지 않는다. 즉 해당 함수에서는 큐가 빌 때까지 딜레이가 생기는 것이다. 실제로 DrawPrimitive 는 대부분 내부 큐에 렌더링할 꺼리를 쌓는 역할을 할 뿐이고 최종적으로 렌더링이 끝나는 것은 present 를 수행한 뒤가 된다.
(흔히 비 경험자들은 "프로파일링을 했더니 present함수가 가장 느리다." 라고 오해하기도 하는 데, 이는 직접적으로 present 함수의 부하가 아니므로 present 함수가 느리다고 생각하는 것은 적절한 판단은 아니다.)

인공지능, 물리학 등의 처리가 늘어날수록 process의 비중이 커져서 위의 구성으로는 GPU가 대기하는 시간이 비례해서 늘어나게 된다. 물론 위와 같이 배치하지 않고 렌더링 루틴 사이에 인공지능루틴들을 잘 넣으면, GPU가 대기하는 타이밍을 줄일 수도 있겠지만, 일정하게 효율을 얻기는 힘들기 때문에 추천하기는 어렵다.

결론부터 말하면 present 호출 후에 렌더링이 끝날 때까지 대기하는 시간에 CPU가 놀지 않고 다음 프레임의 process 를 실행하는 방법으로 이 문제를 해결할 수 있다.

#include <stdio.h>
#include <windows.h>

void _process()
{
   static int cnt=0;
   printf("process(%d)n", cnt++);
   Sleep(500); // process 부하
}

void _flip()
{
   Sleep(500); // flip 후 대기
}

void _display()
{
   static int cnt=0;
   static int time = timeGetTime();

   int delta = timeGetTime() - time;
   time += delta;

   printf("tdisplay (%d) %dn", cnt++, delta);
   Sleep(500); // 렌더링 명령

   _flip();
}

void main()
{
   int i;
   for(i=0; i<10; i++) {
       _process();
       _display();
   }
}

이해를 돕기 위해 가상적으로 위와 같은 파일을 구성해 봤다. 일반적인 구성을 만들어 본 것으로 process 가 500 ms, display 가 1000ms 의 부하를 가진다고 가정했다. (display는 500ms는 렌더링 명령하는 데 소비하는 시간, 500ms는 대기하는 시간이라고 가정했다.) 이를 실행해보면 다음과 같은 결과가 나온다.

process(0)
       display (0) 0
process(1)
       display (1) 1501
process(2)
       display (2) 1501
process(3)
       display (3) 1500
process(4)
       display (4) 1501

즉, _process와 _display에서 각 500ms, 1000ms 를 잡아먹기 때문에 한 프레임은 1500ms가 소요된다. 일반적인 경우라면 이 타이밍을 줄일 수는 없지만 [그림5.1]과 같다고 가정해보면 방법이 생긴다. 즉, _display의 렌더링 시간 중에 대기하는 시간에 다음 프레임의 _process 작업을 하는 것이다. 이 처리를 아래처럼 동기화 객체[2][3]를 이용하여 아래처럼 구현했다.

#include <stdio.h>
#include <windows.h>

HANDLE    g_hFlip, g_hRenderScene;

void _process()
{
   static int cnt=0;
   printf("process(%d)n", cnt++);
   Sleep(500);
}

void _flip()
{
   Sleep(500);
}

int g_done = 1;

DWORD WINAPI _flipper(void * ptr)
{
   WaitForSingleObjectEx(g_hFlip, INFINITE, FALSE);
   while(g_done) {
       _flip();
       SetEvent(g_hRenderScene);
       WaitForSingleObjectEx(g_hFlip, INFINITE, FALSE);
   }
   return 0;
}

void _display()
{
   static int cnt=0;
   static int time = timeGetTime();

   WaitForSingleObjectEx(g_hRenderScene, INFINITE, FALSE);

   int delta = timeGetTime() - time;
   time += delta;

   printf("tdisplay (%d) %dn", cnt++, delta);

   Sleep(500);

   SetEvent(g_hFlip);
}

void main()
{
   DWORD threadid;
   int i;

   g_hFlip = CreateEvent(NULL, FALSE, FALSE, NULL);
   g_hRenderScene = CreateEvent(NULL, FALSE, TRUE, NULL);

   CreateThread(NULL, 0, _flipper, 0, 0, &threadid);

   for(i=0; i<5; i++) {
       _process();
       _display();
   }

   CloseHandle(g_hFlip);
   CloseHandle(g_hRenderScene);
}

플리핑 함수만 처리하는 쓰레드를 두어서 이벤트 객체 g_hFlip로 _flip 을 컨트롤한다. 즉 처음엔 대기하다가 시그널을 주면 flip 함수를 실행하고 flip을 마친후엔 다음 시그널이 들어올때까지 블럭되어 대기하게 된다. 즉 _flip 함수 호출은 시그널을 주는 것으로 대체된다.
렌더링이 다되기도 전에 다음 프레임 렌더링하는 것을 방지하기 위해 g_hRenderScene 이벤트 객체를 두어서 흐름을 조정했다.

결과는 아래와 같다.

process(0)
       display (0) 0
process(1)
       display (1) 1001
process(2)
       display (2) 1001
process(3)
       display (3) 1002
process(4)
       display (4) 1001

즉 같은 내용을 실행하지만 전체적인 실행 간격을 줄일 수 있다는 얘기다.

보통 GPU 렌더링으로 대기하는 시간보다 process 시간이 작다면 이상적으로 GPU는 쉬지 않고 렌더링을 하게 되고, 최적의 프레임으로 렌더링이 가능해진다.

6. 결론

단순히 프레임을 극대화하는 방법은 아니지만, 물리학처리의 과부하, 네트워크 처리, 스크립트 호출 등으로 인한 불규칙한 프레임 저하에 좀 더 유연하도록 처리할 수 있고, 세세한 루틴 최적화에 대한 부담도 줄일 수 있다. (렌더링에 CPU가 관여하는 시간을 최소화했다면 최적화의 의미가 없을 수도 있겠다. - 물론 PC 플렛폼에서는 모든 하드웨어를 고려해야하기 때문에 생각하기 힘들지만...)

아주 기본적인 관계만을 고려한 것이지만 GPU를 최대한 활용하는 상황이라며 효율을 크게 높일 수 있는 방법이다.
점차 게임에서 처리하는 그래픽처리 규모나, 처리할 인공지능이나 물리학의 비중이 커지는 상황에서는 반드시 고려해야할 처리라고 생각된다.

(동기화 객체를 잘 이용한다면 프레임의 저하 없이 비동기적으로 리소스 로딩하는 루틴에도 활용할 수 있을 거 같네요. - 대기시간의 다음 프레임의 process 처리하고 남는 시간까지 활용해서...)

7. REFERENCE

[1] DirectX 9 Performance
   http://mirror.ati.com/developer/gdc/D3DTutorial3_Pipeline_Performance.pdf

[2] Win32 Multithreading and Synchronization
   http://blacksun.box.sk/tutorials.php/id/150

[3] Siberschatz. Operating System Concepts 6nd edition, chapter 7, WILREY, 2003


출처 : http://www.gamecode.org/tt/entry/1851
저작자 표시
신고
Posted by blueasa
TAG GPU, 최적화

댓글을 달아 주세요

티스토리 툴바