Mobile Profiler

각 플랫폼별 profiler 셋팅

프로파일러 항목별 최적화

  1. Meshskinning.SkinOnGPU
    렌더링 전 버텍스당 스키닝을 각 프레임마다 재계산하는데, 이 폴리곤이 많아지면 연산부담도 증가하게 됨.
    이 연산은 CPU에서 처리하는데 Unity에서 옵션값을 통해 GPU에게 할당할 수도 있음
    Player Settings > Player > Other Settings > Compute Skinning
    해당 값이 활성 > GPU 연산, 비활성 > CPU 연산
    언뜻 보면 GPU에게 맡기면 빠를거 같지만 모바일 환경은 GPU 사양이 제한되어 있어, 병목 원인에 따라 선택해야 함
    CPU로 연산하면 SIMD라는 아키텍처를 이용해 고속연산을 수행하기 때문에 모바일 환경이라면 비활성 하는게 이득
    하지만 프레임 드랍의 원인이 CPU라면 GPU에게 처리하도록 하는게 더 나은 방법
    *단, GPU 스키닝 연산은 OpenGL ES2에서는 지원되지 않음. DX11과 OpenGL ES3에서만 유효 (지원여부는 바뀔수있다)

  2. Dynamic Batching
    메시 렌더링시 월드공간 변환을 VS(버텍스셰이더) 에서 담당하는데 (=GPU 연산)
    해당 값 활성화시 이를 CPU에게 맡김 (CPU에게 오버헤드 가중)
    메시 렌더링(드로우콜)은 그래픽스 API 영향이 커서 좋은 API (Metal, Vulkan) 일 경우에는 이를 GPU에게 맡기는 것이 낫다

 

 

유니티 그래픽스 최적화 - 4. 드로우콜과 배칭 (Draw Call & Batching)

. . 4. 드로우콜과 배칭 (Draw Call & Batching) 4-1 드로우콜 (Draw Call) 수많은 병목지점이 있지만, 드로우콜이 병목인 경우가 많다. 최적화를 많이 간과하기 때문. 01) 드로우콜의 이해 CPU가 GPU에 오브젝��

wonsorang.tistory.com

Texture 최적화

  1. 한프레임당 생성할 텍스쳐가 많을 경우 new Texture()를 통해 LoadImage(byte[]) 함수를 쓰는 경우 이를 피하는 것이 좋다.
    • 만약 local file path를 알고 있다면, UnityWebRequestTexture 를 통해 GetTexture()로 바로 얻어오는게 이득 
  2. new Texture() 함수는 의외로 저비용. 만약 텍스쳐를 생성하는 중에 프레임 저하가 발생한다면, Texture에 색상값을 write하는 함수를 의심해볼것.

GC 최적화

  1. GC는 실행될때마다 메인 쓰레드를 잠시 멈추고 메모리를 한번 훑은 뒤 파편화된 공간들의 정리가 완료되면 메인쓰레드를 마저 실행해준다
    • 당연하게도 사용중인 힙메모리가 많을수록 GC.collect 실행시간이 증가하게되고 이는 곧 프레임저하의 주요 원인인 GC Peak를 유발한다
    • 주기적으로 호출하여 메모리를 자주 정리해주는건 좋지만 과도한 호출은 원활한 플레이에 지장을 줌
  2. 2019.3 버전부터 Incremental GC 기능이 추가되었는데, 이 기능은 GC 실행시 처리할 항목들을 여러 개로 나눠 처리하게 해주는 기능임. GC가 빈번히 호출되진 않지만, 호출시마다 GC Peak 가 가파른 환경에서 저 옵션을 켜는 것만으로도 눈에 띄는 효과를 볼 수 있음. 총 처리해야 할 총량이 줄어들진 않지만 나눠서 처리하기 때문에  많은 환경에서 이득을 기대할 만 함
  3. 위와 같은 처리를 해도 결국 메모리는 할당되어야 하고, 이 때문에 개발자들은 메모리를 절약할 수 있는 방법을 찾아야 함
    • 같은 동작이라도 처리방식에 따라 메모리 사용량이 다르기 때문에 병목현상이 발생한다면 API교체를 고려해보자
    • 총 힙메모리 양 대비 일정 가용량을 넘어서면 프레임 저하가 발생할 수 밖에 없으므로 현재 메모리 사용량을 항상 체크
      1. Profiler.usedHeapSizeLong과 Profiler.GetMonoUsedSize를 사용하여 현재 메모리 사용량을 파악할 수 있다
        • usedHeapSize : 우리가 아는 그 힙메모리로서, 변수 할당, 클래스 선언부터 Unity에서는 Texture, GameObject, Material등 런타임 관련 오브젝트를 다룰때 할당되는 영역
        • monoUsedSize : script, Asset, 등은 해당 영역에 할당. Unity의 스택 영역이라고도 볼 수 있다
      2. SystemInfo.SystemMemorySize : Unity가 쓸수 있는 메모리 총량

