2021년 7월 21일 수요일

Unity Text(UIVertex[]) Memory Leak

 Unity Text(UIVertex[]) Memory Leak


메모리 검사를 하던 중 씬을 이동 할때 마다 uivervex[] 라는 항목이 계속 증가하는 것을 확인했는데 따로 text를 저장하는 코드가 없어서 많이 해매였다.

-테스트용 씬 스크립트-

간단한 테스트용 스크립트를 만들었다 폰트를 변경하고 버튼을 누르면 B씬을 불러오고 그 씬에서도 폰트를 변경하고 다시 A씬으로 돌아오는 것을 반복하는 스크립트이다.


-처음 프로파일러에 잡히는 uivertex-

-씬을 여러차례 이동 후에 잡히는 uivertex-

테스트 씬에서 재현을 한 후에 레퍼런스 연결을 따라가보니 FontUpdateTracker 라는 곳에서 해당 텍스트들을 전부 들고 있는 걸 확인했는데 해당 스크립트는 유니티 스크립트라 유니티에서 함수 내부를 확인하지는 못했고 깃허브를 이용해 스크립트를 찾을 수 있었다.(https://github.com/liuqiaosz/Unity/blob/master/UGUI%E6%BA%90%E4%BB%A3%E7%A0%81/UnityEngine.UI/UI/Core/FontUpdateTracker.cs)

보자마자 (static Dictionary<Font, List<Text>> m_Tracked) 해당 딕셔너리가 문제가 있는게 보였고 Add하는 부분과 Remove하는 부분을 찾아보니 몇 군데 있었지만 중요한건 enable과 disable 그리고 font를 수정할 때 add를 하거나 remove를 하는데 여기서 문제였던게

우리 게임에서는 폰트를 코드로 추가해주는데 해당 컴포넌트가 들어가있는 오브젝트가 꺼져있든 켜져있는 수정을 해주는데 이때 add가 되고 만약에 해당 오브젝트가 켜지기 전에 씬을 이동할 경우 disable이 동작하지 않기 때문에 딕셔너리에서 빠지지 않아서 계속 쌓이는 것이였다.

원인을 찾았으니 수정을 해주면된다.

-수정된 폰트 교체 코드-

여기서 FontUpdateTracker.UntrackText는 딕셔너리에서 텍스트를 빼주는 역할을 한다. 꺼져있는 텍스트의 경우 수동으로 처리를 해주는것이다.

수정 후에 재 테스트를 하였다.

-재 테스트 결과-

위에 있던 이미지와 같은 이미지가 아니다 이번에는 확실히 여러번 이동 했음에도 메모리가 정상적으로 빠지는 것을 확인 할 수 있었다.

해당 내용이 비록 작은 게임에서는 그렇게 큰 메모리 문제로 나타나지 않을 수 있으나 텍스트가 많을 경우 그리고 씬을 자주 이동할 경우 심하면 계속 같은 양 만큼 증가 하기 때문에 10mb 20mb 30mb 씩 늘어나는 경우도 있었는데 이러한 내용을 알려주지 않은 유니티에 당황스럽기도하고(어디 있는데 못찾은 걸 수도 있다.) ondestroy에 remove를 넣거나 add할때 if문 하나만 걸어줘도 방지 할 수 있는 걸 지금까지 방치한것도 문제가 있어보인다.

해당 사항은 추후에 다시 정리해서 버그리포트에 올릴 계획입니다.


















2021년 7월 18일 일요일

Unity 2019.4.28f 버그?

 이번에 유니티 버전을 4.22f에서 4.28f로 올리면서 지난버전에서는 없었던 크래쉬 이슈가 다량으로 올라왔다.

확인 결과 ios나 android 스마트폰에서는 해당 증상이 없었으나 에뮬레이터에서 발생한 것을 알 수 있었고 매번 크래쉬가 나지는 않고 간혈적으로 발생했다. 

크래쉬 로그를 뽑아서 확인 해보니깐 모두 같은 함수를 타다가 크래쉬가 났는데 유니티 자체 함수에서 발생하는 것을 알 수 있었다 바로 FindObjectOfType 와 FindObjectsOfType였고 매개변수만 다른 몇몇 개로 수정해서 테스트 해봤으나 여전히 크래쉬가 발생했고 결국 할 수 있는 선택지는 해당 함수를 제거하고 같은 기능을 다른 방식으로 구현했다.

그 결과 크래쉬는 발생하지 않았다.

특이한 점은 새로운 프로젝트를 만들어서 해당 함수를 사용했을 때는 크래쉬가 발생하지 않았다는 점인데 무언가 그 타이밍에 돌던 다른 것과 충돌하는 것일 수도 있을거 같긴하다.

아무튼 유니티는 버전이 바뀔 때마다 새로움을 준다.

---

4.28f 업그레이드 이후에 GetPixels 함수에서도 간혈적으로 크래쉬가 일어나 확인중

-21-07-19) GetPixels32 로 바꾼 후에 크래쉬는 제거 됐으나 GetPixels32를 쓰고 있는 다른 스크립트에서 memory leak이 발견되어 추가 확인중

