프로파일링(Profiling)
모바일용 최적화 실전 가이드(Practical guide to optimization for mobiles)

최적화

Just like on PCs, mobile platforms like iOS and Android have devices of various levels of performance. You can easily find a phone that’s 10x more powerful for rendering than some other phone. Quite easy way of scaling:

  1. 기준 설정에서 잘 작동하는지 확인합니다.
  2. 고성능 설정에서는 더 보기 좋게 표시되도록 만듭니다.
    • 해상도
    • 포스트 프로세싱
    • MSAA
    • 비등방성
    • 셰이더
    • Fx/파티클 밀도 활성화/비활성화

GPU 관련 사항

그래픽스 퍼포먼스는 필레이트, 픽셀, 지오메트리의 복잡성(버텍스 개수)에 따라 좌우됩니다. 더 많은 렌더러를 컬링하는 방법을 찾는다면 앞의 세 가지 요인을 모두 줄일 수 있습니다. Unity 는 시야 범위 외부의 오브젝트를 자동으로 컬링하므로, 오클루전 컬링을 활용하면 많은 도움이 됩니다.

모바일에서는 기본적으로 필레이트(필레이트=화면 픽셀 수 셰이더 복잡성 오버드로우)가 퍼포먼스를 좌우하며, 지나치게 복잡한 셰이더가 문제의 주요 원인입니다. 따라서 Unity 에서 제공하는 모바일 셰이더를 사용하거나, 자체 셰이더를 설계하고 최대한 단순하게 만들어야 합니다. 가능하다면 코드를 버텍스 셰이더로 이동하여 픽셀 셰이더를 단순하게 만드는 것이 좋습니다.

품질 설정에서 텍스처 품질을 낮췄을 때 게임이 더 빠르게 실행된다면 메모리 대역폭 때문에 제한을 받았을 가능성이 큽니다. 이 경우 텍스처를 압축하고, 밉맵을 사용하면서, 텍스처 크기를 줄이는 등의 조치를 해야 합니다.

LOD(세부 수준) - 오브젝트가 멀어짐에 따라 오브젝트를 더 단순화하거나 완전히 제거합니다.

굿프랙티스

모바일 GPU 는 발열량, 전력 사용량, 소음 발생량 등의 이유로 제약이 큽니다. 따라서 데스크톱 부품에 비해 대역폭이 작고 ALU 성능과 텍스처링 파워가 낮습니다. GPU 아키텍처 또한 가능한 한 적은 대역폭 및 전력을 사용하게 튜닝됩니다.

Unity 는 OpenGL ES 2.0 에 최적화되어 있으며, GLSL ES(HLSL 과 유사) 셰이딩 언어를 사용합니다. 빌트인 셰이더는 대부분의 경우 HLSL(Cg 라고도 함)로 작성되어 있습니다. 이는 모바일 플랫폼용 컴파일을 위해 GLSL ES 로 크로스 컴파일됩니다. 또한 원할 경우 GLSL 로 직접 코딩할 수 있으나, 현재는 GLSL->HLSL 이동 툴이 없기 때문에 OpenGL 같은 플랫폼(예: 모바일+Mac)에만 국한하여 가능합니다. HLSL 에서 float/half/fixed 타입을 사용하면, GLSL ES 에서 highp/mediump/lowp 정밀도 한정자로 바뀌게 됩니다.

