블로그 이미지
자신의 단점을 메꾸는 것을 단(鍛)이라 하고 자신의 강점을 갈고 닦는 것을 련(鍊)이라 하여, 두가지를 합친 것을 단련이라고 부른다. 붕대마음

카테고리

전체목록 (666)
참고사이트 (8)
Goal (4)
Travel (10)
My Life (104)
Game (35)
Game Review (7)
Game Plan (0)
Books (5)
English (1)
Optimizing (12)
Study (218)
유용한 것들_etc (44)
유용한 것들_func (20)
Unity (48)
Unreal (87)
작업장 (54)
RenderMonkey (6)
정리요망 (1)
따라잡기 시리즈 (0)
링크용 (0)
Total343,311
Today3
Yesterday91

'assetbundle'에 해당되는 글 2건

  1. 2017.06.03 assetbundle building, change file
  2. 2017.04.14 Asset Bundles vs. Resources : A Memory Showdown

내용은 이러함.

1. 에셋번들 빌드

2. 빌드결과 파일들 복사.

3. 프로젝트 아무것도 안 건드리고 다시 빌드

4. 두번째 빌드결과 파일들과 첫번째 빌드 파일들 비교

이상하게 몇몇 파일들이 자꾸 결과물이 다르다.

실제로 파일용량도 다르다.

대략 내용을 보면 내용은 같은데 파일의 내용 부분에서 저장 데이터 위치가 

조금씩 바뀌면서 용량도 틀려지고 하는듯 하다.

근데 왜 저장 위치가 달라지는 거지?


현재 사용 버전 : 5.5.1p3

결론 : 빌드할 때마다 특정 scene들의 파일 용량이 다르며, 실제이 조금씩 달라진다.

         그럼으로 인해 파일의 CRC 또한 달라진다.


첫번째 해결 시도

혹시 editor 관련 코드들이 random 을 만들어 내거나 하지 않을까?

editor 코드들을 제거. 끝없이 제거..딱히 정확한 연관성을 찾지 못함.


두번째 해결 시도

scene이 제대로 저장되지 않은게 아닐까?

프리팹들을 여기저기에 쓰는데 scene에서도 정보를 자체적으로 저장하니까,

그리고 이미 유니티 버전업도 몇번 진행했으니까 마이그레이션에서 문제가 있을수도 있고..

그래서 모든 신을 오픈하고 다시 저장하는 스크립트를 만들어서 돌림.

그랬더니!!!

파일 해쉬는 여전히 바뀌지만 파일 용량은 바뀌지 않음!!!

이 부분에서 유추할 수 있는 내용은 뭘까?

어느 부분이 해결되서 파일 용량이 안바뀌게 된걸까?

버전에 따른 추가정보 저장이 있는걸까?

그런데 왜 특정 scene만 저장해서 하면 계속 용량이 바뀌는 걸까?

결국 뭔가의 연관성이라는 게 있는걸까?


세번째 해결 시도

복사 붙여넣기 프리팹이 문제일까?

실제로 하나의 프리팹을 만든 후 그걸 여러군데에서 떙겨쓰고 apply하고 수정하고 하는 

일들을 많이 한다.

이는 프리팹을 쓰는 정확한 가이드 라인과 잘못 사용하는 프리팹을 찾을 수 있는 기능을

처음부터 제시하지 못한 나의 잘못. 그래서 이 부분에 대해 조사.

우선 프리팹의 문제인지 검증하기 위해 의심가는 프리팹을 링크를 끊어서 테스트.

여러번 빌드해도 해시과 변경되지 않는다!!

결국은 프리팹이 문제인 건데 여기서 다시 프리팹에 붙어 있는 특정 script가 문제 인가 하여

script를 지우면서 테스트 해 봐도 그건 아닌 듯 하다.

프리팹 링크에 대한 문제인듯 하다.

scene에 올려져 있는 세개의 프리팹의 링크를 끊었다.

a.prefab : 해당 scene에서만 사용하는 프리팹으로 editor 관련 컴포넌트가 붙어있다.

b.prefab : 몇군데의 scene에서 사용하는 프리팹으로 프리팹 안에 다른프리팹들이 여러개 들어 있다.

c.prefab : 해당 scene에서만 사용하는 프리팹으로 컴포넌트에 다른 프리팹에 대한 링크를 들고 있다.

위 프리팹들을 하나씩 다시 재 구축하며 테스트를 진행하였다.


a.prefab를 위한 테스트.

다시 프리팹으로 만들어서 테스트 : 두번의 빌드 후 scene 비교 결과 결과값이 다르다.

