Unity 검색

Improvements to shader build times and memory usage in 2021 LTS | Hero image
Improvements to shader build times and memory usage in 2021 LTS | Hero image
공유

Is this article helpful for you?

Thank you for your feedback!

Unity의 SRP(스크립터블 렌더 파이프라인)가 점점 더 많은 기능을 제공하게 되면서, 빌드 시 처리하고 컴파일하는 셰이더 배리언트의 양도 증가하고 있습니다. 추가 그래픽스 API에 대한 지속적인 지원 및 타겟 플랫폼의 증가와 더불어 SRP 개선 사항도 지속적으로 확장되고 있습니다.

셰이더가 초기('클린') 빌드 후에 컴파일되고 캐시되므로, 추가 증분('웜') 빌드의 속도를 높일 수 있습니다. 클린 빌드에 걸리는 시간이 가장 긴 것은 일반적이지만, 웜 빌드 시간이 길어지는 것은 프로젝트 개발 및 반복 작업 과정에서 흔히 발생하는 문제점입니다.

Shader variant processing and compilation during project build
프로젝트 빌드 중 처리 및 컴파일되는 셰이더 배리언트

유니티의 Shader Management 팀은 의미 있고 확장 가능한 솔루션을 제공하여 이러한 문제를 해결하고자 많은 노력을 기울여 왔습니다. 그 결과 Unity 2021 LTS 이상 버전으로 제작한 프로젝트의 경우, 셰이더 빌드 시간과 런타임 메모리 사용량이 상당히 감소했습니다.

영향을 받는 버전, 백포트, 내부 테스트 결과 등 새로운 최적화에 대해 자세히 알아보려면, 바로 셰이더 배리언트 사전 필터링동적 셰이더 로드 섹션으로 이동하여 확인해 보세요. 이 블로그의 마지막 부분에서는 프로젝트 저작, 빌드, 런타임 전반에 걸쳐 셰이더 배리언트 관리를 더욱 개선하기 위한 향후 계획도 소개합니다.

Unity 셰이더 시스템의 개선 사항을 살펴보기 전에, 먼저 조건부 셰이더 컴파일, 셰이더 배리언트, 셰이더 배리언트 스트리핑의 개념을 간단하게 살펴보도록 하겠습니다.

조건부 셰이더 기능

개발자와 아티스트는 조건부 셰이더 기능을 통해 스크립트, 머티리얼 설정뿐만 아니라 프로젝트 및 그래픽스 설정을 사용하여 셰이더의 기능을 편리하게 제어하고 변경할 수 있습니다. 이러한 조건부 기능을 활용해 프로젝트 저작을 간소화할 수 있으므로, 저작 및 유지 관리해야 하는 셰이더의 수를 최소화하여 프로젝트를 효율적으로 확장할 수 있습니다.

A Clear Coat material feature enabled by the artist at authoring time, by enabling a shader_feature keyword
shader_feature 키워드를 활성화하여 저작 시 아티스트가 사용할 수 있는 Clear Coat 머티리얼 기능

조건부 셰이더 기능은 다양한 방법으로 구현할 수 있습니다.

  • 정적(컴파일 시) 브랜치
  • 셰이더 배리언트 컴파일
  • 동적(런타임) 브랜치

정적 브랜치를 사용하면 런타임의 브랜치 관련 셰이더 실행 오버헤드를 방지할 수 있지만, 컴파일 시 조건을 평가한 후에는 사용할 수 없으며 런타임 제어 기능을 제공하지 않습니다. 한편 셰이더 배리언트 컴파일은 추가적인 런타임 제어 기능을 제공하는 정적 브랜치의 한 형태입니다. 런타임에 최적의 GPU 성능을 유지하기 위해, 가능한 모든 정적 브랜치 조합에 대해 고유한 셰이더 프로그램(배리언트)을 컴파일하는 방식입니다.

이러한 배리언트는 shader_featuremulti_compile 셰이더 키워드를 통해 셰이더 기능을 조건부로 선언하고 평가하여 만듭니다. 이 경우 활성 키워드와 런타임 설정에 따라 런타임에 올바른 셰이더 배리언트가 로드됩니다. 추가 셰이더 키워드를 선언하고 평가하면 빌드 시간, 파일 크기, 런타임 메모리 사용량이 증가할 수 있습니다.

동시에 동적(균일 변수 기반) 브랜치를 사용하면 셰이더 배리언트 컴파일의 오버헤드를 완전히 피할 수 있으므로 빌드 속도를 높이고 파일 크기와 메모리 사용량을 모두 줄일 수 있습니다. 이러한 방식으로 개발 중에 반복 작업을 더 원활하고 빠르게 진행할 수 있습니다.

반면, 동적 브랜치는 셰이더의 복잡도와 타겟 디바이스에 따라 셰이더 실행 성능에 큰 영향을 줄 수 있습니다. 브랜치의 한쪽이 다른 한쪽보다 훨씬 복잡한 비대칭 브랜치는 성능에 부정적인 영향을 줄 수 있습니다. 셰이더를 더 간단한 경로로 실행해도 더 복잡한 경로에서 성능 저하가 발생할 수 있기 때문입니다.

