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

카테고리

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

'Optimizing/Unity'에 해당되는 글 8건

  1. 2017.06.27 MaterialPropertyBlock
  2. 2017.04.11 Mesh 최적화
  3. 2017.03.31 Dynamic Batching이 효율적일까?
  4. 2017.03.22 유니티 텍스처
  5. 2017.03.04 animation 최적화
  6. 2017.01.13 유니티에서의 메모리 관리.
  7. 2017.01.10 Unity sound setting
  8. 2016.09.07 gc.markdependencies

50x50 같은 물체가 있다고 가정하자.

이들 물체의 색은 상황에 따라 몇가지 색상으로 변경될 수 있다.

즉, 하나의 물체의 색상이 빨간색, 파란색, 노란색 등 바뀔 수 있다는 것이다.

2500개의 물체를 그리더라도 색상이 다 같다면 배치는 한번만 이루어 진다.

하지만 2500개의 물체가 다 다른 색상을 가진다면 배치는 물체의 수만큼 이루어 진다.


!테스트에 사용된 리소스와 소스파일은 가장 아래에 있습니다.!


테스트 1. 단순하게 2500개의 물체 뿌리기

Test Code

GameObject[] objects;

void Start()

{

if (sphere)

{

float colorOffset = square * square;

objects = new GameObject[square * square];

for (int xi = 0; xi < square; xi++)

{

for (int zi = 0; zi < square; zi++)

{

GameObject go = Game.CreateDynamicObject(sphere, this.gameObject.transform);

go.transform.position = new Vector3(xi, 0, zi % square);

go.name = "sphere" + xi.ToString() + "_" + zi.ToString();

objects[xi * square + zi] = go;

}

}

}

}

결과값

material : 40

setpass call : 2

draw calls : 2501

(static batching)batches : 0

참고로 테스트 화면이 기본 디폴트 화면이라서 스카이 박스나 플랜등 기본적으로 쓰는 값이 있다.


테스트 2. static batching

Test Code

..

StaticBatchingUtility.Combine(gos, this.gameObject);

..

}

결과값

첫번째 테스트에서 staticbatching만 추가했다.

머티리얼이 바뀌지 않는이상 머티리얼은 하나로 사용하기 때문에 배칭이 될 수 있다.

material : 40

setpass call : 2

draw calls : 102

(static batching)batches : 101

배칭에 관한 내용은 이곳에 정리를 한 적이 있어서 다시 자세히 언급하지는 않고 패스한다.


테스트 3. 색상 바꾸기.

Test Code

첫번째 테스트에서 material.color = ...만 추가되었다.

objects[xi * square + zi].material.color = new Color(rCol + (xi * square + zi) / colorOffset, gCol, bCol);


결과값

material : 2540

setpass call : 2501

draw calls : 2501

(static batching)batches : 0

위 결과값에서 보면 알 수 있듯이 머티리얼 갯수가 기존 40개에서 2540개로 늘어났다.


테스트 4. MaterialPropertyBlock 사용

Test Code

첫번째 테스트에서 Offset 관련 코드만 추가되었다.

go.GetComponent<SphereWithMaterialPropertyBlock>().Offset = (xi * square + zi) / colorOffset;

그리고 물체 프리팹에 아래와 같은 코드를 달아 주었다.

void Start()

{

    _propBlock = new MaterialPropertyBlock();

    _renderer = GetComponent<Renderer>();

    _renderer.GetPropertyBlock(_propBlock);

    _propBlock.SetColor("_Color", Color.Lerp(Color1, Color2, (Mathf.Sin(Time.time * Speed + Offset) + 1) / 2f));

    _renderer.SetPropertyBlock(_propBlock);

}


결과값

material : 40

setpass call : 2501

draw calls : 2501

(static batching)batches : 0

render opaque geometry : 3.26

위 결과값에서 보면 알 수 있듯이 머티리얼 갯수가 테스트 3에서 컬러바꿀 때와 다르게

2540개가 아니라 40개로 2500개가 줄었다.


테스트 5. material.color update(animation)

Test Code

업데이트 함수에서 컬러를 계속 변경해준다.

void Update()

{

float colorOffset = square;

if (objects[0] == null)

return;

for (int xi = 0; xi < square; xi++)

for (int zi = 0; zi < square; zi++)

objects[xi * square + zi].material.SetColor("_Color", Color.Lerp(Color.red, Color.yellow, (Mathf.Sin(Time.time * 1.0f + (xi * square + zi) / colorOffset) + 1) / 2f));

}


결과값

material : 2540

Render.OpaqueGeometry : 9.03

Material.SetPassUncached : 6.3

Color Change 관련 Update : 5.49


테스트 6. MaterialPropertyBlock update(animation)

Test Code

첫번째 테스트에서 Offset 관련 코드만 추가되었다.

go.GetComponent<SphereWithMaterialPropertyBlock>().Offset = (xi * square + zi) / colorOffset;

그리고 물체 프리팹에 아래와 같은 코드를 달아 주었다.

void Start()

{

    ..

   go.GetComponent<SphereWithMaterialPropertyBlock>().Offset = (xi * square + zi) / colorOffset;

    ..

}

각 프리팹의 스크립트에서 아래와 같이 업데이트 해 준다.

void Update()

 {

      _renderer.GetPropertyBlock(_propBlock);

      _propBlock.SetColor("_Color", Color.Lerp(Color.red, Color.yellow, (Mathf.Sin(Time.time * Speed + Offset) + 1) / 2f));

      _renderer.SetPropertyBlock(_propBlock);

}

그리고 셰이더 코드의 컬러값에 [PerRendererData]을 추가해 준다.


결과값

material : 40

Render.OpaqueGeometry : 2.90

Material.SetPassUncached : 없음

Color Change 관련 Update : 2.64


테스트 5와 테스트 6의 결과를 비교해 보면 Material 갯수가 2540에서 40으로 줄어들었고

렌더링 하기 위한 시간이 9.03에서 2.90으로 6.13정도 줄었다.

이는 아마 setpass 관련 시간인듯 하다.