새로 만든 프리팹이며 하나의 신에서만 사용하는데도 신 빌드 결과값이 다름.

이 프리팹에 붙어있는 두개의 컴포넌트는 아래와 같다.

- script1 : editor script

- script2 : mono script

테스트를 진행해 봤다.

script1을 제거하고 테스트, script2를 제거하고 테스트 등 몇가지 테스트를 해 봤지만

script가 하나라도 붙어 있으면 계속 빌드를 하면 결과값이 바뀐다.

왜 script가 하나라도 붙어있으면 결과값이 바뀌는 걸까? 

다른 프리팹은 안바뀌는데... 

결과값에 영향을 안주는 프리팹과 비교해 본 결과 차이점은 결과값에 차이를 안 주는 프리팹은 

유니티 자체 컴포넌트(ex : animation, mesh 등)을 사용하고 있었다.

그래서 다시 a.prefab에 유니티 자체 컴포넌트를 붙여서 테스트 해 보니 결과값이 바뀌지 않음.

결론은....커스텀 script를 컴포넌트로 가지는 프리팹이 scene의 하이라키에 있으면 scene을 빌드할 때 문제가 생긴다??

혹시 custom하게 추가 한 스크립트 실행순서 때문일까? 모두 지우고 테스트 해 보니 이건 원인이 아님.

(지웠던 순서를 되돌리기 위해선 해당 스크립트의 메타를 고쳐야 한다. 순서는 메타에 저장된다)


여기까지의 정리를 보자면

1. scene의 하이라키에 프리팹이 올려져 있고 그 프리팹이 unity 지원이 아닌 자체 script을 컴포넌트로 가지면 

   번들을 빌드할 때 마다 파일이 조금씩 달라진다.

2. 새로 프로젝트를 만들어서 똑같이 테스트 해 본 결과 달라지지 않는다. 즉 현재 작업중인 프로젝트에 무었인가가 

   빌드시 프리팹을 건드는게 있다고 추측할 수 있다.

3. 그리고 scene의 하이라키에 missing난 컴포넌트가 있으면 결과 번들파일이 달라진다.

   그러니 missing난 컴포넌트를 잘 살펴보자.


분석 시작 3주째....

어느순간 내가 분석한 데이터가 다 필요없다는걸 깨달았다.

분명 두번 빌드해서 결과값이 같은걸 보고 연관성을 찾으려 했는데

사실 두번 빌드 했을 경우 우연히 결과값이 맞을수도, 틀릴수도 있다는 걸 알았다.

그래서 연속으로 30번 빌드하는 기능으로 바꿔서 테스트 해 보니 내가 찾았던 문제로 의심되던 것들이 

다 의미가 없어졌다. 아무런 연관성이 없다. 이런 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...

미친 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

깊은 빡침이란 이런걸까...아..미친듯이욕하고싶다...이런...

AssetBundleExtractor의 도움으로 내부 덤프파일을 일일이 검사해보니 특정파일의

SInt64 m_PathID 값이 다른걸 발견했다.

이 m_PathID를 검색해 보니 이런 을 발견했다.


작업을 위해 필요했던 것들.

1. 모든 번들을 다 빌드하면 시간이 많이 걸리기 때문에 하나의 번들과 그에 연관된 번들만

   빌드하기 위해 파일별 개별 빌드 기능을 만듬.

2. scene 파일이 많기 때문에 모든 scene을 열어서 새로 저장하는 기능이 필요해서 만듬.

3. 특정 프리팹을 지울 때 이 프리팹을 쓰는 scene들을 찾기 위한 기능을 만듬.

   모든 scene을 열어서 하이라키에 특정 프리팹을 쓰는 곳이 있는지 찾음.

4. 특정 번들만 다른이름 으로 두번 연속 빌드해서 결과값 비교 기능 만듬.

5. 모든 스크립트 재컴파일 기능 추가.

6. 모든 scene의 모든 컴포넌트를 검사하여 missing 찾는 기능 추가.



딮빡침......그래서 유니티 패치를 찾아봄.....

아...이런..딮딮딮...빡침...미친...빡침...

나의3주가...나의시간이....허무하게소비되었어.....패치를꼼꼼히읽어야지.늘생각했거늘...이런...

업그레이드 후 테스트 하니 잘 됨.

이 버그를 발견해서 수정하려고 시작한 날짜가 5.12일 이고 위 패치노트가 나온날이 5.24일 이니까

이 때 미친듯이 구글링 했을때 못찾았던건 당연한건가.......

찜찜하지만 해결하는데 한달이 넘게걸렸군.....이런 거지같은...내 인생 최대의 고난이었음...