Shader 최적화

  1. Galaxy S20 폰에서 몇몇 머티리얼 렌더링 시 프레임 렉이 발생(픽셀이 깨져 해상도가 매우 낮아보임)
    • 60 frame 이상을 찍고 있는 상태에 렌더링 부분만 프레임 렉이 발생해서, 전형적인 GPU쓰로틀링 현상이라 쉐이더 픽셀연산을 살펴보았지만, 단순히 텍스쳐에 _Time.y 을 이용한 UV 애니메이션이 들어간 쉐이더조차 프레임 렉이 발생
    • 원인은 _Time 변수 때문이었는데, 요 값은 게임시작 시부터 지금까지 흐른 시간을 받아오는 변수라서, 수많은 유니티 강좌에서 UV 애니메이션을 설명할때 자주 등장한다.
    • 문제는 이 값이 계속 증가하면서 모바일GPU 칩의 부동소수점 연산처리 한도를 벗어나게 되어, 제대로 된 _Time.y 값이 반환되지 않았던 것이다.
    • Galaxy S20의 GPU칩은 16bit 나 그 이하의 비트만을 부동소수점 연산으로 제공하기 때문에 문제가 도드라져 보였던 것
    • 해결책으로는 _Time.y 값을 쓰지 않고 외부의 script에서 Time.deltaTime 값을 누적해 직접 쉐이더에 전달하고, 해당 값은 일정 값을 넘지 않도록 클램핑하는 것이다.
    • _Time 을 이용한 UV 애니메이션이 들어간 모든 Shader 를 고치는 대공사를 진행함

프로그래밍 & 코드 최적화

  1. 적절한 targetFrameRate 설정
    • Adaptive Performance 사용 고려
      • 현재 메모리 가용상태에 대한 콜백을 제공하고, 병목의 원인이 GPU인지 CPU인지 정보도 알려줌
      • PackageManager 에서 설치 후 콜백만 연결해주면 바로 사용 가능
      • Galaxy S10 이상의 안드로이드만 지원
      • 추후 지원폰을 확대하겠다고 했지만, 마지막 버전이 나온지 1년이 넘었네..
  2. 비동기 로딩 사용
    • AssetBundle.LoadFromFileAsync() 등..Async가 붙은 함수를 사용하자
  3. 사용하지 않는 MonoBehaviour 함수 제거
    • Start(), Update(), FixedUpdate() 등은 C++ (엔진) 단에서 C# 함수와 매핑과정을 거치는데 많을수록 엔진 메모리에 할당을 많이 해줘야 하므로 안쓰면 지워줘야 한다.
    • 코드 내부에 아무것도 없으니까 남겨놔도 되겠지 생각하지만, 엔진은 내부가 비어있는지를 판단할 수 없기 때문에 무조건 호출함(맵핑함)
  4. Update() 내부에서 다음 함수들의 사용은 지양할것(모두C++단을 통해 찾은 뒤 C#단으로 넘겨주는 함수)
    • GameObject.xxx (Find(), FindObjectsOfType() 등..)
    • GetComponent()
    • Camera.main
  5. Debug.Log() 주의
    • 코드가 그대로 빌드에 넘어가므로 출시 전쯤에는 전처리를 이용해 비활성화 설정
  6. 정적 데이터 파싱 주의
    • XML/JSON 은 런타임시 바로 읽는건 지양하고 Scriptable Object 형태로 읽을 수 있도록 개발툴을 마련하기

본 포스팅은 'Dev Weeks : 성능 프로파일링과 최적화' Youtube 영상을 정리한 것입니다.

'Unity' 카테고리의 다른 글

rigidbody, collider 개념 및 정리  (0) 2020.10.08
IL2CPP  (0) 2020.09.29
Addressables  (0) 2020.07.28
좌표계  (0) 2020.06.09
유니티 셰이더 - UV 연산기법  (0) 2020.05.22

+ Recent posts