업데이트 시간을 보면 5.49에서 2.64로 줄었다.

이제 정리를 해 보자.

Material을 접근 할 때는 Renderer.sharedMaterial과 Renderer.material 이렇게 두가지가 있다.

일반적으로 렌더링 시 sharedmaterial을 참조해서 랜더링 하는 이는 하나의 머티리얼을 공용으로 

사용하는 것이고 이를 통해 배칭이 가능하게 된다.

하지만 여기에 수정을 가해야 할 때(예를들어 머티리얼의 컬러값을 바꿔야 할 때) 생각해야 할 부분이 있다.

10개의 물체가 같은 머티리얼을 공유하는 중에 sharedmaterial.color값을 수정하면 모든 물체의 color값이 바뀐다.

이걸 원한다면 다행이지만 그렇지 않다면 material.color을 수정해야 하는데 이렇게 되면 하나의 물체의 컬러만 

바꿀 수 있긴 하지만 material을 참조하는 순간 내부적으로 sharedmaterial의 사본이 생성되어 material에 할당된다.

하지만 MaterialPropertyBlock을 사용하면 사본이 생성되지 않는다.

그리고 모든 물체들이 다시 같은 색상 파라미터값을 가지게 되면 배칭이 된다.

아래 참고 글중 "Setting MaterialPropertyBlocks breaks batching"을 인용하자면,


"배칭은 GPU로 GPU로 보내기 전에 여러 오브젝트의 지오메트리를 결합하여 하나의 객체로

렌더링하는 것을 의미한다. 당연히 모든 셰이더는 정점 데이터의 일부가 아니라 전반적으로 같아야 한다.

그래서 머티리얼 프로퍼티를 바꾸면, 어떤 짓을 하더라도, 프로퍼티가 같지 않는 한 오브젝트는 배칭이 되지 않는다.

MaterialPropertyBLock은 파라미터 값이 많이 바뀌는 많은  인스턴스가 있는 상황을 위한 것이다.

그래서 머티리얼을 복제할 필요는 없지만 동적으로 전역 셰이더를 동일한 MaterialPropertyBlock로 설정한다.

이는 드로우콜이 절약되지는 않지만 오버헤드는 절약된다. (뒤의 이야기는 질문자의 코드에서 사용번 관련 이야기)"


그리고 유니티 도큐먼트에 보면 "같은 머티리얼을 가진 여러개의 오브젝트를 그리고 싶은데 프로퍼티 값이

살짝씩 다른경우에 사용한다. 예를 들면 각 메시를 그릴 때 색사을 살짝 다르게 하는 정도며 렌더 스테이트의

변경은 지원되지 않는다. 유니티 지형 엔진은 나무를 그리기 위해 이 기능을 사용하고 있다.

모든 나무가 같은 머티리얼이지만 다른 색상, 크기, 그리고 바람 속성값을 가진다.

Graphics.DrawMesh나 Renderer.SetPropertyBlock에게 전달된 블럭은 복사되어지며, 

그래서 이 기능을 가장 효율적으로 사용하는 방법은 하나의 블럭을 만들어서 모든 DrawMesh 호출에 

재활용 하는 것이다."라고 적혀있다.


아래 링크중 "The Magic Of Material Property Blocks"을 참고하면

"셰이더 파일에 [PerRendererData] 속성으로 셰이더 프로퍼티를 명시해 줘야 한다.

이걸 추가 해 주면 이 셰이더를 사용하는 모든 렌더러에 공유되는 대신 해당 속성(예를 들면 _Color)

이 렌더러별로 설정되는 방식으로 유니티에 셰이더를 컴파일 하도록 요청한다.

그렇지 않으면 SetPropertyBlock이 작동은 하지만 내부적으로 머티리얼 인스턴스를 만들어서

renderer.material과 같은 결과가 된다." 라고 적혀 있어서 그렇게 하긴 했는데

실제로 저 [PerRendererData]를 넣는 것과 넣지 않는것에 대한 프로파일러의 변화값을 못찾겠다.


testProperty.unitypackage


Reference Link

- The Magic Of Material Property Blocks

- The Magic Of Material Property Blocks 번역

- Dynamic Batching이 효율적일까?

- unity, MaterialProperty

- Setting MaterialPropertyBlocks breaks batching

- unity, ShaderLab: Properties



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

MaterialPropertyBlock  (0) 2017.06.27
Mesh 최적화  (0) 2017.04.11
Dynamic Batching이 효율적일까?  (0) 2017.03.31
유니티 텍스처  (0) 2017.03.22
animation 최적화  (0) 2017.03.04
유니티에서의 메모리 관리.  (0) 2017.01.13
Posted by 붕대마음

댓글을 달아 주세요

Mesh 최적화

Optimizing/Unity / 2017.04.11 07:40

5.5.1p3 기준


하나씩 테스트를 해 보자.

Mesh Compression : 이 값을 높이면 메쉬 파일 크기가 줄어든다.

실제로 어떻게 차이가 나는건지 테스트 해 보자.

Mesh Compression : Off



Mesh Compression : High


뭔가 모습이 좀 달라진거 같기도 하고 아닌거 같기도 하고...

겹쳐서 보면 좀 움찔움찔 바뀌는 것 같음.


버텍스 갯수라던지 파일의 용량은 1도 안 줄어들었음.. 뭘까 분명 줄어든다고 했는데...

그런데 모양은 바뀐단 말이지....

여기 링크를 읽어보면 이렇게 적혀 있다.


"게임 파일에서 적은 공간을 차지하도록 메쉬와 임포트된 애니메이션 클립을 압축할 수 있다.

메쉬압축을 하려면, 메쉬를 선택 후 인스펙터에 있는 Mesh Compression을 Low, Medium 또는 High로 설정해라

메쉬와 애니메이션 압축은 양자화를 사용는데 이 말인 즉슨, 압축을 사용하면 공간은 덜 차지하지만

부정확성이 생길 수 있다는 것이다.

압축을 사용하고자 하는 모델에 어느정도 압축이 적합한지 테스트 해 봐라.

