언리얼 엔진의 UI 렌더링 과정에 대해 정리해보았습니다.
구글링 및 ChatGPT, Gemini를 참고했습니다. -> 무조건 신뢰하지 마시고 참고만 해주시길 바랍니다.
흐름 요약
[Game Thread]
├─ Input & State Update
│ - 마우스 / 키보드 / 터치 입력 처리
│ - Focus, Hover, Capture 상태 갱신
│ - Widget Tick (NativeTick)
│ - UI 애니메이션 상태 업데이트
│
├─ Slate Tick
│ ├─ Layout Pass
│ │ ├─ Prepass
│ │ │ - SlatePrepass()
│ │ │ - Widget Tree 순회 (자식 → 부모)
│ │ │ - ComputeDesiredSize() 호출
│ │ │ - Desired Size 계산 및 캐싱
│ │ │
│ │ └─ Arrange
│ │ - Widget Tree 순회 (부모 → 자식)
│ │ - 실제 크기 / 위치 계산
│ │ - FGeometry 생성
│ │
│ └─ Paint Pass
│ - Widget Tree 순회
│ - OnPaint() 호출
│ - Render Transform 적용
│ - Clipping 영역 계산
│ - Layer / Material 기준 배칭
│ - Slate Draw Elements 생성
│
└─ Render Command Enqueue
- 생성된 Draw Elements를 Render Thread로 전달
- 실제 GPU 렌더링은 수행하지 않음
- “무엇을 그릴지”에 대한 명령만 생성
↓
[Render Thread]
└─ Slate Render
├─ (Conditional) Render To Texture (RTT)
│ - Retainer Box
│ - Widget Component
│ - Offscreen Render Target에 UI 렌더링
│
└─ Back Buffer Composite
- 일반 Slate UI
- RTT 결과(Texture)
- Vertex / Index Buffer 생성
- UI 전용 셰이더 바인딩
- RHI 통해 GPU 명령 제출
↓
[GPU]
- 2D Quad 렌더링
- Alpha Blending
- Clipping (Scissor / Stencil)
- Font Atlas 기반 텍스트 렌더링
1. Game Thread
1.1. Slate Tick
Game Thread에서 매 프레임 수행됩니다.
- 입력 이벤트 처리 (마우스, 터치, 키보드)
- Widget Tick (NativeTick)
- 애니메이션 상태 업데이트
- Visibility / Enabled 상태 갱신
Slate는 한 프레임 동안 레이아웃 계산(Prepass, Arrange) 과 페인팅(OnPaint) 단계에서 Widget Tree를 순회합니다.
- Prepass: 위젯의 희망 크기(Desired Size)를 계산
- Arrange : 위젯의 실제 크기와 위치를 계산 (FGeometry)
- OnPaint: 실제로 그릴 요소(Draw Elements)를 생성
Slate는 렌더링 전에 레이아웃 계산을 수행합니다.
1.1.1. Layout 계산 단계
Slate 레이아웃은 두 패스로 이루어집니다. 두 패스로 분해한 것은 최적화 때문입니다.
Pass 1: Cache Desired Size
연관된 함수는 SWidget::CacheDesiredSize and SWidget::ComputeDesiredSize
Pass 2: ArrangeChildren
연관된 함수는 SWidget::ArrangeChildren
1.1.1.1. Prepass - Cache Desired Size
Prepass는 모든 위젯의 Desired Size(희망 크기)를 계산하는 단계입니다.
이 패스의 목표는 각 위젯이 얼마만큼의 공간을 차지하려는지 알아내는 것입니다.
- SlatePrepass() 함수에서 수행
- Desired Size 계산
- 위젯 트리를 순회
- 자식 → 부모 방향으로 Desired Size 집계 (상향식)

1.1.1.2. Arrange - ArrangeChildren
Arrange는 부모가 확보한 공간 안에서 자식 위젯에게 실제 크기와 위치를 할당하는 단계입니다.
Slate 는 최상위 창부터 시작해서 프로그래머가 제공한 제약(constraint)에 따라 자손을 배치할 것인지 각 창에 묻습니다.
각 자손에 할당된 공간이 알려지면 Slate 는 재귀를 통해 자손의 자손을 배치할 수 있습니다.
모든 자손이 배치될 때까지 재귀는 계속됩니다.
- Geometry 계산
- 위젯 트리를 순회
- 부모 → 자식 방향으로 실제 Geometry를 할당. (하향식)

