Version: 2019.3
코루틴
관리되는 힙에 대한 이해

에셋 검사

실제 프로젝트에서 발생하는 문제는 대부분, 임시로 “테스트”하기 위한 변경을 방치하거나 지친 개발자의 클릭 실수로 성능 저하를 유발하는 에셋을 추가하거나, 기존 에셋의 임포트 설정을 변경하는 등과 같은 명백한 실수에서 비롯됩니다.

상당한 스케일의 프로젝트에서는 인적 오류에 대한 제1 방어선을 마련하는 것이 가장 좋습니다. 예를 들어, 프로젝트에 압축되지 않은 4K 텍스처를 추가할 수 없도록 하는 짧은 코드를 작성하는 것은 상대적으로 간단합니다.

사실, 이는 놀랍게도 아주 일반적인 문제입니다. 압축되지 않은 4K 텍스처는 60MB 정도의 메모리를 차지합니다. iPhone 4S와 같은 저사양 모바일 디바이스의 경우, 180–200MB 이상의 메모리를 소모하는 것은 위험합니다. 만일 실수로 추가한 경우, 이 텍스처는 애플리케이션의 가용 메모리 중 1/4–1/3 정도를 의도치않게 차지하게 되며, 이는 진단이 어려운 메모리 부족 오류를 유발합니다.

물론 이러한 문제는 메모리 프로파일러로 발견하여 해결할 수 있기는 하지만, 이러한 문제가 애초에 발생하지 않도록 하는 것이 더 좋습니다.

AssetPostprocessor 사용

Unity 에디터에서 AssetPostprocessor 클래스는 Unity 프로젝트에 최소한의 기준을 적용하기 위해 사용할 수 있습니다. 이 클래스는 에셋을 가져오는 동안 콜백을 받습니다. 클래스를 사용하려면, AssetPostprocessor에서 상속받은 이후, 한 개 이상의 OnPreprocess 메서드를 구현합니다. 다음의 메서드들이 흔히 사용됩니다.

  • OnPreprocessTexture

  • OnPreprocessModel

  • OnPreprocessAnimation

  • OnPreprocessAudio

사용할 수 있는 더 많은 OnPreprocess 메서드는 AssetPostprocessor 페이지의 스크립팅 레퍼런스를 참조하십시오.


public class ReadOnlyModelPostprocessor : AssetPostprocessor {

   public void OnPreprocessModel() {

        ModelImporter modelImporter =

 (ModelImporter)assetImporter;

        if(modelImporter.isReadable) {

            modelImporter.isReadable = false;

            modelImporter.SaveAndReimport();

        }

    }

}

위의 간단한 예제는 프로젝트에서 AssetPostprocessor가 어떻게 규칙을 적용하는지 보여줍니다.

이 클래스는 프로젝트에 모델을 임포트하거나, 모델의 임포트 설정이 변경될 때마다 호출됩니다. 코드는 Read/Write enabled 플래그(isReadable 프로퍼티)가 true로 설정되어 있는지 단순히 확인합니다. 만약 true인 경우에는 플래그를 강제로 false로 바꾸어 저장한 후, 에셋을 다시 임포트합니다.

여기서 SaveAndReimport 메서드를 호출하면, 코드 조각이 다시 호출됩니다. 하지만, 이제 isReadable의 값은 false이므로, 이 코드로 인하여 무한 임포트 루프를 발생시키지 않습니다.

왜 이러한 변경이 일어나는지에 대한 이유는 아래의 “모델” 섹션에 설명되어 있습니다.

일반적인 에셋(Asset) 규칙

텍스처(Textures)

Read/Write enabled 플래그 비활성화

Read/Write enabled 플래그는 텍스처가 메모리에서 한 번은 GPU, 한 번은 CPU가 접근 가능한 메모리, 이렇게 두 번 메모리에 저장되게 합니다. (1) (참고: 이는 대부분 플랫폼의 경우, GPU 메모리로부터 리드백(되읽기)을 하는 것은 매우 느리기 때문입니다. GPU 메모리에서 텍스처를 CPU 코드(Texture.GetPixel 등)가 사용할 임시 버퍼로 읽어 들이는 것은 대단히 비효율적입니다.) Unity에서 기본적으로 이 설정이 비활성화되어 있기는 하지만, 실수로 이 설정이 활성화될 수 있습니다.

