
# 언리얼 엔진 셰이더 시스템 개선: 요약
## 발표자 소개
* **이름:** Dan Oxnidis
* **소속:** 언리얼 엔진 렌더링 아키텍처 팀
* **주요 업무:** 셰이더 시스템, 재질 시스템 등
## 발표 개요
이번 발표는 언리얼 엔진 셰이더 시스템의 주요 변경 사항, 셰이더 수 증가의 원인, 프로젝트에서 셰이더 수를 줄이는 전략, 그리고 엔진 업데이트를 통해 이루어진 시스템 개선 사항을 다룹니다.
## 1. 동기 (Motivation)
* **핵심 문제:**
* 수년간 지속된 방대한 셰이더 수와 긴 컴파일 시간
* 프로젝트 빌드 시 셰이더 재컴파일 문제 발생
* 빌드 타임아웃으로 인한 QA 빌드 지연 및 워크플로우 방해
* **근본 원인:**
* 잦은 셰이더 코드 변경으로 인한 DDC(Data Driven Caching) 메커니즘 과부하
* 단순한 코드 변경(공백, 주석 수정 등)으로 인한 전체 셰이더 재컴파일 발생
* **임시 해결책 (비효율적):**
* 주말 셰이더 제출 제한
* 셰이더 개발팀 분리 및 별도 CI 프로세스 (결과 반영 지연)
## 2. 셰이더 시스템 기본 정보
* **셰이더 유형:**
* **글로벌 셰이더:**
* HLSL 코드로 직접 작성 (프로그래머 작성)
* 특정 패스, 컴퓨트 작업 등에 사용
* 씬 오브젝트와 직접적으로 연결되지 않음
* 콘텐츠 비의존적
* **재질 셰이더:**
* HLSL 코드와 재질 그래프 결합으로 자동 생성
* 씬 오브젝트에 적용
* 콘텐츠 의존적 (대규모 프로젝트에서 컴파일/쿡 시간 대부분 차지)
* **기타 셰이더:** Niagara, 컴퓨트 프레임워크 등 (영향 적음)
* **셰이더 타입 (Shader Types):** C++ 및 HLSL 코드 결합으로 셰이더 생성
* 각 구현은 렌더링 패스와 매핑
* **재질 셰이더:** 베이스 패스, 섀도우 패스, 뎁스 패스 등
* **글로벌 셰이더:** 포스트 이펙트, 라이팅 패스, 컴퓨트 관련 등
* **버텍스 팩토리 (Vertex Factories):**
* 메시, 파티클, 헤어, 워터 등 다양한 버텍스 데이터 언패킹 처리
* 동일 재질도 사용 플래그에 따라 다른 버텍스 팩토리로 셰이더 생성 가능
* **셰이더 포맷 (Shader Formats):**
* 단일 런타임 타겟을 위한 셰이더 코드 및 데이터 타입
* PC는 SM5, SM6, Vulkan, 모바일 등 다중 타겟 지원
* 각 포맷별 C++ 컴파일러 구현 (외부 컴파일러 호출 및 UE 특화 전/후처리)
* **셰이더 컴파일 단계:**
* **에디터:**
* **시작 시:** 글로벌 셰이더, 기본 셰이더 컴파일 (에디터 실행 전 필수)
* **맵 로딩 시:** 해당 맵 렌더링에 필요한 셰이더 컴파일
* **PIE (Play In Editor):** 게임 로직 실행 및 추가 에셋 로딩에 필요한 셰이더 컴파일
* **쿡 (Cook):**
* 에디터 시작 단계와 동일 (타겟 플랫폼 수만큼)
* 패키지 로딩/저장 중 해당 패키지에 필요한 셰이더 컴파일 (패키지 저장 블록)
* **셰이더 무효화 (Shader Invalidation):**
* 코드 또는 콘텐츠 변경으로 인한 셰이더 재컴파일 트리거
* **전체 무효화:** 모든 셰이더 재컴파일 요구 (GUID 변경 등)
## 3. 셰이더 수 증가의 주요 원인
* **간소화된 공식:** `총 재질 셰이더 수 ∝ 재질 수 × 버텍스 팩토리 수 × 셰이더 타입 수 × 셰이더 포맷 수`
* `재질 수` 및 `버텍스 팩토리 수`는 콘텐츠 의존적 (예술가/기획자 영향)
* `셰이더 타입 수`는 엔지니어링 영향 (새로운 기능 추가)
* `셰이더 포맷 수`는 타겟 플랫폼 구성에 따라 결정 (엔지니어링 영향)
* **핵심 문제:** 위의 변수 중 최소 두 가지 이상이 꾸준히 증가
* **콘텐츠 측면 추가 요인:**
* 새로운 재질 및 재질 인스턴스 생성
* 재질/재질 함수 내 `Static Switch` 추가
* 품질 수준별 재질/재질 함수 변형
* 재질 인스턴스 내 재질 속성 오버라이드
* **Static Switch의 함정:**
* Boolean Switch 하나당 셰이더 수 2배 증가 가능성
* 현대 GPU에서 동적 분기(Dynamic Branching) 비용 감소에도 불구하고 여전히 Static Switch 사용 습관 존재
* **권장:** 정말 필요한 경우가 아니라면 Static Switch 사용 자제, 성능 프로파일링 필수
## 4. 셰이더 컴파일 및 캐싱 메커니즘
* **셰이더 맵 캐싱:**
* 동일한 재질 매개변수를 가진 여러 재질 인스턴스는 동일한 셰이더 맵 공유 (메모리 절약)
* 셰이더 컴파일 작업 제출 시, 코드 및 설정이 완전히 일치하면 컴파일 1회만 수행 후 결과 캐싱
* **셰이더 바이트코드 중복 제거:**
* 컴파일된 셰이더의 최종 바이트코드는 1회만 저장 (런타임 메모리 및 PSO 컴파일 비용 절감)
* **문제점:** 이 중복 제거는 컴파일 후 작동하므로, 불필요한 컴파일 작업을 이미 수행한 후 인식 가능
* **UE5 온디맨드 셰이더 컴파일 (ODSC):**
* **기능:** 에디터 실행 시 불완전한 셰이더 맵으로 시작, 가시적인 씬에 필요한 셰이더만 필요 시 컴파일
* **효과:** 맵 로딩 및 PIE 시 셰이더 컴파일 부담 대폭 감소 (UE4 대비)
* **한계:** 에디터 워크플로우 개선에 집중, 쿡 프로세스에는 영향 없음
* **중요한 파생 효과:** 셰이더 캐시 메커니즘 도입 (ODSC 요청 결과 DDC 캐싱)
## 5. 셰이더 수 감소 전략 (엔진 변경 없이)
1. **재질 관리:**
* 프로젝트 내 재질 수 최소화
* 재질 인스턴스 활용 권장 (중복 제거 가능성 높음)
2. **Static Switch 지양:**
* 가능하면 `Uniform Parameters` 활용 (성능 측정 필수)
* **예외:**
* 재질 변환기(Material Translator)의 동적 분기 지원 미흡 시 (신규 재질 변환기 개발 중)
* 텍스처 샘플링 회피 또는 샘플러 제한 초과 방지 목적
3. **사용하지 않는 셰이더 분석 및 제거:**
* 컴파일되었으나 사용되지 않는 셰이더 식별
* 불필요한 재질 사용 플래그 비활성화
* 사용하지 않는 렌더링 기능 비활성화 (CVar, 설정 등)
* **UE 5.6 신규 기능:**
* `ListShaders` 런타임 명령어: 런타임 시 로드된 셰이더 목록 확인
* `Shader Type Stats` 쿡 결과: 쿡 시 컴파일 및 패키징된 셰이더 목록 확인
* **팁:** 불일치 항목 발견 시 엔진 팀에 제보
## 6. UE 5.2 ~ 현재 셰이더 시스템 개선 사항
* **셰이더 미니파이어 (Shader Minifier):**
* **문제점:** DXC(DirectX Shader Compiler) 전처리 단계에서 과도한 시간 소요 (큰 셰이더 파일, 불필요한 함수 파싱)
* **해결책:** 사전 데드 스트립핑(Dead Stripping) 기법 도입
* **작동 방식:** 셰이더 소스 파일을 함수, cbuffer 등으로 분할 후, 엔트리 포인트에 관련 있는 청크만 추출하여 새로운 소스 파일 생성
* **효과:**
* 전처리 단계 컴파일 비용 대폭 감소
* 전체 컴파일 시간 현저히 개선 (특히 코드 비중이 큰 셰이더)
* **적용:** UE 5.1/5.2 (실험적), UE 5.3 (기본 활성화, Metal/OpenGL 제외), UE 5.4 (모든 플랫폼 기본 활성화)
* **전처리 라이브러리 재구축:**
* **기존:** MCPP (느리고 유지보수 어려운 C++ 라이브러리)
* **대안:** DXC (코드 출력 방식 비유연) → **Rad Game Tools의 Pure C Preprocessor (사용)**
* **개선 작업:** 파일 로딩 가상화, 안정성/메모리/성능 최적화
* **적용:** UE 5.2 (실험적), UE 5.3 (기본 활성화, MCPP 대체)
* **개별 셰이더 DDC 캐싱 (Per-Shader DDC Caching):**
* **핵심 아이디어:** 전처리 및 미니파이된 셰이더 소스를 기반으로 개별 셰이더 캐싱 활성화 및 공유 캐시 지원
* **작동 방식:**
1. 셰이더 맵 캐시 미스 발생 시, 개별 셰이더 전처리 및 미니파이
2. 개별 셰이더별 캐시 키 생성 및 쿼리 (인메모리 캐시 → DDC)
3. 컴파일이 필요한 셰이더만 컴파일
4. 캐시 히트된 셰이더 결과와 컴파일된 셰이더 결과를 결합하여 새 셰이더 맵 생성 및 캐싱
* **주요 작업:**
* 셰이더 포맷 리팩토링 (전처리/컴파일 분리)
* 셰이더 작업 생성/제출 최적화 및 병렬화
* ODSC의 에디터/로컬 전용 제한 해제 (공유 캐시 활용)
* **DDC 병목 현상 해결:** Zen Server 도입 (파일 시스템 DDC 대체)
* **효과:**
* 이전보다 훨씬 적은 셰이더 컴파일 발생
* 대규모 셰이더 무효화 빈도 현저히 감소
* 코드 변경(주석, 공백 등)으로 인한 무효화 제거
* 플랫폼 간 셰이더 무효화 오류 감소 (예: Xbox 헤더 변경 시 PS5 셰이더 컴파일 문제 해결)
* 평균 셰이더 컴파일 수 감소 (공유 캐싱으로 인한)
* **바이트코드 중복 제거 최적화:**
* **문제점:** 셰이더 맵 레코드 구조로 인한 DDC 내 셰이더 데이터 중복 발생 (쿡 메모리 및 DDC 오버헤드 증가)
* **해결책:** 바이트코드 배열을 DDC의 값 첨부(Value Attachments)로 분리 → 해시가 동일하면 DDC가 자동으로 중복 제거
* **효과:** DDC 저장 공간 및 쿡 메모리 사용량 대폭 감소
* **DXC 저수준 최적화:**
* 인라이닝(Inlining) 및 디버그 정보 내보내기 성능 개선
* 특정 셰이더 컴파일 시간 개선 및 심볼 포함/미포함 컴파일 시간 격차 해소
* **한계:** D3DSM6에만 적용, Vulkan은 별도 문제 존재
* **재질 번역 결과 캐싱:**
* 대규모 재질 번역 시간 단축
* **Unreal Build Accelerator (UBA) 활용:**
* 분산 빌드 시스템을 이용한 셰이더 컴파일 분산
* 가상 입력/출력 파일 메커니즘 활용 (디스크 I/O 감소)
* 셰이더 컴파일 작업 배치 시각화 및 분석 기능
* **기타 개선 사항:**
* 유사 버텍스 팩토리 병합 (셰이더 수 감소, 런타임 성능 영향 미미)
* 심볼 버퍼 최적화 (디버그 심볼 중복 저장 감소)
* 메모리 사용량 최적화 (공유 버퍼 사용)
## 7. 결과 (Results)
* **CitySample 프로젝트 쿡 시간 비교 (UE 5.1 vs 5.7):**
* **전체 무효화:** 37분 → 14분
* **전체 무효화 (심볼 포함):** 1시간 15분 → 15분
* **부분 무효화 (common.ush 함수 변경):** 37분 (거의 전체 재컴파일) → 12분 (21개 셰이더만 컴파일)
* **주요 개선 사항:**
* 요청 셰이더 및 컴파일 셰이더 수 감소 (셰이더 감소 및 중복 제거)
* 전체 쿡 시간 대폭 단축
* 심볼 포함/미포함 컴파일 시간 격차 해소
* 평균 전처리 시간 개선 (미니파이어, 정규화 추가에도 불구하고)
* 평균 컴파일 시간 개선 (미니파이어 + DXC 최적화)
* 캐시 히트율 증가 (CitySample은 예외적으로 높은 편, 일반 프로젝트는 더 큰 향상)
* **메모리 사용량:** UE 5.7에서 UE 5.1 대비 피크 메모리 사용량 약 20GB 감소
* **DDC 저장 공간:** UE 5.7에서 UE 5.1 대비 약 20% 증가 (심볼 비활성화 시)
## 8. 향후 계획 (Future Work)
* **콘텐츠 생성자 대상 개선:**
* 변경 사항이 셰이더 생성에 미치는 영향 시각화 도구 제공
* **버텍스 팩토리 폐지 고려:**
* 동적 분기 기반 버텍스 페치 코드 작성 (GPU 성능 영향 검증 필요)
* 유사한 버텍스 팩토리 병합 (성능 저하 없는 범위 내)
* **신규 재질 변환기 (Material Translator):**
* 적절한 동적 분기 지원 → Static Switch 필요성 감소
* Bindless와 결합하여 텍스처 샘플링 관련 Static Switch를 동적 분기로 전환 (퍼뮤테이션 수 대폭 감소)
* **코드 생성 분리:** 재질 변환/컴파일 단계를 분리하여 필요한 코드만 생성, 셰이더 중복 제거율 향상 목표 (1:1에 근접)
* **셰이더 모듈 개념 도입:**
* 전처리 코드 공유 라이브러리 구축 → 각 셰이더별 전처리 비용 절감
* **UBA 가상 파일 확장:**
* 셰이더 파라미터 메타데이터, 컴파일러 환경 설정 등 공유 데이터 관리
* 작업 배치 제거 및 부하 분산 개선
* **글로벌 셰이더에 대한 ODSC 적용:**
* 전체 글로벌 셰이더 컴파일을 비동기적으로 제출하고 필요 시 블록킹
* **커뮤니티 피드백 요청**
## 9. 감사합니다.
* 본 발표 내용은 많은 사람들의 노력으로 이루어졌습니다.
* 질문 받습니다.