업그레이드 하지 않고 이 문제를 피해가기 위해서는 나름 테스트한 결과를 토대로 방법을 강구하자면

scene의 하이라키상에 프리팹이 있고 이 프리팹이 유니티가 지원해주는 스크립트가 아닌것을 컴포넌트로 가지면

문제가 발생한다. 그러니 프리팹을 끊던지 컴포넌트를 없애던지 프리팹을 동적로드로 바꾸던지 해야 한다.

이건 테스트 할 당시에는 발생을 안했는데 확실성을 가지진 않는다.

그러니 업그레이드를 추천한다.


Reference Link

- [SCENE]M_ROOTORDER ORDER BEING SAVED AS A PREFABMODIFICATION 

  WITHIN THE SCENE FILE HAS AN INCORRECT VALUE

UNITY TEXT FORMAT AND M_ROOTORDER TEXT

- SOME ASSET BUNDLE COMPONENTS HAVE DIFFERENT M_PATHID COMPARED TO 

  NEW ASSET BUNDLE WITH DELETED LIBRARY

-

'Unity > Unity Study' 카테고리의 다른 글

RenderQueue  (0) 2017.06.21
There are inconsistent line endings  (2) 2017.06.20
assetbundle building, change file  (0) 2017.06.03
메모리 프로파일러  (0) 2017.05.19
Asset Bundles vs. Resources : A Memory Showdown  (0) 2017.04.14
Asset Bundle Compression  (0) 2017.03.22
Posted by 붕대마음

댓글을 달아 주세요

필요에 의해 번역한 내용입니다. 원문을 보시기를 추천합니다.


리소스 : Resources

에셋번들 : Asset Bundles


최근에 우리는 기존 방식의 리소스 시스템 사용에 대한 것과 

왜 이전 에셋 로딩이 이후 에셋 로딩보다 메모리 오버헤드가 더 높은지에 대해 많은 문의를 받았다.


본문이 너무 길어 읽지 않은 사람들을 위해 : 

만약 리소스 시스템에서 할 수 없는걸 에셋번들이 할 수 있다면 

결국에는 에셋번들의 오버헤드가 훨씬 작을 것이다.

만약 에셋번들에 익숙하지 않고 에셋번들에 대해 더 알고자 한다면 Unity Manual

Asset Bundles & Resources Guide를 읽어보길 추천한다.


바로 살펴보면, 우리는 기본적으로는 같은 내용인 버그 리포트를 몇개 받았는데,

내용은 에셋번들을 통해 에셋을 로드할 때는 메모리가 몇 메가바이트 증가하지만

리소스를 사용하면 메모리가 증가하지 않는다는 내용이다.

버그를 단계별로 재현해 보면 위의 내용과 매우 비슷한 결과를 얻을 수 있다.

일반적인 메모리는 시작때 사용되고 에셋이 로드될 때 증가하며, 원래 값으로 돌아가지 않는것을 발견했다.


에셋번들의 메모리 사용량


리소스의 메모리 사용량


위의 실제 수치들을 살펴보기 전에 이러한 수치를 나타내는 시스템과 그들 사이의 관계에 대해 이야기 해 보자.

유니티의 네이티브 메모리 시스템은 메인스레드 대 백그라운드 스레드 같이 어디에 할당되어 동작하는 타입인지, 

또는 현재 실행중인 플랫폼이 무었인지에 따라 1MB ~ 32MB(평균 1MB 에서 4MB)의 

다양한 크기의 몇몇개의 고정 크기의 블럭 할당자(메모리풀)를 사용한다 

Reserved Total(총 예약)은 OS에 의해 할당된 모든 블럭들의 합인 반면,

Used Total(총 사용)은 Reserved Total에서 유니티가 실제로 사용하는 것을 나타낸다.

FMOD, Profiler 등의 각 레이블은 해당 할당자의 설정 값이거나 시스템에서 보고된

예상 외부 메모리를 나타낸다.

레벨 영역에 대해서는 Memory Profiler 메뉴얼을 참고하면 된다.

메뉴얼 페이지에 없는 것 중 알아야 할 게 좀 있는데, Used Total과 Reserved Total은 

이 글을 쓸 당시에(Unity 5.5.0f3) FMOD 값을 포함하지 않고 있는데

이 부분은 수정하고 있는 중이다.

(앱이 시작될 때 리소스 기준으로 계산 해 보자면, Used Total : 30.1메가 = 

Unity : 21.4메가, Mono : 356킬로바이트, GfxDriver : 4.6메가, Profiler : 3.7메가)