메쉬 압축은 단지 좀 더 작은 데이터 파일만 만들어 낼 뿐 실행시간에 더 적은 메모리를 사용하지는 않는다는 것을 알아야 한다.

애니메이션 키프레임 감소는 실행시간에 좀 더 작은 데이터 파일과 더 적은 메모리를 사용한다.

일반적으로 항상 이 기능을 켜둬라."


으잉? 데이터파일은 작아졋는데 메모리는 줄어들지 않는다고? 흐음...좀 이상하네.

아래 유니티 포럼에서 댓글을 좀 모아서 정리하자면 아래와 같다.


"이 기능은 실제로는 렌더링이나 메모리를 위한 기능이 아니라 게임 빌드 용량을 위한 기능이라고 봐야할듯하다.

즉, mesh 압축은 더 작은 메시로 만드는게 아니라 꼭지점/ 겹침 선을 제거한다.

메쉬 데이터를 저장하는 정밀도를 줄여 공간을 절약한다.

하지만, 메쉬 렌더링이 끝나면 이전과 같은 복잡성을 가지며 정확도는 약간 떨어진다.

그래서 파일 용량은 줄어들지만 렌더링시 퍼포먼스적인 그다지 이슈는 없다.


실제로 메쉬압축을 끄고 빌드한 경우 2.74메가, High로 한 경우 2.70메가가 나왔다.

우선 유니티 도큐먼트에서 말했던 더 작은 데이터 파일을 만들어 낸다는 말은 맞는말!! 

근디 좀 더 확확 해주지 0.04메가 줄다니..ㅜㅜ.


혹시나 해서 프로파일러로 메모리에 올라가는 크기를 찍어봤다.

메쉬 압축 Off : 349.3, 메쉬 압축 High : 248.2 가 나왔다.

메모리는 안 준다더니 줄어들었네??

물론 이건 위에서 만든 번들로 실행한게 아닌 로컬파일로 실행한 거긴 하지만 머 같지 않을까...


찜찜한 기분에 번들로 체크를 해 봤다.

메쉬 압축 Off : 349.3 메쉬 압축 High : 248.2

그래도 줄어들었다.


혹시나 해서 프레임 디버그로 정점갯수도 찍어봤지만 메쉬 압축을 Off로 하든 High로 하든 결과는 같았다.

그래서 내가 테스트 한 결과는 아래와 같다.

메쉬 압축 Off : 파일 용량(2.74 MB), 메모리(349.3 KB)

메쉬 압축 High : 파일 용량(2.70 MB), 메모리(248.2 KB)

테스트 환경 : pc, assetbundle. 유니티 프로파일러, 유니티 프레임 디버거 사용.


유니티 도큐먼트랑은 결과가 조금 다르게 나왔다.

내가 해석을 잘못 한걸까? ㅜㅜ.

유닛히에게 문의 좀 해봐야겠다.

(실제 테스트는 위 사진처럼 유니티의 예제 모델이 아닌 프로젝트 모델로 하였지만

글을 공개로바꾸기 위해 유니티 모델 사진으로 수정했슴.)


Read/Write Enabled : 런타임에 mesh 쓰기가 가능하도록 메모리에 복사본이 생성한다.

이 옵션을 끄면 유니티가 게임의 mesh 데이터를 언로드 할수 있기 때문에 메모리가 절약된다.

하지만 런타임에 비균등 스케일로 메쉬를 스케일링 또는 인스턴스화 하면 이 기능을 켜야하는 경우가 많다.

비균등 스케일링은 mesh 데이터를 메모리에 유지해야 하기 때문이다.

런타임에 MeshColliders를 만들고자 할 경우에도 마찬가지이다.


Optimize Mesh : 메쉬의 삼각형 순서를 결정한다.

이 기능을 사용하면 메쉬가 더 빨리 디스플레이 되며 로드시간이 줄어든다.

내부적으로 정점 캐시 영역에 대한 삼각형을 최적화 한다.


Import BlendShapes : 모프타겟 또는 정점 애니메이션.

자세한 내용은 이곳을 참고하자.

안쓰면 꺼두자. 


Normal & Tangents : Normal의 계산여부

이 옵션을 None으로 하면 게임크기가 줄어들고 Tangents 기능이 자동으로 꺼진다.

Import가 기본옵션이며 파일에서 법선을 임포트 한다.

Calculate는 Smoothing angle에 따라 법선을 계산한다.

None은 법선을 해제하므로 메시가 매핑된 법선을 사용하거나 실시간 라이팅의 영향을

받고 있는 법선인 경우는 사용하면 안된다.

쓰지않으면 None으로 설정하자.


Tangents : 탄젠트와 종법선 계산여부.

이 옵션은 위의 Normal & Tangents의 하위옵션이다.

Import는 파일에서 탄젠트와 종법선을 가져온다.

Calculate는 탄젠트와 종법선을 해제한다.

None은 탄젠트와 종법선을 해제하는데 메쉬에는 탄젠트가 없기에 법선매핑 셰이더로 사용할 수 없다.

쓰지않으면 None으로 설정하자.


Reference Link

- Mesh compression - what does it actually do?

- Does mesh compression increase performance of Unity3D games?

- Reducing the file size of your build

- Mesh

- Model

- Animation BlendShapes

-

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

MaterialPropertyBlock  (0) 2017.06.27
Mesh 최적화  (0) 2017.04.11
Dynamic Batching이 효율적일까?  (0) 2017.03.31
유니티 텍스처  (0) 2017.03.22
animation 최적화  (0) 2017.03.04
유니티에서의 메모리 관리.  (0) 2017.01.13
Posted by 붕대마음

댓글을 달아 주세요

배칭이란 무었인가?

드로우 콜(DC)을 줄이기 위한 방법중 하나로 크게 정적배칭과 동적배칭으로 나뉜다.


그렇다면 DC란 무었이고 왜 줄여야 하는가?

DC란 그냥 CPU가 GPU에게 메시를 그려달라는 명령이다.

문제는 DC 함수를 호출할 때 마다 CPU의 부하가 발생하는 작업이 이루어 진다는 것이다.