2021년 7월 16일 금요일

Bloom Shader 최적화

 Bloom Shader 최적화

유니티의 다양한 기본 포스트 프로세싱 중에 Bloom이라는 기능이 있다.

광원에 좀 더 효과를 주는 기능인데 잘 조절 하면 이쁘게 나오지만 성능에 제법 부하를 주는 친구다. 특히 게임이 모바일이라면 더욱 더

우선 Bloom 효과를 적용하고 프레임 디버그를 이용해 확인 해보면 다음 과 같은 결과를 볼 수 있다.
-프레임 디버그로 확인한 Bloom-

여러장의 텍스처를 이용해 찍어내는데 메모리도 있지만 draw call도 상당히 증가하고 덕분에 프로파일러를 통해 확인해보면 

-프로파일러로 확인한 Bloom-


괜찮은 성능의 pc에뮬로 기본 씬에서 찍어도 0.5~1.0ms를 차지한다.
실제 개발중인 모바일게임 프로파일링 당시에는 많게는 8ms를 차지 하기도 했다.(8ms면 30프레임 기준으로 혼자서 1/4을 차지 하는 수준이다.)

그러면 이러한 문제를 어떻게 해결할 수 있을까
답은 간단하다 Bloom이 찍는 텍스처 수를 줄이면 된다.

-PostProcessPass-

Package - Universal RP - Runtime -  Passes - PostProcessPass.cs
해당 스크립트의 50번째 줄 정도를 보면 k_MaxPyramidSize라는 변수가 있다.

해당 변수가 Bloom효과를 적용할 때 횟수를 정해준다 과감하게 2로 줄인다.


-2로 줄인 후의 결과-

Draw call은 22 -> 4로 감소 하였고, 0.5ms -> 0.1ms로 감소 하였다.
실제 모바일게임에서는 8ms에서 1ms까지 감소하는 효과를 보여주었다.

물론 해당 방법을 사용하게 되면 적용되는 Bloom효과가 다소 다르게 보여지기 때문에 수치 조절은 다시 해줘야한다.

해당 기능 성능이 다소 부담되기도 하고 줄여도 어느 정도 수치 조절로 커버가 가능한 모바일 게임에서 유용하게 사용 할 수 있을 것으로 보인다.

해당 수정사항들은 패키지 파일의 스크립트를 수정 하는 것이니 패키지 파일을 Package 폴더로 따로 이동해 custom하게 사용을 해야한다.(그렇지 않으면 유니티를 재부팅하면 원상복구된다.)

2021년 7월 13일 화요일

쉐이더 메모리 최적화

쉐이더 메모리 최적화

쉐이더에는 multi_compile와 shader_feature 라는 기능이 있다.

쉐이더의 베리언트를 나눠서 하나의 쉐이더에서 여러 기능을 구현 할 수 있도록 해주는 것인데 둘의 차이라면 multi_compile는 모든 분기가 빌드에 포함되고 shader_feature 사용중인 분기만 빌드에 포함된다는 것이다.

그러면 shader_feature 만 쓰면 된다고 생각 할 수도 있지만 런타임중에 분기가 바뀌는 경우에는 shader_feature 의 경우 빌드에 포함되지 않았기 때문에 동작이 제대로 되지 않는다.

그렇다고 multi_compile를 전부 쓰면 쉐이더의 베리언트가 배로 계속 늘어 무식하게 커지게 된다.

pc게임이라면 어느정도 사이즈는 신경 쓰지 않을 수 있지만 회사에서 모바일 게임을 개발 중이기 때문에 민감하지 않을 수 가 없었다.

-Unity Urp Lit Shader-

빌트 인 쉐이더의 standard 쉐이더를 대체하는 urp의 기본 lit 쉐이더의 기본 베리언트들이다.
일반적인 게임에서는 그냥 사용해도 별 무리가 없으나 해당 쉐이더를 개조해서 게임에서 사용중이 였기 때문에 multi_compile로 더늘고 최종본에서는 최대 140mb 가까이 메모리를 먹었었다.

이 경우 필요없는 베리언트들을 줄이는 작업을 해주면 좋다.

-수정된 Lit Shdaer-

_MAIN_LIGHT_SHADOWS_CASCADE
캐스케이드 관련 키워드이다 모바일게임이다 보니 사용하지 않아 지웠다.