굿프랙티스를 따르기 위한 체크리스트는 다음과 같습니다.

  1. 머티리얼의 수를 가능한 한 최소로 유지해야 합니다. 이렇게 하면 Unity 의 일괄 처리 작업이 쉬워집니다.
  2. 여러 개의 개별 텍스처 대신 텍스처 아틀라스(여러 개의 작은 이미지 모음으로 이루어진 큰 이미지)를 사용해야 합니다. 로딩 시간이 빨라지고, 상태 변환도 더 적게 일어나며, 일괄 처리가 용이합니다.
  3. 텍스처 아틀라스 및 공유 머티리얼을 사용하는 경우 Renderer.material 대신 Renderer.sharedMaterial 을 사용해야 합니다.
  4. 포워드 렌더링된 픽셀 광원에는 비용이 많이 듭니다.
    • 가능할 경우 실시간 광원 대신 라이트 매핑을 사용해야 합니다.
    • 품질 설정에서 픽셀 광원 수를 조정해야 합니다. 기본적으로, 오직 방향 광원만이 픽셀당 하나씩 존재해야 하며, 다른 모든 광원은 버텍스당 하나씩 존재해야 합니다 물론 게임에 따라 달라질 수 있습니다.
  5. 적절한 우선 순위를 찾아내기 위해서는 품질 설정에서 광원의 렌더 모드를 조절해 보아야 합니다.
  6. 컷아웃(알파 테스트) 셰이더는 반드시 필요한 경우에만 사용해야 합니다.
  7. 화면의 투명도(알파 블렌드) 적용 범위를 최소화해야 합니다.
  8. 다수의 광원이 특정 오브젝트를 비추는 상황을 피해야 합니다.
  9. 셰이더 경로(그림자, 픽셀 광원, 반사)의 전반적인 수를 줄여야 합니다.
  10. 렌더링 순서는 중요합니다. 일반적으로 다음과 같습니다.
    • 완전히 불투명한 오브젝트의 순서는 대체로 앞에서 뒤쪽입니다.
    • 알파 테스트 오브젝트의 순서는 대체로 앞에서 뒤쪽입니다.
    • 스카이박스
    • 알파 블렌드 오브젝트(필요할 경우 뒤에서 앞쪽으로)
  11. 포스트 프로세싱은 모바일에서 드는 비용이 많이 때문에 신중하게 사용해야 합니다.
  12. 파티클: 오버드로우를 줄이고 가능하면 가장 간단한 셰이더를 사용해야 합니다.
  13. 매 프레임마다 수정되는 메시 처리를 위해 버퍼를 두 배로 잡아야 합니다.
void Update (){
  // flip between meshes
  bufferMesh = on ? meshA : meshB;
  on = !on;
  bufferMesh.vertices = vertices; // modification to mesh
  meshFilter.sharedMesh = bufferMesh;
}

셰이더(Shader) 최적화

필레이트의 영향을 받는지 확인하는 방법은 간단합니다. 디스플레이 해상도를 낮췄는데 게임이 더 빨라진다면 필레이트로부터 제약을 받고 있다는 것을 의미합니다.

다음 방법을 통해 셰이더 복잡도를 줄일 수 있습니다.

  • 알파 테스트 셰이더 사용을 피하고 대신 알파 블렌드 셰이더를 사용해야 합니다.
  • 단순하고 최적화된 셰이더 코드(예: Unity 가 제공하는 “모바일” 셰이더 등)를 사용합니다.
  • 셰이더 코드에서는 비용이 많이 드는 수학 함수(pow, exp, log, cos, sin, tan 등) 사용을 피해야 합니다. 대신 미리 계산된 룩업 텍스처를 사용해 볼 수 있습니다.
  • 최상의 퍼포먼스를 위해서 가능한 한 낮은 수의 정밀도 포맷(Cg 의 float, half, fixed)을 선택해야 합니다.

CPU 관련 사항

픽셀 프로세싱에 있어 게임이 GPU 의 제약을 받는 경우가 많이 있습니다. 그러므로 CPU 는 사용되지 않는 경우가 발생하며, 특히 멀티코어 모바일 CPU 인 경우 자주 발생합니다. 따라서 GPU 작업을 일부 덜어서 CPU 에서 대신 처리할 수 있게 하는 것이 합리적입니다(Unity 에서 이를 모두 처리합니다). 메시 스키닝, 작은 오브젝트 일괄 처리, 파티클 지오메트리 업데이트 등이 이에 해당합니다.

이는 신중하게 사용되어야 합니다. 드로우 콜에 좌우되지 않는 상황이라면, 일괄 처리가 성능에 더 악영향을 줄 수도 있습니다. 왜냐하면 이 때 컬링 효율이 떨어지고, 광원의 영향을 받는 오브젝트가 더 많아지기 때문입니다.

굿프랙티스

  • FindObjectsOfType(또는 Unity 게터 프로퍼티 전반)은 매우 느리므로 신중하게 사용해야 합니다.
  • 움직이지 않는 오브젝트에는 정적 프로퍼티를 설정하여, 정적 배칭과 같은 내부 최적화가 가능하도록 해야 합니다.
  • 오클루전 컬링과 정렬 성능 향상(Early-Z 컬링을 이용하기 위해 필요)을 위해 CPU 사이클을 적극 사용해야 합니다.

물리(Physics)