그림발췌. ARM. The Architecture for the Digital World. API Level Optimizations.


위 그림을 보면 DC가 많을수록 오버헤드와 프로세싱의 비율이 커진다.

즉, DC를 적게 호출할 수록 오버헤드와 프로세싱이 줄어든다고 생각할 수 있다.

그렇다면 오버헤드와 프로세싱은 무슨 작업인가?

이에대한 내용은 아래 ppt의 일부에서 잘 설명되어 있다.

참고 : Unite Seoul 2016 Batch! Let's take a look at Batching!

CPU는 DC호출 시 State 변경에 대한 오버헤드와 Command Buffer의 Flushing 오버헤드를 감당해야 한다.

buffer는 데이터를 하나하나 옮기는 것이 비 효율적이라 일정량을 모아서 옮기는 것을 뜻하며

flush는 버퍼에 쌓여있는 것을 다른곳에 옮기고 버퍼를 비우는 것을 의미한다.


유니티 문서에 적혀있는 설명은 아래와 같다.

다이나믹 배칭은 말 그대로 동적 오브젝트를 배칭하는 기능이다.

동적배칭은 기본적으로 자동으로 이루어 지지만 아래와 같은 조건이 있다.

1. 동적 오브젝트 배칭은 정점마다 어느정도 오버헤드가 발생하므로  총 900개의 정점 이하의 

   메시로만 적용된다.

   - 만약 셰이더에서 position, normal, 그리고 하나의 UV를 사용한다면 300개의 정점만 가능하며

     position, normal, UV0, UV1 그리고 tangent까지 사용한다면 180개 정점까지 가능하다.

2. 트렌스폼을 미러링한 오브젝트들은 배칭되지 않는다.

   (오브젝트 A의 스케일 1, 오브젝트 B의 스케일 -1일 때  이 두 오브젝트는 같이 배칭되지 않는다.)

3. 다른 머티리얼 인스턴스를 사용하면 같이 배칭되지 않는다.

4. 라이트맵이 있는 오브젝트는 추가 렌더러 파라미터(라이트맵의 인덱스나 offset/ scale)를 가지는데, 

   일반적으로 동적으로 라이트 매핑된 오브젝트가 완전히 동일한 라이트맵의 지점에 있어야 배칭된다.

5. 멀티 패스 셰이더를 쓰면 배칭이 안된다.

  - 포워드 렌더링에서 대부분의 유니티 셰이더는 몇가지 라이트를 지원하여 효과적으로 추가 패스를 수행한다.

    additional per-pixel를 위한 draw call은 배칭되지 않는다

  - Legacy Deferre(light pre-pass)렌더링 경로는 두번 그려야 하기 때문에 다이나믹 배칭이 꺼져있다.


스테틱 배칭은 움직이지 않고 동일한 머티리얼을 공유한다면 DC을 줄일 수 있고 정적배칭보다

눈에 띄게 효과가 나타난다. (게임중 움직이거나 회적, 스케일이 없다는 것을 인스펙터의 Static로 설정)


현재는 MeshRenderer와 ParticleSystem만 배칭되며 Skinned MeshRenderer은 안된다.

반투명한 셰이더의 경우 오브젝트 순서상 다른것과 겹치면 배칭되지 않을 수 있으므로

순서를 잘 설정해야 한다. 유니티는 오브젝트 순서대로 렌더링을 지시하고 배칭을 시도하기 때문이다.


이상 배칭을 하기 위한 조건들을 알아보았다.

배칭은 결국 DC을 줄이기 위해 모아서 그리는 방법이다.

배칭을 사용하면 DC은 줄어들 수 있다.


그렇다면 배칭은 무조건적으로 좋은건가?

내가 생각하는 최적화는 무었인가 잘못사용하고 있는게 없다면 결국은

상황에 맞게 하나를 얻고 하나를 잃는 선택의 연속이다.

배칭 또한 그와 같다.

정적배칭의 경우 배치된 메시의 복사본을 저장하기 위해 추가 메모리가 필요하다.

즉, 메모리가 여유롭고 렌더링 성능이 딸린다면 사용하는게 좋은 판단이 될 수 있지만

그렇지 않다면 사용을 심각하게 고민해 봐야 한다.

그나마 정적 배칭은 게임의 로딩단계에서 버텍스 버퍼를 한번 생성하면 되지만

동적배칭은 어떠할까? 한번 버텍스 버퍼를 생성하면 그만일까?

동적배칭의 경우 CPU에서 트렌스폼(변환)을 수행해야 할 수도 있는데

이 경우, CPU에서의 변환 및 버텍스버퍼 구성에 걸리는 시간과 DC 횟수

어느것을 우선시 할 건지를 생각해 봐야 한다.

모든 게임오브젝트 정점들을 CPU에서 월드공간으로 변환하여 동작하기 때문에 

DC을 하는것 보다 부하가 작다면 이점이 있다.

DC의 리소스 요청은 주로 사용하는 그래픽스 API같은 여러가지 요인에 의해 결정된다.

예를들어 콘솔이나 Apple의 Metal과 같은 현대 API에서는 DC 오버헤드가 일반적으로

매우 낮기 때문에, 그다지 동적배칭이 효율적이지 않다.


어떻게 유니티는 어떤 오브젝트가 batch(일괄처리)인지 알까?

유니티는 scene에 어떤 오브젝트를 포함시킬지, 

포함시키지 않을지를 결정하기 전에는 렌더링을 시작하지 않는다.

모든 Update(), FixedUpdate(), 또는 LateUpdate() 함수 안에서

오브젝트의 위치나 활성화 상태를 변경함으로써 이 결정을 할 수있다.

유니티의 프레임 실행순서는 이곳을 참조하면 된다.

유니티가 정확하게 무었을 렌더할지 알 때, 배치를 만든다.

배치는 GPU에게 보낼 데이터가 있는 패키지다.

보통은 패키지의 수가 적을수록 좋다.

하지만 유니티는 모든것을 하나의 배치로 묶을 수 없다.

상당히 많은 양의 지오메트리 정점들을 함께 배치할 수 있지만