Total System Memory Usage(총 시스템 메모리 사용 양)는 플랫폼에 의해 시스템에서 보고받은 

가상메모리 크기로 이는 모든 플랫폼의 특징은 아니며, 지원하지 않는 경우에는 0으로 표시된다.

마지막으로 Used Total은 오브젝트의 헤더나 바이트 정렬을 고려하지 않지만, Reserved Total은 고려한다.

그래서 Asset Bundles vs Resources의 메모리 사용량을 살펴보기 위해, 

유니티의 Used Total과 Reserved Total 부분을 둘 다 집중해서 살펴볼 것이다.


프로파일러에서 진행되는 작업의 이해를 위해 필요한 또 다른 정보 하나는

에셋번들과 리소스의 데이터가 디스크에 배치되는 방법이다.

리소스와 에셋번들 두 방식의 핵심은 데이터 구조가 무척 비슷하다는 것이며,

직렬화된 오브젝트를 로드하기 위한 에셋 파일 경로의 매핑과 

추가 리소스 파일의 효율적인 비동기 로딩(텍스쳐, 오디오 등)을 위해

리소스와 에셋번들 둘 다 모든 오브젝트에 대한 직렬화된 데이터를 포함하는 하나의 파일을 가진다.

에셋번들은 이러한 파일들을 아카이브에 같이 저장하고 매핑은 에셋번들 오브젝트 안에 직렬화된 데이터에 저장된다.

리소스는 매핑을 ResourceManager라는 전역 싱글톤에 저장하고 파일 자체는 디스크에서 없어진다.

게다가, 리소스 시스템과는 달리, 에셋들은 모두 같은 에셋번들에 있어야 할 필요가 없으므로

데이터를 일부만 로드하여 메모리 사용량을 보다 많이 제어할 수 있다.

좀 더 자세한 정보는 에셋번들 내부 구조에 관한 메뉴얼에서 확인할 수 있다.

(이 글을 쓰고 있는 지금도 에셋번들 내부 구조에 관한 메뉴얼 글이 계속 갱신되고 있는 중인 듯 하다.)


Asset Bundles

Resource

유니티의 메모리와 파일 시스템에 대한 지식을 활용하여, 

이제 메모리 사용량 수치가 무었을 나타내는지에 대해 좀 더 깊이있게 이야기 해 보자.

첫번째로 두드러지는 내용은 에셋번들의 Reserved Unity 영역이 10메가 증가한 반면,

(60.1메가 -> 70.1메가) Resource는 전혀 증가하지 않았다(63.3메가 -> 63.3메가)는 것이다.

왜 그럴까?

이는 실제로 앞서 언급했던 블럭 할당자 때문이다.

이 특별한 메모리 사용량 테스트를 위해 비동기 로딩 API와 코루틴을 사용하는 AsyncBundleLoader.cs 를 사용하였다.

이 조합은 현재 시점까지 아직 사용되고있지 않는 다른 블럭 할당자를 실제로 사용하기에 무척 주의해야 한다.

그래서 이 10메가의 증가는 초기 메모리 블럭 할당과, 새 오브젝트에 필요로 하는 추가 메모리로

4메가 블럭을 할당, 이렇게 두 할당이다.

두개의 새로운 할당 중, 하나는 에셋번들 비동기 로딩을 위해 2메가 블럭을 할당하고,

다른 하나는 Type Trees를 위해 4메가를 할당한다.(Type Trees에 대해 자세한건 나중에 이야기 하자)

이러한 블럭 크기들은 여러개의 Asset들을 동시에 로딩하는 것에 최적화 되어있다.

예를들어, 에셋번들 비동기 로딩을 위한 할당이나 새 블럭을 필요로 하는 Type Trees가 없어도

동시에 4~5개의 에셋번들에서 오브젝트들을 로드할 수 있다.

이는 당연히 이 번들에 에셋번들 크기와 압축 사용여부, 그리고 얼마나 많은 스크립트를 

사용했는지에 따라 다르다.


이러한 블럭 할당의 경우, Type Tree를 위한 4메가 블럭은 에셋번들에서 오브젝를 실제로 로딩할 때만 필요하다.

이 블럭은 사용후에는 사라져야 하지만, 예제에서 코루틴을 어떻게 구조화 했는지에 따라 

AssetBundleRequest 오브젝트는 여전히 사용중으로 간주되어 가비지 컬렉터에 의해 정리되지 않는다.

에셋번들 비동기 로딩을 위한 2메가는 에셋번들 아카이브 내의 버퍼를 읽는 스레드용으로