자체 셰이더에 조건부 셰이더 기능을 도입할 때는 이러한 접근 방식과 장단점을 염두에 두어야 합니다. 더 자세한 내용은 셰이더 조건부, 셰이더 브랜치, 셰이더 배리언트 기술 자료를 참고하세요.

셰이더 배리언트 스트리핑

셰이더 배리언트 스트리핑은 셰이더 처리 및 컴파일 시간이 늘어나는 현상을 줄이기 위해 사용됩니다. 셰이더 배리언트 스트리핑의 목적은 다음과 같은 요소에 따라 컴파일에서 불필요한 셰이더 배리언트를 제외하는 것입니다.

  • 포함된 머티리얼과 활성화된 키워드
  • 프로젝트 및 렌더 파이프라인 설정
  • 스크립터블 스트리핑

셰이더 배리언트를 열거할 때, 에디터에서는 빌드에 포함 및 참조되는 머티리얼에 의해 활성화되지 않았으며 shader_feature로 선언된 모든 키워드를 자동으로 필터링합니다. 결과적으로 이러한 키워드는 추가 배리언트를 생성하지 않습니다.

예를 들어 Clear Coat 머티리얼 프로퍼티가 Complex Lit URP Shader를 사용하는 머티리얼에서 활성화되지 않은 경우, Clear Coat 기능을 구현하는 모든 셰이더 배리언트는 빌드 시 안전하게 제거됩니다.

한편, multi_compile 키워드는 사용 가능한 플레이어 설정과 스크립트에 따라 개발자와 플레이어가 런타임에 셰이더의 기능을 자유롭게 제어할 수 있도록 합니다. 반대로 이러한 키워드는 에디터에서 shader_feature 키워드와 같은 수준으로 자동 제거할 수 없다는 단점이 있습니다. 따라서 일반적으로 더 많은 배리언트를 생성합니다.

스크립터블 스트리핑은 C# API로, 런타임에 필요하지 않은 키워드와 조합을 통해 빌드 중에 컴파일에서 셰이더 배리언트를 제외하도록 합니다. 렌더 파이프라인은 스크립터블 스트리핑을 활용하여 프로젝트의 렌더 파이프라인 설정과 빌드에 포함된 품질 에셋에 따라 불필요한 배리언트를 제거합니다.

 낮은 품질높은 품질배리언트 멀티플라이어
주요 광원/그림자 드리우기:끄기켜기2x
주요 광원/그림자 드리우기:켜기켜기1x
주요 광원/그림자 드리우기:끄기끄기1x

에디터의 셰이더 배리언트 스트리핑 효과를 극대화하려면 런타임에 활용되지 않는 모든 그래픽스 관련 기능과 렌더 파이프라인 설정을 비활성화하는 것이 좋습니다. 셰이더 배리언트 스트리핑에 대한 자세한 내용은 공식 기술 자료를 참조하세요.

셰이더 배리언트 사전 필터링

셰이더 배리언트 스트리핑을 사용하면 빌드의 렌더 파이프라인 품질 에셋 등의 요소를 기반으로 컴파일되는 셰이더 배리언트의 양이 상당히 줄어듭니다. 하지만 현재 스트리핑은 셰이더 처리 단계의 마지막에 수행됩니다. 가능한 모든 배리언트를 단순히 열거하는 작업은 컴파일과 상관없이 여전히 많은 시간이 소요될 수 있습니다.

유니티는 셰이더 배리언트 처리 및 프로젝트 빌드 시간을 줄이기 위해 엔진의 빌트인 셰이더 배리언트 스트리핑에 상당한 최적화 기능을 추가했습니다. 셰이더 배리언트 사전 필터링을 사용하면 클린 빌드와 웜 빌드 시간을 크게 단축할 수 있습니다.

이러한 최적화는 렌더 파이프라인 설정에 의한 사전 필터링 속성에 따라 multi_compile 키워드를 조기에 제외하는 방식으로 이루어집니다. 이 방식을 사용하면 잠재적인 스트리핑과 컴파일을 위해 열거되는 배리언트의 양이 줄어들기 때문에 셰이더 처리 시간이 감소합니다. 가장 극적인 예시에서는 웜 빌드 시간이 최대 90%까지 줄어들기도 합니다.

셰이더 배리언트 사전 필터링은 2023.1.0a14에 처음 도입되었으며, 2022.2.0b152021.3.15f1로 백포트되었습니다.

Shader processing time reduction for warm project builds in URP Boat Attack
URP 보트 어택의 웜 프로젝트 빌드에 대한 셰이더 처리 시간 단축
Shader processing time reduction for warm project builds in URP Terrain Demo
URP 터레인 데모의 웜 프로젝트 빌드에 대한 셰이더 처리 시간 단축

같은 원칙을 적용하면 배리언트 사전 필터링은 초기/클린 빌드 시간 단축에도 도움이 됩니다.