배치마다 한번만 머티리얼 설정이 가능하다.

이는 배치가 같은 머티리얼을 쓰는 오브젝트들만 배치할수 있다는 것을 의미한다.


Reference Link

- unity doc , Draw call batching , English

- unity doc, GPU instancing

- 유니티 게임 엔진에서의 드로우콜이란?

- Batch! Let's take a look at Batching!

- Be aware of static and dynamic batching

- Mobile Optimization Batching in Unity

- Execution Order of Event Functions

- 모바일 게임 최적화의 정석 - 렌더링편

- OpenGL ES 2.0 "Designing a High-Performance"

-

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

MaterialPropertyBlock  (0) 2017.06.27
Mesh 최적화  (0) 2017.04.11
Dynamic Batching이 효율적일까?  (0) 2017.03.31
유니티 텍스처  (0) 2017.03.22
animation 최적화  (0) 2017.03.04
유니티에서의 메모리 관리.  (0) 2017.01.13
Posted by 붕대마음
TAG batching

댓글을 달아 주세요

원본 글 : Unity Texture compression and optimization

개인 필요에 의해 발번역한 글 입니다.

원문을 보시길 강력 추천합니다.


유니티 게임을 만들 때 어느 텍스처 압축 포맷이 가장 좋은지 일반적으로 궁금해 한다.

일반적인 기준은 "디스크에서의 크기, 차지하는 메모리, 퍼포먼스"다.

유니티 엔진은 많은 옵션을 가지고 있다 : ETC1, ETC2, DXT3, DXT5, 기타등등.

어떻게 선택해야 할까?

다른 사람들이 어떻게 포맷을 선택하는지 알기 위해 최적화 트릭에 대해 간단히 알아보자.


첫번째로 "텍스처가 GPU에서 얼마만큼의 메모리를 가질까?"를 이해해야 한다.

- 근래의 많은 엔진들은 텍스처를 메모리에 있는 것과 같은 포맷으로 디스크에 저장하여,

  텍스처가 필요로 하는 메모리와 같은 크기의 파일로 만든다.

- 비압축 256x256 크기의 이미지는 256KB. 256x256크기의 DXT1 이미지는 32KB. DXT3 또는 DXT5는 64KB.

- "안드로이드 환경에서 어떻게 PNG를 GPU 텍스처로 옮기는가" 이 글을 보면

   왜 png 텍스처를 사용하고 싶지 않은지에 대해 설명이 되어 있다.

   대신 png들을 ext1, pvr 또는 etc로 export한다.


"하드웨어 압축을 사용하는 효율적인 게임 텍스처"는 apk용량과 게임의 로딩시간을 줄이기 위해

gpu에서 지원하는 사용할 만한 압축 포맷을 검사한다.

- 위의 링크에서 볼 수 알 수 있듯이, 상위버전의 OpengGL을 지원하면  더 나은 포맷을 사용할 수 있다.

  ETC1을 대체하기 위해 고품질과 알파채널을 지원하는 고유 포멧이 있다, 

  아래 내용을 참조해라.

   . ATC는 Adreno GPU에서 사용가능 하다.

   . PVRTC는 PowerVR GPU에서 사용가능 하다.

   . DXT1 S3 DXT1 텍스처 압축. Nvidia Tegra 플랫폼에서 실행된다.

   . S3TC S3 텍스처 압축, 일반적인 DXT 변형. Nvidia Tegra 플랫폼에서 실행된다.

- 게임에서 하드웨어 가속을 받는 텍스처를 사용하면 apk, 런타임 메모리 사용 및 로딩시간을 줄일 수 있다.


"유니티 2D 텍스처 최적화"는 메모리 절약을 위해 논리적 그룹 대신 유형별 텍스처로 그룹화하는 방법에 대해 설명한다.

- 위 링크에서 보면 2048 x 2048 텍스처가 압축을 했음에도 Texture2D 메모리에서 4MB나 차지한다.

- 에셋이 렌더링되지 않고 검은색 모양으로 보인다면 디바이스의 텍스처 메모리 한계를 넘어 선 것이므로 최적화를 해야한다.

- 스프라이트 시트의 세가지 압축 타입 유형 : Solids(입체), Fades(알파 블렌딩), 그리고 Alpha punchouts(알파 테스트).

- Solid는 알파가 없는 사각형 에셋이기에, 알파를 지원하지 않는 4비트 압축을 선택하여 많은 절약을 할 수 있다.

- Alpha Punchouts는 알파값을 사용하는 사각형 이외의 모양이며, 이미지 값은 알파값이 100% 또는 0%이다.

  이 타입이서는 5bit 압축을 선택한다.

- Fades는 가변도의 알파값을 사용하는 가장 복잡한 오브젝트에 사용한다.

  이 타입은 최소한으로 사용하고 깔끔하게 잘 보이도록 최소 8bit RGBA를 사용한다.


"ECT1 vs ECT2 vs DXT5" 어느 텍스처가 가장 좋은지에 대한 논의"

- ECT2는 ES3.0을 지원하는 디바이스에서만 가능하다

- 하지만 만약 2015년형 모든 디바이스가 타겟이라면 ECT2나 DXT5 둘 다 지원하기 때문에  

  텍스처를 선택하는데 그다지 문제가 되지 않는다. ARGB에 적합한 것을 고르고 나서

  RGB에는 그냥 ETC1을 사용하면 된다.

- 대부분의 디바이스는 ETC1을 지원한다. (Texture compression support)

- 이번 주말에 ETC1 + DXT5 조합으로 바꾼 덕분에 나의 파일 용량을 25프로 줄였다.


"Android Texture Compression - a comparison study with code sample"는 

안드로이드 디바이스에서 다른 포맷(png, etc, etc2, pvrtc, s3tc..)의 용량, 퀄리티를 비교한다.


"안드로이드에서 기본 텍스처 압축 포맷으로의 ETC2"에서는 ETC2 포맷의 사용에 대해 논의한다.

