셰이더 순열이라고도 하는 셰이더 배리언트는 조건부 동작을 셰이더 코드에 도입하는 방법 중 하나입니다.
Unity는 셰이더 소스 파일을 셰이더 프로그램으로 컴파일합니다. 각 컴파일된 셰이더 프로그램에는 여러 조건에 대한 서로 다른 셰이더 프로그램 버전과 같이 하나 이상의 배리언트가 있습니다. 런타임 시 Unity는 현재 요구 사항과 일치하는 배리언트를 사용합니다. 셰이더 키워드를 사용하여 배리언트를 설정합니다.
셰이더 코드에서의 조건부에 대한 개요와 기술의 종류와 그 사용 시기는 셰이더 코드의 조건부를 참조하십시오. Unity가 셰이더 배리언트를 로드하는 방법에 대한 자세한 내용은 셰이더 로딩을 참조하십시오.
매우 많은 배리언트가 있는 셰이더는 “메가 셰이더” 또는 “우버 셰이더”라고 부릅니다. Unity의 스탠다드 셰이더는 그러한 셰이더의 한 예입니다.
셰이더 배리언트의 주요 장점은 동적 브랜치가 GPU 성능에 영향을 미치지 않고도 셰이더 프로그램에서 런타임 조건부를 사용할 수 있다는 것입니다.셰이더 배리언트의 주요 단점은 셰이더 배리언트 수가 많으면 빌드 시간과 런타임 성능 문제가 모두 발생할 수 있다는 것입니다.
Unity가 셰이더 배리언트를 만들 때 정적 브랜치를 사용하여 여러 개의 작고 특화된 셰이더 프로그램을 만듭니다. 런타임 시 Unity는 조건과 일치하는 셰이더 프로그램을 사용합니다. 즉 동적 브랜치에서 GPU 성능을 저하시키지 않고 GPU 성능을 저하시킬 수도 있는 코드에 대해 셰이더 배리언트를 사용할 수 있습니다.
하지만 대량의 배리언트는 빌드 시간, 파일 크기, 런타임 메모리 사용, 로딩 시간 증가와 같은 결과를 초래할 수 있습니다. 또한 수동으로 미리 로드(“예열”)하는 경우 복잡성이 크게 증가합니다. 프로젝트에 굉장히 많은 셰이더 배리언트가 포함되어 있는 경우 이러한 문제는 성능과 워크플로에 상당한 문제를 일으킬 수 있습니다.
경고: 실수로 무수히 많은 셰이더 배리언트를 생성하여 성능에 심각한 문제를 일으키기 쉽습니다. 따라서 Unity가 셰이더 배리언트의 수를 결정하는 방법, 컴파일에서 불필요한 배리언트를 제외(“스트립”)하는 방법, 셰이더에서 다른 조건부 타입을 사용하는 시기를 이해하는 것이 매우 중요합니다.
빌드 시 Unity는 현재 빌드 타겟의 각 그래픽스 API를 위해 1세트의 셰이더 배리언트를 컴파일합니다. 그래픽스 API와 빌드 타겟의 각 조합을 위한 배리언트 수는 셰이더 소스 파일과 셰이더 키워드 사용에 따라 달라집니다.
셰이더 배리언트의 수를 확인할 수 있습니다.
Unity는 현재 빌드 타겟의 리스트에 있는 각 그래픽스 API를 위해 1세트의 셰이더 배리언트를 컴파일합니다. 셰이더는 빌드 타겟과 그래픽스 API의 조합마다 다릅니다. 예를 들어, Unity는 Metal iOS와 Metal macOS를 위해 각각 다른 셰이더를 컴파일합니다.
일부 셰이더 프로그램이나 키워드는 특정 그래픽스 API나 특정 빌드 타겟만 타게팅할 수 있으므로, 그래픽스 API와 빌드 타겟의 각 조합을 위한 배리언트 수의 합계는 다를 수 있습니다. 단, 이러한 배리언트를 컴파일하는 프로세스는 동일합니다.
현재 빌드 타겟을 위한 그래픽스 API의 리스트를 보고 수정하려면 Player Settings 창이나 PlayerSettings API를 사용하십시오.
Unity는 현재 빌드 타겟과 그래픽스 API의 조합을 위해 컴파일할 셰이더 프로그램의 수를 정해야 합니다.
Unity는 빌드에 포함된 셰이더 소스 파일마다 몇 개의 고유한 셰이더 프로그램을 정의할지 다음과 같이 정합니다.
참고: 셰이더 소스 파일은 빌드의 씬에서 참조되었거나, Resources 폴더의 요소에 의해 참조되었거나, Graphics Settings 창의 Always-included shaders 섹션에 포함된 경우 해당 빌드에 포함된 것입니다.
Unity는 현재 빌드 타겟과 그래픽스 API를 위해 컴파일해야 하는 셰이더 프로그램을 정한 후, 각 셰이더 프로그램을 위해 컴파일해야 하는 셰이더 배리언트 수를 정합니다.
Unity는 각 셰이더 프로그램에 대해 서로 다른 배리언트가 나오는 셰이더 키워드 조합을 정합니다. 이는 다음을 구성합니다.
Unity가 셰이더 프로그램을 위해 컴파일하는 셰이더 배리언트의 수는 키워드 세트의 산물입니다. 즉, Unity는 각 세트의 요소 하나를 포함하는 각 조합마다 하나의 배리언트를 컴파일합니다.
예를 들어, 이 세트는 3가지 셰이더 배리언트 키워드를 포함합니다.
이 세트는 4가지 셰이더 배리언트 키워드를 포함합니다.
해당 셰이더 배리언트 키워드에 영향을 받는 셰이더 프로그램으로 인해 다음의 12가지 배리언트가 생성됩니다.
Unity가 컴파일하는 배리언트의 수는 셰이더 배리언트 키워드를 추가하는 대로 매우 급격히 늘어날 수 있습니다. 이러한 매우 급격한 증가를 조합적 폭발이라고 합니다.
예를 들어, 셰이더에 각각 두 개의 키워드(<feature name>_ON
, <feature name>_OFF
)가 포함된 셰이더 배리언트 키워드 세트가 여러 개 있는 일반적인 사용 사례를 가정해보겠습니다. 셰이더에 이러한 키워드 세트가 2개 있는 경우, 배리언트는 4개가 됩니다. 셰이더에 이러한 세트가 10개 있는 경우, 배리언트는 무려 1,024개가 됩니다.
컴파일 후 Unity는 같은 패스 내 동일한 배리언트를 자동으로 식별하여 이러한 동일 배리언트가 같은 바이트코드를 가리키도록 합니다. 이를 중복 제거라고 합니다.
중복 제거는 같은 패스의 동일 배리언트가 파일 크기를 늘리는 일을 방지합니다. 단, 동일 배리언트는 여전히 컴파일 중 작업 낭비를 초래하며, 런타임 시 메모리 사용 및 셰이더 로딩 시간도 증가시킵니다. 이를 감안하여 항상 불필요한 배리언트를 스트리핑하는 것이 좋습니다.