Read/Write Enabled 플래그는 셰이더 외부(Texture.GetPixelTexture.SetPixel API 등)에서 텍스처 데이터를 조작할 때만 필요합니다. 이를 제외하고는 최대한 사용을 피하십시오.

가능한 경우 밉맵 비활성화

카메라에 대해서 상대적으로 변동이 적은 Z 뎁스를 가지는 오브젝트의 경우, 밉맵을 비활성화하면 텍스처를 로드하는 데 필요한 메모리의 약 1/3 정도를 절약할 수 있습니다. 하지만 만일 오브젝트가 Z 뎁스를 변화시키는 경우에는, 밉맵을 비활성화하면 GPU에서 텍스처 샘플링 성능이 저하될 수도 있습니다.

일반적으로 화면에서 일정한 크기로 표시되는 UI 텍스처나 기타 텍스처의 밉맵을 비활성화 하는 것이 좋습니다.

모든 텍스처를 압축

프로젝트의 타겟 플랫폼에 적합한 텍스처 압축 포맷을 사용하면 메모리를 크게 절약할 수 있습니다.

만약 선택된 텍스처 압축 포맷이 타겟 플랫폼에 적합하지 않은 경우, Unity는 텍스처가 로드될 때 이를 압축 해제하며, 막대한 CPU 사용 시간과 메모리를 차지합니다. 이는 칩셋에 따라 상당히 다른 텍스처 압축 포맷을 지원하는 Android 디바이스에서 가장 흔히 발생하는 문제입니다.

적절한 텍스처 크기 제한 적용

텍스처 크기를 조절하는 것을 잊거나, 의도치않게 텍스처 크기 임포트 설정을 변경하는 일은 아주 흔합니다. 이를 방지하기 위해서, 서로 다른 텍스처 타입마다 적절한 최대 크기를 설정하고, 이를 코드를 통해서 적용하도록 하십시오.

많은 모바일 애플리케이션의 경우, 텍스처 아틀라스의 경우에는 2,048x2,048 또는 1,024x1,024, 3D 모델에 적용되는 텍스처의 경우에는 512x512면 충분합니다.

모델

Read/Write enabled 플래그 비활성화

Read/Write enabled 플래그는 모델의 경우에도 텍스처 에 대해 설명한 것과 동일하게 동작합니다. 하지만, 모델의 경우에는 기본적으로 활성화되어 있습니다.

만일 프로젝트가 스크립트를 통하여 런타임 시점에 메시를 수정하거나, 메시가 MeshCollider 컴포넌트의 기반으로 사용되는 경우, Unity에서 이 플래그를 활성화해야 합니다. 만일 모델이 MeshCollider에서 사용되지 않고, 스크립트에서 수정되지 않는 경우, 이 플래그는 비활성화될 수 있으며, 모델이 차지하는 메모리가 절반 가량 절약됩니다.

캐릭터 모델에서 리그 비활성화

기본적으로 Unity는 비캐릭터 모델의 경우에는 제네릭 릭을 가져옵니다. 이는 모델이 런타임 시점에 인스턴스화되는 경우, Animator 컴포넌트가 더해지게 됩니다. 만약 모델이 애니메이션 시스템을 통해서 애니메이션화되는 것이 아니라면, 모든 활성화된 애니메이터는 프레임당 한 번씩 동작되어야 하므로, 애니메이션 시스템에 불필요한 부하를 주게 됩니다.

Animator 컴포넌트의 자동 추가와 원치 않는 애니메이터를 씬에 실수로 추가할 가능성을 방지하기 위해, 애니메이션화되지 않는 모델의 리그를 비활성화합니다.

애니메이션화되는 모델에 게임 오브젝트 최적화 옵션 활성화