- apk 파일의 용량을 줄이기 위해 DXT5를 사용한다. 만약 디바이스에서 네가 설정한 텍스처 타입을 

  지원하지 않는다면 유니티는 소프트웨어 압축해제를 사용하여 32bit RGBA 텍스처로 로드한다.

- 메모리를 절약하기 위해 ETC2를 사용한다. 만약 낮은 스펙의 디바이스에서 ETC2를 지원하지 않는다면

  ETC2를 선택하는 것은 좋지 않을 것이다.

  Unity Packer를 사용하는 대신 이미지들을 패킹하는 추가 이유는, 스스로 디더링을 할 수 있기 때문이다.


몇몇의 텍스처에서 추천하는 최적화

- 2D를 위해 밉맵을 사용하지 않는다. "스프라이트에서 밉맵을 켜는게 가능한가?

- 텍스처를 DXT1 또는 DXT5로 압축한다. "메모리 최적화 기술"

- "어느 해상도로 스프라이트를 그려야 하는가?" 2048x2048은 이미 모바일 디바이스에서 좀 큰거 같다.


"어떻게 ETC1 압축에 알파를 더할 수 있을까?" ETC1 + Alpha 채널에 대한 질문에 대답 시도.

- packing tag로 지정하여 일부 아틀라스에 배치된 스프라이트.

- ETC1 압축 사용 체크박스를 체크했는지 뿐 아니라 안드로이드 오버라이드 체크박스까지 체크.

  유니티는 결과 아틀라스를 알파없는 두 개의 텍스처로 분리한 다음 렌더 파이프라인의

   마지막 단계에서 결합한다.

- 유니티 5.3.0과 그 이전버전들에서의 UI 셰이더는 ETC1+ Alpha를 지원하지 않았다.

- UI 요소들은 ETC1과 잘 맞지 않는다. 이에 대한 버그가 있다.


"유니티 모바일 알파분리". 이 블로그는 ETC1+Alpha 문제를 해결했고

안드로이드와 IOS 디바이스 모두에 대한 해결책을 제공했다.

(대략적으로 보면 결론적으로 텍스처를 두장 쓴다는 말,

현재 내가 참여하고 있는 프로젝트에서도 이 방법을 사용하고 있고 

rgba32텍스처를 자동으로 변환해 주는 시스템을 만들었다.

아래 링크(rgba32 bit texture를 etc 4bit texture 2장으로 사용하기)를 참조하면 된다.)


ETC1과 알파채널 알파분리

https://github.com/keijiro/unity-alphamask

결국, 어찌됐든 ETC + 알파채널을 사용하는게 대부분의 경우 합리적인 품질로

최적의 빌드크기와 메모리 공간을 제공하니 이걸 사용하자.


Reference Link

- Unity Texture compression and optimization

- S3의 S3TC대 3Dfx의 FXT1

- 모바일 게임 최적화의 정석, 텍스처 압축 편

- unity 2d texture optimization

- Texture compression support

- Android Texture Compression - a comparision study with code sample

- ETC2 as default texture compression on Android

- Is it possible to turn on Mip Maps for a sprite?

- Memory optimization tricks

- What Resolution should I be painting sprites in?

- How Can I Add Alpha To ETC1 Compression?

- ETC1 + Alpha Feature

- Unity mobile Splitalpha

- 유니티 최적화 기법 - 텍스처 편

- PVRTC - the most efficient texture compression standard for mobile graphics world

- Tiling와 Offset을 활용한 2개의 압축텍스처 최적화

- rgba32 bit texture를 etc 4bit texture 2장으로 사용하기

- 모바일 게임 최적화의 정석 - 텍스처 압축 편

-


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

Mesh 최적화  (0) 2017.04.11
Dynamic Batching이 효율적일까?  (0) 2017.03.31
유니티 텍스처  (0) 2017.03.22
animation 최적화  (0) 2017.03.04
유니티에서의 메모리 관리.  (0) 2017.01.13
Unity sound setting  (0) 2017.01.10
Posted by 붕대마음

댓글을 달아 주세요

케릭터에 붙어있는 애니메이션이 많을수록 케릭터 로딩이 느려진다.

한 스테이지에 케릭터가 10개가 나온다고 가정하고, 각 케릭터가 가져야 할

애니메이션 갯수가 영웅일 경우 20개, 적군일 경우 10라고 할 경우,

애니메이션 용량이 3메가라 가정해서 영웅 5명의 애니메이션은

 5*20*3 = 300메가....헐...

적군 5명은 5*10*3 = 150메가....헐....

이건 좀 말이안되는듯..내가제대로 계산한거 맞음??? 후덜덜.....


그래서 애니메이션 최적화를 시도해보자!

이미 이 주제에 대해서는 많은 분들이 좋은 팁을 알려주셨으니 참고하면 되고

내가 할 이야기는 scale에 관한 내용이다.


최적화를 할 타겟 파일을 보자.

5메가가 넘는 엄청난 녀석이다.

엄청나게 많은 애니메이션을 하겠지 뭐....

유니티에서 애니메이션 창을 열어 왜 이렇게 용량이 큰지 살펴보다보니 우선 당연히 

플레이 타임이 긴 애니메이션임....그래그래..그건 이해가 되는데 세번째 사진을 보면

position이나 rotation은 이해가 가지만 scale?? 이건 값도 없는데 왜 들어가 있지..

그냥 1인걸 보증하기 위해서인가..??

유니티의 퍼포먼스 및 최적화 꿀팁에 보면 이렇게 나와있기도 하고....

그래서 과감히 scale을 삭제삭제..

용량이 5.21에서 1.49로 거의 70%가 줄었다.....

머지...? 너무 많이 준거아냐...난.그냥...2~30%만 줄어도 감사요~! 할려고 했는데.....

물론 scale 말고도 필요없어보이는걸 좀 제거하긴 했지만....


애니메이션 테스트를 진행해 보니 역시나 잘 나온다.

그래서 과감히 결정.

빠른 로딩을 위해 모든 애니메이션에서는 scale을 제거하고

특정 타임때 필요한 scale animation은 추가 script나 shader에서 지원.