Arrange 결과는 FGeometry 구조체로 표현
FGeometry에는:
- Absolute Position
- Local Position
- Size
- Scale
- Render Transform 정보
1.1.2. Paint 단계
레이아웃이 끝나면 각 Widget의 OnPaint()가 호출됩니다.
Slate는 보이는 모든 위젯에 대해 반복하면서, 확정된 Geometry를 기반으로 실제로 그려야 할 UI 요소를 Draw Elements 형태로 생성하는 과정입니다.
- 이 단계에서는 GPU 렌더링이 일어나지 않음
- CPU 상에서 “그릴 명령 목록”만 만든다.
1.1.2.1. Render Transform 적용
Render Transform은:
- 애니메이션
- 회전 UI
- 스케일 효과
등에 사용되며, 레이아웃에는 영향을 주지 않습니다.
1.1.2.2. 클리핑 처리
Paint 단계에서 클리핑 영역이 결정됩니다.
부모로부터 전달된 클리핑 영역과 현재 위젯의 Geometry를 기반으로 유효 렌더링 영역(Culling Rect)이 결정되며, 이 정보는 Draw Elements에 포함되어 Render Thread로 전달됩니다.
- Rect Clipping
- Stencil Clipping (복잡한 마스크)
1.1.2.3. 배칭
Draw Call 수를 줄이기 위해 배칭.
Layer ID, Shader Resource(Texture), Blend State, Clipping 상태 등이 동일한 경우에 한해 Batch Merge가 수행됩니다. (Draw Call 합치기)
1.1.2.4. 최종적으로 Draw Elements를 생성
- 각 Draw Element는 다음 정보를 포함합니다.
- Layer ID
- Geometry
- Brush / Font
- Tint Color
- Clipping State
- Blend Mode
완성된 Draw Elements는 Render Thread로 전달됩니다.
2. Render Thread
Render Thread는 Game Thread에서 생성된 Slate Draw Elements를 실제 GPU 명령으로 변환하는 역할을 담당합니다.
UI 렌더링은 이 단계에서 크게 Widget Render(RTT) 와 Slate Render(Back Buffer 출력) 로 나뉩니다.
모든 UI가 Widget Render를 거치는 것은 아니며, RTT는 Retainer Box, Widget Component 등 명시적으로 Render Target이 필요한 경우에만 사용됩니다.
2.1. Widget Render
- RTT(Render To Texture) 처리:
- Widget Render는 Draw Elements를 곧바로 화면에 그리지 않고, 먼저 Render Target(Texture)에 렌더링하는 단계입니다.
- Retainer Box, Widget Component, 일부 복합 UI에서 사용됩니다.
- Retainer Box의 핵심 목적은 성능 최적화입니다.
- UI 전체를 매 프레임 다시 그리지 않음
- 일정 주기마다 Texture만 갱신
- 변하지 않는 UI는 그대로 재사용
- “UI를 하나의 이미지로 캐싱해서 쓰자”
2.2. Slate Render
Slate Render 단계는 최종 UI 합성 단계입니다.
- RTT를 사용하지 않는 일반 UI
- Retainer Box로 생성된 Texture
- HUD, 메뉴, 오버레이
이 모든 요소가 Back Buffer에 직접 렌더링됩니다.
Slate Renderer는 다음 작업을 수행합니다.
- Draw Elements의 순서를 기반으로 렌더링 순서 및 GPU 상태 전환을 확정
- GPU 리소스 준비
- Vertex Buffer / Index Buffer 생성
- 각 UI 요소는 2D Quad 형태로 변환
- 텍스트는 Font Atlas 참조
- UI 전용 셰이더 바인딩
- 단순한 Vertex / Pixel Shader
- 2D 좌표 변환
- 알파 블렌딩 중심
- RHI를 통해 GPU 명령 제출
- RHI (Rendering Hardware Interface) : 플랫폼마다 렌더링 코드를 통일하기 위한 공통 인터페이스
3. GPU — UI 실제 렌더링
GPU에서 수행되는 작업
- 2D Quad 렌더링
- Alpha Blending
- 클리핑(Scissor / Stencil)
- 텍스트 렌더링 (Font Atlas)
[성능 관점에서의 UI 렌더링]
주요 병목 지점
- 과도한 Widget Tree 깊이
- 잦은 Visibility 변경
- 잦은 Layout 재계산
- Dynamic Text 업데이트
최적화 팁
- Invalidation Box 적극 활용
- Tick 비활성화
- UI 애니메이션 최소화
- 텍스트 변경 빈도 감소
'GameDevelop > UnrealEngine' 카테고리의 다른 글
| UHT (Unreal Header Tool) (0) | 2026.01.15 |
|---|---|
| TArray, TMap (1) | 2026.01.10 |
| UnrealEngine - UObject와 AActor (0) | 2025.12.18 |
| UnrealEngine - Garbage Collection(GC) (0) | 2025.12.18 |
| UnrealEngine - 변수 UI에 붙이기 (블루 프린트 사용) (7) | 2024.09.14 |