Optimize Game Objects 옵션은 애니메이션화된 모델의 성능에 상당한 영향을 줍니다. 만일 이 옵션이 비활성화 된 경우, Unity는 모델이 인스턴스화될 때마다 모델의 골격 구조를 복제하는 거대한 트랜스폼 계층 구조를 생성합니다. 이 트랜스폼 계층 구조는 업데이트하는 데 비용이 많이 들며, 특히 다른 컴포넌트(Particle Systems이나 Colliders)가 부착된 경우 더욱 부담이 됩니다. 또한, 멀티스레드 메시 스키닝이나 본 애니메이션 계산에 대한 Unity의 성능을 제한합니다.

모델의 골격 구조의 특정 위치가 노출되어야 하는 경우(동적으로 무기 모델을 부착할 수 있도록 모델의 손을 노출하는 경우 등), 이러한 위치는 Extra Transforms 리스트에 추가할 수도 있습니다.

자세한 내용은 Unity 매뉴얼의 모델 임포터 페이지를 참조하십시오.

가능한 한 메시 압축 사용

메시 압축을 활성화하면, 모델 데이터의 다른 채널들에 대한 부동 소숫점 수를 나타내는데 사용되는 비트 수를 줄여줍니다. 이는 정밀도가 약간 손상될 수 있으며 최종 프로젝트에서 사용하기 전에 이 부정확성의 영향을 아티스트가 확인해야 합니다.

압축 수준에 따른 비트 수의 정확한 값은 ModelImporterMeshCompression 스크립팅 레퍼런스를 참조하십시오.

채널마다 서로 다른 압축 수준을 사용할 수도 있으므로, 프로젝트에서 UV와 정점 포지션을 압축하지 않고 탄젠트와 노멀만 압축하도록 선택할 수 있습니다.

참고: 메시 렌더러 설정

프리팹 또는 게임 오브젝트에 메시 렌더러를 추가할 경우, 컴포넌트의 설정을 눈여겨 보십시오. 기본적으로 Unity는 섀도우 캐스팅과 받기, 라이트 프로브 샘플링, 반사 프로브 샘플링, 모션 벡터 계산을 활성화합니다.

만약 프로젝트에서 이들 기능 중 하나 이상 필요로 하지 않는 경우, 자동화된 스크립트를 통하여 이를 비활성화합니다. 마찬가지로 런타임 시 MeshRenderer를 추가하는 코드에서도 이 세팅을 켜거나 꺼야합니다.

2D 게임에서 그림자 옵션을 켠 상태에서 씬에 MeshRenderer를 실수로 추가하게 되면, 전체 그림자가 렌더링 루프에 전달됩니다. 이는 일반적으로 성능 낭비가 됩니다.

오디오(Audio)

플랫폼에 적합한 압축 설정

사용 가능한 하드웨어에 부합하는 오디오 압축 포맷을 사용합니다. 모든 iOS 장치는 MP3 하드웨어 압축 해제가 가능하며, 다수의 Android 디바이스는 기본적으로 Vorbis를 지원합니다.

이와 더불어, Unity에 비압축 오디오 파일을 가져오십시오. Unity는 항상 프로젝트를 빌드하는 동안 오디오 파일을 다시 압축합니다. 따라서 압축된 오디오를 가져와서 다시 압축할 필요는 없습니다. 이는 최종 오디오 클립의 품질을 낮출 뿐입니다.

오디오 클립을 모노로 설정

소수의 모바일 디바이스만이 스테리오 스피커를 지원합니다. 따라서 모바일 프로젝트의 경우, 임포트한 오디오 클립을 모노 채널로 강제로 설정하면 메모리 소비를 절반으로 줄일 수 있습니다. 이 설정은 대부분의 UI 음향 효과와 같이, 스테리오 효과가 없는 오디오 클립의 경우에도 적용할 수 있습니다.

오디오 비트레이트 축소

물론 이는 오디오 디자이너와 상의해야 하지만, 오디오 파일의 비트레이트를 최소화하는 경우 메모리 소비와 빌드된 프로젝트의 크기를 더욱 줄일 수 있습니다.

코루틴
관리되는 힙에 대한 이해