물리 연산은 CPU 를 많이 사용합니다. 에디터 프로파일러를 통해 프로파일링할 수 있습니다. CPU 에서 물리 연산이 시간을 지나치게 소모하는 것 같다면 다음을 참조하십시오.

  • (Project settings -> Time 메뉴에서) Time.fixedDeltaTime 을 최대한 크게 조정해 볼 수 있습니다. 움직임이 느린 게임이라면, 빠른 액션이 포함된 게임보다 아마 더 적은 고정 업데이트만 필요할 것입니다. 빠른 속도의 게임일 경우 연산을 더 자주 해야 하고, 따라서 fixedDeltaTime 을 낮추지 않을 경우 충돌 동작이 제대로 되지 않을 수 있습니다.
  • Physics.solverIterationCount(물리 관리자).
  • 가능한 천 오브젝트를 적게 사용해야 합니다.
  • 리지드바디는 필요한 경우에만 사용해야 합니다.
  • 메시 콜라이더 설정에서 기본 콜라이더를 사용해야 합니다.
  • 정적 콜라이더(즉, 리지드바디가 없는 콜라이더)는 큰 퍼포먼스 저하를 야기하기 때문에 절대로 움직이지 말아야 합니다. 프로파일러에서 ‘Static Collider.Move’로 나타나지만 실제 프로세싱은 Physics.Simulate 에서 이루어집니다. 필요할 경우, 리지드바디를 추가하고 isKinematic 을 참(true)으로 설정해야 합니다.
  • Windows 에서는 NVidia 의 AgPerfMon 프로파일링 툴을 사용하여, 필요한 경우 더 상세한 정보를 얻을 수 있습니다.

Android

GPU

다음은 잘 알려진 모바일 아키텍처 리스트입니다. PC/콘솔 부문과는 다른 하드웨어 공급업체이며, 동시에 “일반” GPU 와는 매우 다른 GPU 아키텍처입니다.

  • ImgTec PowerVR SGX - 타일 기반, 디퍼드: 모든 것을 작은 타일(16x16)로 렌더링하며, 보이는 픽셀만 셰이드합니다.
  • NVIDIA Tegra - 클래식: 모든 것을 렌더링합니다.
  • Qualcomm Adreno - 타일: 전체를 큰 타일(256k)로 만들어 렌더링합니다. Adreno 3xx 는 기존 버전으로 전환 가능합니다.
  • ARM Mali - 타일: 모든 것을 작은 타일(16x16)로 만들어 렌더링합니다.

서로 다른 렌더링 접근 방식을 적용하면서 그에 맞게 게임을 설계해야 합니다. 특히 정렬에 주의를 기울여야 합니다. 개발 사이클 초기에 저사양 디바이스를 어디까지 지원할지 정의해야 합니다. 게임 설계를 진행하면서 프로파일러를 설정하고 테스트를 진행해야 합니다.

플랫폼 특정 텍스처 압축을 사용해야 합니다.

Further reading

화면 해상도

Android 버전

iOS

GPU

PowerVR 아키텍처(타일 기반 디퍼드)만 고려합니다.

  • ImgTec PowerVR SGX - 타일 기반, 디퍼드: 모든 것을 타일로 렌더링하며, 표시되는 픽셀만 셰이드합니다.

즉,

  • 밉맵은 크게 필요하지 않습니다.
  • 안티앨리어싱 및 비등방성은 비용이 적게 들며, 일부 경우 iPad 3 에는 필요 없습니다.

단점으로는,

  • 프레임당 버텍스 데이터(버텍스 수 * 버텍스 셰이더 적용 후 요구되는 스토리지)가 드라이버에서 할당한 내부 버퍼를 초과하면, 씬은 “분할”되어야 하며 이때 성능 저하가 일어납니다. 드라이버에서 해당 시점 이후 더 큰 버퍼를 할당할 수도 있고, 버텍스 수를 줄여야 할 수도 있습니다. 이 현상은 iPad2(iOS 4.3)의 경우 상당히 복잡한 셰이더가 적용되는 버텍스 약 10 만 개 정도의 시점에서 분명히 나타납니다.
  • 타일 기반 디퍼드 렌더링(TBDR)을 하려면 더 많은 트랜지스터를 타일링 및 디퍼드 파트에 할당해야 하며, 개념적으로는 “기본 성능”을 위해서는 트랜지스터를 덜 사용합니다. TBDR 에서 드로우 콜용의 GPU 타이밍을 얻는 것은 매우 어려우므로(실질적으로 불가능함), 따라서 프로파일링이 어려워집니다.

Further reading