AnimationCurve에 propertyName으로 체크하긴 했는데 모르는 이름도 있어서

적당히 대충했다. Nub이나 Editor 이런건 뭐지.. 필요없을 거 같은데...


2017.03.12 추가 테스트

아래 AnimationUtility.SetEditorCurve makes clip file size bigger?  에서 아래와 같은 글을 읽었다.

이 아저씨 말로는 짜투리정보는 에디터에서만 그런거고 실제로  빌드하면 알아서 다 제거해 준다고 한다.

그리고 inspector에 실제 애니메이션 메모리 크기가 보인다고 한다.

그런데 난 왜 안보이지? 보이지 않아서 추가 테스트를 해봤다.


최적화 전 : 68개의 파일, 총 66메가.

최적화 후(scale, editor 관련 제거) : 68개의 파일, 총 51.9메가

이렇게만 보면 꽤나 많이 절약된 걸로 보인다. 

이제 이걸로 번들을 뽑아보았다.

최적화 전 번들파일은 4.7메가, 최적화 후 번들파일은 4.4메가였다.

물론 이 번들파일에는 애니메이션 이외에도 여러가지가 들기는 하지만

0.3메가가 줄어들었다는건 어느정도 의미가 있다고 생각한다.


그리고 인스펙터는 아니지만 프로파일러에서는 애니메이션 클립의 메모리가 보인다.

최적화 전 : 670kb


최적화 후 : 329.4kb


프로파일러상에서도 용량이 줄었다.

그렇다면 내가 수행한 애니메이션 최적화가 효과가 있다는거 아닐까?


추가 용량을 줄일려면 유니티 메뉴얼 "FBX 임포터 관련 문서"를 참조해서 Rotation Error와 Position Errror

줄이는 것도 한 방법이다. 


애니메이션을 임포트할때, 모델을 IK노드는 FK안으로 구워지므로 유니티에서는

IK노드가 전혀 필요가 없다.

하지만 모델에 이 IK노드가 남아있다면 이 노드들이 애니메이션에 영향을 안 주더라도 

CPU오버헤드를 유발시킨다.

유니티나 모델링 툴에서 IK노드를 지울 수 있다.

이상적으로는, IK와 FK 하이라키를 모델링 하는 동안에는 따라 나누어 관리해야 나중에 지우기 쉽다.


버전을 올리고 나서 기존에 AnimationClipCurveData API 쓰던걸 버전문제 때문에 EditorCurveBinding으로 

바꾸는 작업을 했는데 용량이 더 커진다.

GetCurveBindings와 GetObjectReferenceCurveBindings 둘다 사용해 봤는데 마찬가지.

뭔가 내가 제대로 못하고 있는건지...

결과물이 마음에 들지 않기때문에 폐기 관련 경고는 그냥 무시하기로....


Reference Link

- 영웅의 군단의 테크니컬 아트

- 애니메이션 커브의 사용

- 애니메이션 퍼포먼스 및 최적화

AnimationUtility.SetEditorCurve makes clip file size bigger?

- 유니티 메뉴얼 FBX 임포터

- unity doc, modeling characters for optimal performance

- Unity3D Performance Optimization Codes

- unity doc, mesh importing

-

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

Dynamic Batching이 효율적일까?  (0) 2017.03.31
유니티 텍스처  (0) 2017.03.22
animation 최적화  (0) 2017.03.04
유니티에서의 메모리 관리.  (0) 2017.01.13
Unity sound setting  (0) 2017.01.10
gc.markdependencies  (0) 2016.09.07
Posted by 붕대마음

댓글을 달아 주세요

유니티에서 사용하는 메모리는 3가지 종류의 영역이 있다.


code영역

유니티 엔진과 라이브러리, 게임코드가 컴파일 되어서 디바이스의 메모리 코드 영역에 로딩되게 된다.

크게 최적화 할 필요가 없음. 코드 파일 용량이라고  해 봤자 어차피 얼마 안된다.


Managed Heap 영역

Mono(.Net Framework의 오픈소스버전)가 관리하는 영역.

instantiated object, variable, new 키워드로 할당한 메모리, 들이 거주하게 되는 영역.

Managed인 이유는 Mono Framework이 이 영역의 메모리를 할당하거나 해제하면서 관리하기 때문.


유니티 레퍼 참고 

- 자동 메모리 관리

오브젝트나 string 또는 배열이 생성될 때, 이를 저장하기 위해 요구되는 메모리는

Heap이라고 불리는 중앙 풀에서 할당되어진다.

아이템을 더 이상 사용하지 않을 떄, 한번 차지했던 메모리를 되찾아서

다른곳에 사용될 수 있다.

과거에는 프로그래머들이 힙 메모리의 블럭을 할당하고 해제하기 위해

명시적으로 함수를 호출하였다.

요즘은, 유니티의 Mono엔진과 같은 런타임 시스템이 자동으로 메모리를 관리해 준다.

자동 메모리 관리는 명시적으로 할당/해제를 하는 코딩적 노력이 적고

메모리 누수(할당하고 해제하지 않은 경우)가 발생할 가능성이 많이 감소시켜준다.


GC에게 먹이를 주지 않는 몇가지 방법을 보자.

1. string의 조합대신 mono 라이브러리가 제공하는 system.text.stringbuilder를 사용하자.

2. string의 업데이트를 최대한 줄이자.

   void Update() { string scoreText = "score : " + score.Tostring();}

   위 코드는 매번 string을 업데이트 시킨다.

   void Update() { if(score != oldscore) string scoreText = "score : " + score.Tostring();}

   위 코드는 실제 값이 다를경우만 업데이트 시킨다.

3. 함수가 배열값을 반환할 때 문제가 발생할 수 있다.

float[] RandomList(int val)

{

   var result = new float[val];

   for(int i=0; i<val; i++)

        result[i] = Random.value;

   

   return result;

}   

위의 함수는 새 배열을 만들어 값을 채울 때 무척 우아하고 편하다.

하지만 반복적으로 호출되면 매번 새로운 메모리가 할당된다.

배열이 무척 커질 수 있기 때문에 사용가능한 힙 공간이 빠르게 소모되어