사용되며 번들에 대한 내부 참조가 없을 때 사라진다.

마지막 4 메가 블럭은 모든 오브젝트 저장에 사용되는 메인 할당자에 있기 때문에 사라지지 않는다.

일반적인 프로젝트에서는 오브젝트의 생성 및 삭제가 빈번하게 발생하므로 오브젝트를

해제하여 할당자에게 되돌려주는 대신 재사용을 위해 메모리를 풀링한다.

마지막으로 언로드된 Reserved Unity 값을 보면, 에셋번들의 값이(64.1 MB)

단지 할당자가 새 블럭을 얻는 순서때문에 Resource의 값(63.1 MB)에 매우 가깝다는 것을 알수 있다.


여태껏 Reserved 메모리에 대해 이야기 했는데, 에셋번들과 리소스 사이에

실제 Reserved 메모리를 사용하는 효율성은어떨까?

이는 유니티의 Used 영역이 바로 표시해 주기 때문에 인지하기가 무척 쉽다.

에셋번들의 경우 Reserved 메모리에서 21.7 MB를 사용하고, 

리소스는 에셋번들보다 조금 더 많은 22.2 MB를 사용한다.

게다가, 언로드 시킬 때, 이 메모리는 각각 20.7 MB와 21.2 MB로 떨어진다.

그래서 Asset Bundle이 효율적인 메모리 활용면에서는 분명하게 승리자이다.

이미 알고 있듯이, 언로딩 후 에셋번들 사용량은 시작 했을 때 보다 훨씬 커졌다.

(16.3 MB -> 20.7 MB , 4.4 MB가 커져있다.)

이전에 언급했었듯이 메모리 재사용을 위해 풀링되어 있기 때문에 에셋번들과 에셋을 

다시 로드하면 21.7 MB로  돌아간다.

리소스의 경우에는, 시작과 언로드 간의 메모리 차이는 반올림 오류 때문이다.


에셋번들을 위한 블럭 할당은 성능과 하위 호환성과의 절충으로 줄일 수 있다.

위에서 언급했듯이, 오브젝트를 로드하기에 충분한 메모리가 없기 때문에

4 메가 블럭 할당은 불가피 하다.

나머지 6 MB중 2 MB는 비동기로딩 API를 사용하기 때문에 발생한다.

그래서 블럭 할당을 피하기 위해서는, FPS 버벅거림을 감수하고 synchronous API을 사용하면 된다.

마지막 4 MB 할당은 Tree system 타입 때문이며, 위에서 언급했듯이 더 자세히 설명할 것이다.

이 시스템은 여분의 데이터를 Asset BUndle에 저장하지만, 리소스에는 저장하지 않으며,

이는 Asset Bundle을 보다 넓은 범위의 유니티 버전과 호환 가능하게 만들고

FromerlySerializedAsAttribute 와 같은 직렬화 속성을 작동시킨다.

이는 만약 새 버전으로 유니티를 업그레이드하거나 중요하지 않은 코드를 바꾸거나 할 때, 

그것들을 재빌드 하는 대신에, 게임 변경시 유저가 전체 에셋번들을 다시 다운로드 하게 할 때

동일한 에셋번들을 계속 사용할 수 있다.

이 추가 데이터를 쓰지 못하게 하려면 BuildAssetBundleOptions.DisableWriteTypeTree 옵션을

BuildPipeline.BuildAssetBundles API에 전달하면 된다.


Asset Bundles Loaded Synchronous without Type Trees


Asset Bundle 1, Resource 0 개 있다.

만약 이 데이터를 직접 재현하고자 한다면 이 블로그에서 사용된 스크립트가 Github Gist에 업로드 되어 있다.

현재 Texture, Monobehaviro, 그리고 Prefab를 100개씩 생성하도록 설정되어 있으며,

고정 랜덤화 시드를 사용하기때문에 실행 할 때 마다 동일한 결과가 나올 것이다.

(하지만 아마도 나의 결과와는 다를 수 있다.)

Asset Bundle 프로젝트에 실수로 컨텐츠가 있는 Resource폴더를 만들지 않도록 만들어야 하는데

그렇지 않다면 메모리 값이 예상보다 두배가 된다.



Reference Link

- 원문

- unity doc, The Profiler WIndow

- unity doc, Practical guide to optimization for mobiles

- unity doc, FromerlySerializedAsAttribute

- 파일 직렬화

- 파일 아카이브

- unity doc, 자동 메모리 관리를 이해하기

-

Posted by 붕대마음

댓글을 달아 주세요

최근에 달린 댓글

최근에 받은 트랙백

글 보관함