_ADDITIONAL_LIGHTS
_ADDITIONAL_LIGHTS_VERTEX는 사용하지 않아서 지웠다.

_ADDITIONAL_LIGHT_SHADOWS
추가 라이트의 그림자는 지원하지 않아서 지웠다.

_MIXED_LIGHTING_SUBTRACTIVE, DIRLIGHTMAP_COMBINED, LIGHTMAP_ON
라이트맵 관련 키워드인데 라이트맵을 사용하지 않아 지웠다.

multi_compile_instancing
GPU instancing 기능을 사용할 때 사용하는 키워드인데 어차피 srp를 사용할거기 때문에 제거했다.(terrain의 경우 srp적용이 안되서 terrain lit 쉐이더를 수정할 경우 남겨둔다.)


기본 lit 쉐이더에서 해당 작업을 진행 할 경우 베리언트 수가 9로 줄어 들게 된다.
모든 기본 쉐이더에서 해당 작업을 진행하면 제법 쏠쏠한 이득을 챙길 수 있다.

해당 수정사항들은 패키지 파일의 스크립트를 수정 하는 것이니 패키지 파일을 Package 폴더로 따로 이동해 custom하게 사용을 해야한다.(그렇지 않으면 유니티를 재부팅하면 원상복구된다.)








2021년 7월 12일 월요일

유니티 어드레서블 그룹 정리 툴

어드레서블 정리 툴 

사용 하던 유니티에 어드레서블을 적용하고 나서 몇몇 문제들이 있었습니다.

그중에 내가 직접 넣은 에셋말고 그 에셋에 포함되서 들어오는 에셋들 때문에 그룹 업데이트가 빈번하게 되서 암시적으로 오는 에셋들을 전부 분리해서 에셋으로 빼니깐 양이 많아져서 그룹 정리하는게 일일이 손으로 하기 힘들어서 툴을 만들게 되었습니다.

암시적으로 들어가 있는 에셋들을 분리 해주는 툴은 어드레서블 패키지를 한줄 수정해야하고 좀 불안정해서 그룹핑 하는 툴만 올려봅니다.


 -어드레서블 그룹핑 툴-

사용법 및 스크립트는 깃허브에 있습니다.

에디터 폴더에 넣으시면됩니다.

github : https://github.com/do-won-kim/AddressableGrouping


2021년 7월 10일 토요일

어드레서블과 아틀라스(레거시) 최적화 문제

어드레서블과 아틀라스(레거시) 최적화 문제


-테스트 용으로 빌드한 apk의 메모리 프로파일러 같은 아틀라스가 3개 올라와있다.-


 메모리 최적화를 위해 메모리 프로파일링을 하던 도중 아틀라스가 중복되서 올라오는 현상을 발견했다.

워낙 큰 아틀라스라 처음에는 여러장 있는 건줄 알고 있었으나 같은 아틀라스인 걸 확인 후에 레거시는 새로나온 V2처럼 수동조작으로 하는게 아닌데 왜 중복으로 올라가는지 찾아본 결과

어드레서블을 이용해 번들을 나눌 때 파일 형식 별로 갯수와 용량을 맞추었는데 아틀라스가 같은 스프라이트가 번들이 나눠져 있을 경우 나눠진 만큼 아틀라스도 중복되서 올라가는 것이였다.

-같은 아틀라스를 쓰는 스프라이트를 같은 번들에 모두 넣었을 때 메모리가 하나만 올라갔다-

스프라이트의 경우 아틀라스가 없으면 일정 갯수대로 있으면 아틀라스 별로 번들 그룹을 정하는 것으로 해결


- 참고1 테스트용 apk를 만들면서 이상한 점을 발견했는데 빈 프로젝트에 아틀라스 패킹을 하고 번들을 찢어 놓으면 메모리가 여러개 올라가지 않고 하나만 올라갔다는 점이다.

이상해서 이것 저것 테스트 하던 와중 번들내에 있는 스프라이트를 하나 어드레서블 로드를 했을 때 갑자기 여러장이 올라가서 이게 문제 인가 했지만 테스트 했던 코드를 모두 지우고 다시 빌드 했을 때도 여전히 여러장의 메모리가 올라가는 것을 확인했다.

유니티는 정말 봐도 봐도 신기하다는걸 이번 테스트로 다시 확인 했다.

결국 정확한 원인은 파악하지 못했지만 해결 방법은 같은 아틀라스를 쓰는 스프라이트들은 같은 번들에 넣는 걸로 해결이 가능하였다.

- 참고2 해당 문제는 apk빌드 후에 프로파일링을 해야 발견 됐다 에디터에서는 간혹 캐쉬가 남아서 한두개 더 찍히기도 했지만 껐다가 키면 다시 아틀라스가 하나씩만 메모리에 올라가는 것을 확인 하였다.