GC 빈도가 높아 질 수 있다.

이를 피하는 방법은 배열의 레퍼런스를 사용하는 것이다.

배열을 함수의 인자로 전달하면 함수 내에서 배열을 수정할 수 있고

함수의 리턴 후에 결과값도 남는다. 아래에 알맞게 수정한 결과물을 참고하자.

void RandomList(float[] valArray)

{

  for(int i=0; i<valArray.Length; i++)

     valArray[i] = Random.value;

}

위 함수는 기존 함수보다 우아하지는 않지만(초기 할당이 호출하는 쪽에서 필요함)

함수 호출이 될 때마다 새로운 가비지가 생성되는 것은 없다.


4. 오브젝트 풀링 시스템을 사용하자.

5. 값만 저장하는 데이터 타입은 class보다는 구조체를 사용하자. 

   struct는 stack에 생성되기 때문에 GC에 들어가지 않는다.



GC의 호출로 인해 프레임이 떨어지는 것을 최소화 히기 위한 방법

- Object Recycling 기법 : 다시 써야 한다면 Destroy보다는 Active을 on off 시키자.

- GC를 막거나 컨트롤 할 수는 없지만 미리 GC를 호출할 수는 있다.

  좋은 타이밍에 GC를 호출해 주면 플레이 타임에 프레임 하락을 유발하는 일이 적어진다.

- 작은 Heap과 빠르고 빈번한 GC를 할 건지 큰 Heap과 느리지만 빈번하지 않은 GC를 할건지 

  자신의 프로젝트에 맞게 선택.



Native Heap 영역

유니티 엔진이 OS에서 메모리를 할당받아 texture, sound, effect, level data등을 저장하는 영역.

유니티 엔진이 Mono가 메모리를 관리하는 것 처럼 현재 scene에서 필요 없게 된

리소스가 차지하는 메모리 영역을 해제하는 작업을 수행하기도 하지만 manual하게 할당된

리소스에 대해서는 유니티가 관리를 하지 못하므로 부주의 하다면 심각한 메모리 부족사태에 이를 수 있다.


일반적으로 Scene을 로드할 때 Scene에 포함된 Asset들은 같이 메모리 상에 로드되고

Scene이 끝나서 다음 Scene으로 넘어갈 경우 이전 Scene의 리소스중 다음 Scene에 사용되지 않는 것은

메모리상에서 자동 해제 되는데 DontDestroyOnLoad일 경우는 지워지지 않는다.

그러므로 DontDestroyOnLoad로 지정한 GameObject와 그 하위 오브젝트가 무거운 asset을 

가지고 있거랑 링크하고 있지 않도록 해야 한다.


또 다른 경우는 script 내에서 scene object를 참조하고 있는 경우이다.

대부분 script의 참조는 scene이 전환됨에 따라 같이 파괴되지만

Scene이 전환되더라도 남아있는 GameObject가 있고 해당 GameObject가 Script를 가지고 있는데

이 Script 내에서 다른 Asset(ex: sound, texture...)에 대한 참조를 유지하고 있는 경우

리소스가 메모리상에 남아 메모리 자원부족에 이르게 할 위험이 있다.

singleton인스턴스가 자원에 대한 참조를 가지고 있는 경우도 마찬가지 문제가 발생할 수 있다.



GC.Collect() 

사용하지 않는(또는 레퍼런스가 없는)메모리 구역을 해제한다.


Resources.UnloadUnusedAssets()

이 함수는 Resource.Load()를 통하여 할당된 에셋을 해제한다.



Reference

- 유니티의 메모리 관리 

- Managing Memory in Unity3D

-

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

Dynamic Batching이 효율적일까?  (0) 2017.03.31
유니티 텍스처  (0) 2017.03.22
animation 최적화  (0) 2017.03.04
유니티에서의 메모리 관리.  (0) 2017.01.13
Unity sound setting  (0) 2017.01.10
gc.markdependencies  (0) 2016.09.07
Posted by 붕대마음

댓글을 달아 주세요

1. 자주 호출되고 짧은거 (3.5 이하)

Decompress On Load, ADPCM


2. 자주 호출되지만 중간거

Compressed In Memory, ADPCM


3. 거의 호출되지 않고 짧은거

Compressed, ADPCM


4. 거의 호출되지 않고 중간거

Compressed In Memory, Vorbis



BGM, Ambient Sound

Compression, Vorbis



Quality : 

100은 하지말것.

적당히 70 권장.

직접 들어보고 낮춰도 되는건 50정도로.



Reference Link

Wrong Import Settings are Killing Your Unity Game

- Unity Doc, Audio Clicp

- Audio Clip

-

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

Dynamic Batching이 효율적일까?  (0) 2017.03.31
유니티 텍스처  (0) 2017.03.22
animation 최적화  (0) 2017.03.04
유니티에서의 메모리 관리.  (0) 2017.01.13
Unity sound setting  (0) 2017.01.10
gc.markdependencies  (0) 2016.09.07
Posted by 붕대마음

댓글을 달아 주세요

어느순간 gc.markdependencies 가 엄청난 렉을 유발한다.

거의 대부분은 Resources.UnloadUnusedAssets() 함수 호출 때문.

이 함수 자체가 많은 GCCollect 시간을 요구하는데 모바일 폰에서

cpu 타임이 느려질 수 있다.


대신 수동으로 Resources.UnloadAssets("")을 사용하자.

이 함수는 GC를 유발시키지 않는다.



이 함수를 실시간을 보증하지 않는다. 

그래서 yield return 0과 같이 써주는 걸 권장한다.

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

Dynamic Batching이 효율적일까?  (0) 2017.03.31
유니티 텍스처  (0) 2017.03.22
animation 최적화  (0) 2017.03.04
유니티에서의 메모리 관리.  (0) 2017.01.13
Unity sound setting  (0) 2017.01.10
gc.markdependencies  (0) 2016.09.07
Posted by 붕대마음

댓글을 달아 주세요

최근에 달린 댓글

최근에 받은 트랙백

글 보관함