화면 해상도

iOS 버전

동적 오브젝트(Dynamic Objects)

에셋 번들(Asset Bundles)

  • 에셋 번들은 특정 한도까지만 장치에서 캐시됩니다.
  • 에디터 API 를 사용하여 생성합니다.
  • WWW.LoadFromCacheOrDownload 와 같은 WWW API 를 사용하여 로드하거나, 또는 리소스로서 AssetBundle.CreateFromMemory 나 AssetBundle.CreateFromFile 을 사용하여 로드합니다.
  • AssetBundle.Unload 를 사용하여 언로드합니다. 번들을 언로드하기 위한 옵션이 있으나, 로드한 에셋에 사용하지 말아야 합니다. 또한 로드한 에셋이 씬에서 레퍼런스되더라도 모든 로드한 에셋을 삭제할 수 있습니다.
  • Resources.UnloadUnusedAssets 은 씬에서 더는 레퍼런스하지 않는 모든 에셋을 언로드합니다. 그러므로 필요 없는 에셋 레퍼런스는 꼭 삭제해야 합니다. 공용 및 정적 변수는 절대로 가비지 컬렉션의 대상이 되지 않습니다.
  • Resources.UnloadAsset 은 메모리로부터 특정 에셋을 언로드합니다. 필요할 경우 디스크에서 다시 로드할 수 있습니다.

iOS 에서 에셋 번들의 동시 다운로드 수에 제한이 있습니까? (예를 들어, 동시에(또는 매 프레임) 10 개가 넘는 에셋 번들을 안전하게 다운로드할 수 있습니까?)

다운로드는 OS 에서 제공하는 비동기 API 를 통해 구현되어 있기 때문에, 다운로드를 위해 얼마나 많은 스레드를 생성해야 할지를 OS 가 결정합니다. 여러 개의 병행 다운로드를 시작할 때에는 장치가 지원할 수 있는 총 대역폭 및 가용 메모리 공간을 염두에 두어야 합니다. 각각의 병행 다운로드마다 임시 버퍼를 할당하므로, 메모리가 부족하지 않도록 주의해야 합니다.

리소스(Resources)

  • 어떤 에셋이 빌드에 포함되려면 Unity 가 해당 에셋을 인식해야 합니다.
  • Unity 가 바이너리 데이터로 인식해야 할 모든 raw 바이트에 .bytes 파일 확장자를 추가해야 합니다.
  • Unity 에서 텍스트 에셋으로 인식해야 할 모든 텍스트 파일에 .txt 파일 확장자를 추가해야 합니다.
  • 리소스는 빌드 타임에 플랫폼 포맷으로 변환됩니다.
  • Resources.Load()

덜 중요한 이슈 리스트

  • 적절한 압축이 적용되지 않은 텍스처
  • 경우에 따라 다른 해결책이 적용되지만, 압축을 하면 되는 경우들 외에는 텍스처를 꼭 압축해야 합니다.
  • ETC/RGBA16 - Android 용 디폴트지만 GPU 공급업체에 따라서 변경 가능합니다. 가장 좋은 접근방식은 가능한 한 ETC 를 사용하는 것입니다. 알파 텍스처는 하나의 채널을 알파용으로 하여 두 개의 ETC 파일을 사용할 수 있습니다.
  • PVRTC - iOS 용 디폴트이며, 대부분의 경우에 적합합니다.
  • Get/Set 픽셀이 활성화된 텍스처 - 메모리 사용량이 두 배가 되므로, Get/Set 이 필요하지 않으면 비활성화해야 합니다.
  • 런타임에 JPEG/PNG 에서 로드한 텍스처는 압축되지 않습니다.
  • 고용량 mp3 파일은 로드 시 압축 해제로 표시됩니다
  • 추가 씬 로딩
  • 메모리에서 제거되지 않고 남아있는 사용되지 않는 에셋
  • 게임이 랜덤으로 크래시한다면, 개발 키트 또는 2GB 메모리 디바이스(iPad 3 등)에서 테스트해 볼 수 있습니다.

때로는 콘솔 문제가 아닌 랜덤 크래시일 수 있습니다.

  • 빠른 스크립트 호출 및 스트리핑은 iOS 에서 랜덤 크래시로 이어질 수 있습니다. 이를 제외하고 테스트해 볼 수 있습니다.
프로파일링(Profiling)
모바일용 최적화 실전 가이드(Practical guide to optimization for mobiles)