본문 바로가기

유니티 최적화

[유니티 최적화] 앱 다운로드 크기 최적화 #9 ( 쉐이더 Shader 용량 최적화 )

쉐이더( Shader ) 용량 최적화

쉐이더는 게임 상에서 사용하지 않는 쉐이더라도 프로젝트 폴더안에 있으면 컴파일 후에 빌드에 포함되게 된다.

쉐이더의 개수가 많다면 빌드 시간과 용량이 증가하는 원인이 된다.

확실히 사용하지 않는 쉐이더 파일 이라면 (.shader) 제거하는 것이 좋다.

 

보통 쉐이더의 용량은 크지 않은데 '쉐이더 배리언트'라는 방식이 용량 증가의 원인이 된다.

 

GPU는 대표적인 SIMD 병렬처리 장치인데

Single Instruction Multiful Data 라는 것의 약자로 같은 연산을 여러 데이터에 대해서 동시에 병렬처리 하는 장치라는 뜻이다.

 

그래픽 연산을 하는 과정에서는 벌텍스 데이터(Vertext Data) 나 픽셀 데이터( Pixel )에 대한 병렬 처리가 일어나게 된다.

간략한 GPU 연산과정, Vertex 와 Fragment(Pixel)에 대한 처리가 일어난다



만약 분기문이 있는 상황에서 인접한 픽셀별로 다른 분기의 코드가 실행 된다면?

1번 픽셀의 연산이 끝났음에도 불구하고 2번픽셀의 연산이 끝나기를 기다리는 시간이 발생하게 되고

성능 저하의 원인이 된다.

 

if{ 

   //1번 픽셀

   //연산 10개

else

{

   //2번 픽셀

   // 연산 10000개

}

 

1번 픽셀의 연산이 작아서 빠르게 수행 되지만 병렬처리를 위해 2번 픽셀의 연산이 끝날때까지 기다려야 하는 일이 발생.

 

때문에 HLSL ( 유니티에서 사용하는 쉐이더 언어 ) 에서는 렌더링 옵션별로 쉐이더를 구분해서 생성하는

쉐이더 베리언트( Shader Variant )방식으로 쉐이더를 컴파일 하게 된다.

 

문제는 유니티에서 지원하는 렌더링 기능들이 많아지면서 쉐이더 배리언트가 너무 많이 생성되는 경우가 있다.

쉐이더 배리언트는 

#pragma multi_compile와 같은 전처리 구문을 선언하면 자동으로 생성되는데

자세한 내용은 유니티 가이드에서 확인

https://docs.unity3d.com/kr/2021.2/Manual/SL-MultipleProgramVariants.html

 

HLSL에서 셰이더 키워드 선언 및 사용 - Unity 매뉴얼

이 페이지는 HLSL 코드에서 셰이더 키워드를 사용하여 작업하는 방법에 대한 정보를 포함합니다. 셰이더 키워드에 대한 일반적인 안내는 셰이더 키워드를 참조하십시오. 셰이더 그래프에서 셰이

docs.unity3d.com

 

예시로 몇가지를 보자면 

#pragma multi_compile_fog

는 유니티 상에 fog 기능을 사용할때 쉐이더 배리언트를 생성하는 선언문

 

#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON

#pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
#pragma multi_compile _ SHADOWS_SHADOWMASK

라이트 맵핑을 사용할때 배리언트를 생성

 

등등인데 문제는 분기별로 생성하다 보니 단순히 계산해서 10개의 배리언트를 선언하면

2의 10승 만큼의 쉐도우 배리언트가 생성되기때문에 용량이 급격히 증가하게 된다.

 

무분별하게 선언했을때는 한개의 쉐이더에서 배리언트를 6만개까지 생성되는 것을 확인했다( 쉐이더 용량 10Mb )

혹시 에셋스토어에서 받은 쉐이더가

게임내에서 사용하지 않는 기능에 대한 배리언트를 생성 되고 있다면 선언문을 제거하는 것을 추천

 

비슷한 기능으로

#pragma shader_feature

 

구문이 있는데 이는 빌드시 해당되는 머테리얼이 없으면 기능에 대한 배리언트는 생성하지 않는다고 한다.

 

머테리얼 설정에 따라 사용될 수도 있는 기능은 #pragma shader_feature 로 선언하고

 

머테리얼이 아닌 전역 쉐이더 설정은  #pragma multi_compile 로 선언하라는 유니티에서 가이드 하고 있다.

 

결론은 용량 최적화를 위해 사용하지 않는 기능들에 대한 쉐이더 배리언트를 제거하는 것을 추천.