Shader processing time reduction for clean project builds in URP Terrain Demo
URP 보트 어택의 클린 프로젝트 빌드 시간 단축을 위한 셰이더 배리언트 사전 필터링
Shader processing time reduction for warm project builds in URP Terrain Demo
URP 터레인 데모의 웜 프로젝트 빌드에 대한 셰이더 처리 시간 단축

동적 셰이더 로드

그동안 Unity 런타임은 씬과 리소스를 로드하는 동안 모든 셰이더 오브젝트를 디스크에서 CPU 메모리에 우선적으로 로드했습니다. 대부분의 경우, 구축된 프로젝트와 씬에는 애플리케이션 런타임 동안 특정 시점에 필요한 것보다 더 많은 셰이더 배리언트가 있습니다. 많은 양의 셰이더를 사용하는 프로젝트라면 런타임 시 셰이더 메모리 사용량이 증가하는 경우가 많습니다.

동적 셰이더 로드는 셰이더 로드 동작과 메모리 사용량에 대해 세분화된 사용자 제어 기능을 제공하여 이 문제를 해결합니다. 이 최적화를 통해 셰이더 데이터 청크를 메모리로 스트리밍할 수 있을 뿐만 아니라, 런타임에 더 이상 필요하지 않은 셰이더 데이터를 사용자가 제어하는 메모리 할당량에 따라 제거할 수 있습니다. 이를 통해 메모리 할당량이 제한된 플랫폼에서 셰이더 메모리 사용량을 상당히 줄일 수 있습니다.

이제 에디터의 플레이어 설정에서 새로운 Shader Variant Loading Settings를 사용할 수 있습니다. 이 설정을 사용하여 로드되는 최대 셰이더 청크 수와 셰이더당 청크 크기(MB)를 오버라이드할 수 있습니다.

Editor > Project Settings > Player > Shader Variant Loading Settings
에디터 > Project Settings > Player > Shader Variant Loading Settings

이제 다음 C# API를 사용할 수 있으므로, 아래와 같은 에디터 스크립트를 사용하여 Shader Variant Loading Settings를 오버라이드할 수 있습니다.

또한 런타임에 C# API를 사용하여 Shader.maximumChunksOverride를 통해 로드된 셰이더 청크의 최대량을 오버라이드할 수도 있습니다. 이렇게 하면 런타임에 쿼리된 사용 가능한 전체 시스템그래픽스 메모리 등의 요소에 따라 셰이더 메모리 할당량을 오버라이드할 수 있습니다.

동적 셰이더 로드는 2023.1.0a11에 처음 도입되었으며, 2022.2.0b10, 2022.1.21f1, 2021.3.12f로 백포트되었습니다. URP(유니버설 렌더 파이프라인)의 보트 어택(Boat Attack)에서는 셰이더의 런타임 메모리 사용량이 315MiB(기본값)에서 66.8MiB(동적 로드)로 78.8% 감소한 것으로 나타났습니다. 이 최적화에 대한 자세한 내용은 공식 공지사항에서 확인할 수 있습니다.

Dynamic shader loading utilized in URP Boat Attack, leading to a 78.8% reduction in runtime shader memory usage.
동적 셰이더 로드를 활용하여 런타임 셰이더 메모리 사용량을 78.8% 줄인 URP 보트 어택

향후 계획

앞서 언급한 주요 변경 사항 외에도 유니티는 유니버설 렌더 파이프라인의 셰이더 배리언트 생성 및 스트리핑을 개선하기 위해 계속 노력하고 있습니다. 또한 Unity의 셰이더 배리언트 관리 전반에 대한 추가적인 개선 사항도 검토 중입니다. 유니티의 최종 목표는 셰이더 빌드와 런타임 오버헤드를 최소화하면서 점점 늘어나는 기능을 원활하게 지원하는 것입니다.

현재 검토 중인 개선 사항 중에는 유사한 배리언트에서 셰이더 리소스 중복을 없애고 셰이더 키워드와 Shader Variant Collection API를 전반적으로 개선하는 것이 포함됩니다. 개선 목표는 셰이더 배리언트 처리 및 런타임 성능과 관련하여 더 많은 유연성과 제어 기능을 제공하는 것입니다.

앞으로를 위해, 유니티는 에디터 내부에서 사용할 수 있는 셰이더 배리언트 추적 및 분석용 툴을 개발하여 셰이더 배리언트 사용에 관한 다음과 같은 세부 정보를 제공하고자 합니다.

  • 가장 많은 배리언트를 생성하는 셰이더와 키워드
  • 컴파일되었으나 런타임에 사용되지 않는 배리언트
  • 제거되었으나 런타임에 필요한 배리언트

많은 분들의 피드백이 지금까지 가장 중요한 솔루션의 우선순위를 정하는 데 큰 도움이 되었습니다. 유니티의 공개 로드맵을 확인하고 가장 필요한 기능에 투표해 주시기 바랍니다. 원하는 추가 변경 사항이 있다면 언제든지 기능을 요청하거나, 셰이더 포럼에서 팀에 직접 문의해 주세요.

2022년 12월 28일 엔진 & 플랫폼 | 13 분 소요

Is this article helpful for you?

Thank you for your feedback!

관련 게시물