iOS로 앱을 만들면서 앱의 화면이 어떤 구조로 그려지는지 궁금한적이 있으셨나요?
저는 너무 궁금했습니다.
그래서 여러가지 공부하던 와중에 Render Loop이라는것을 알기 되었고 공부한 내용을 공유하고자 합니다.
Render Loop에 대해 이야기 하기전에 알고 가야할 개념이 있습니다.
Refresh Rate와 Frame Duration인데요
아이폰, 아이패드, 맥북의 화면은 Refresh Rate(주사율) 로 구분하여 60Hz, 120Hz 이렇게 크게 두가지로 나뉩니다.
애플은 120Hz Refresh Rate(주사율)을 가진 디스플레이를 ProMotion 디스플레이라고 부릅니다.
Refresh Rate(주사율)는 초당 그리는 화면의 개수를 나타내고
Frame Duration는 1개의 화면을 그리는 시간을 나타냅니다.
Refresh Rate와 Frame Duration는 서로 별개의 개념이 아닙니다.
서로 기준이 다를 뿐이죠
60Hz 디스플레이의 경우 16.67ms, 즉 0.001667초에 한번 화면을 그리고
120Hz 디스플레이의 경우 8.33ms, 즉 0.000833초에 화면을 한번 그린다고 생각하면 됩니다.
120Hz 디스플레이의 경우 같은 시간에 그려야 하는 화면의 개수가 60Hz디스플레이에 비해 2배 많죠? 그래서 애플에서는 120Hz 디스플레이를 탑재하는 경우 상대적으로 고 사양인 프로라인 (아이폰 프로, 아이패드 프로, 맥북 프로)에 적용합니다.
물론 모든 화면을 0.001667초 ~ 0.000833초에 한번씩 그리는게 아닙니다.
애플 기기의 디스플레이는 가변 주사율이라고 해서 최대 60 ~ 120Hz 출력하는 것이지 많은 연산이 필요한 작업은 Refresh Rate를 낮춰서 가변적으로 동작합니다.
항상 120Hz로 동작하면 너무 많은 연산이 이루어져서 기기 배터리가 빨리 소모되기에 이런 방식을 사용합니다.
그럼 이제 Render Loop에 대해서 알아볼까요?
Render Loop는 iOS앱의 화면을 그리는 흐름입니다.
앞서 이야기 했듯이 애플의 모든 화면이 있는 기기는 초당 최대 60 ~ 120번 화면을 새로고침합니다.
1개의 화면을 frame이라고 하고 frame을 그리는것을 렌더링 한다고 합니다.
60Hz 디스플레이의 경우 0.001667초에 한번 frame을 그리고
120Hz 디스플레이의 경우 0.000833초에 한번 frame을 그린다고 했죠?
iOS에서 이 한개의 frame을 렌더링 할 때 마다 Render Loop가 발생합니다.
Render Loop는 원칙상 해당 디스플레이의 Frame Duration안에 실행되어야 합니다.
컴퓨터 게임을 좋아하시는 분들이라면 VSYNC를 한번이라도 들어보셨을겁니다.
게임에서 계단현상을 줄여주는 수직동기화 기능을 VSYNC라고 하곤 하죠
하지만 iOS에서는 다른 의미로 사용됩니다.
새로운 frame의 render loop를 시작하는 이벤트로 이용됩니다.
Render Loop는 위 이미지와 같은 형태로 진행됩니다.
총 3개의 라인으로 구성된 파이프라인 형태로 병렬구조로 진행됩니다.
3개의 라인을 App, Render Server, On The Display단계로 분리 합니다
App, 이벤트 받기, 수정할 데이터 수집/정리,Render 트리거
Render Server, Rendering 준비 및 실행
On The Display, 화면 출력 별도의 연산 존재하지 않음
위 이미지 처럼 연산 단계를 세분화할 수 있습니다.
- Event Phase
- 앱이 화면 변경을 요구하는 이벤트를 받고, 업데이트가 필요한 요소를 수집, 정리함 (ex. 화면 터치, 키보드 입력, 네트워킹)
- Commit Phase
- 재연산이 필요한 레이어를 Tree형태로 정리하고 Render Server에 전송
- Render Prepare
- 넘겨받은 Tree를 분석해서 그래픽 연산에 용이하게 선형 파이프라인 형태로 변환
- Render Execute
- 준비를 마친뒤 컴포넌트들을 렌더링, 각각의 레이어는 최종 Texture로 합쳐진다.
- Display
- 렌더링 끝난 결과를 화면에 보여주는 단계 (별도의 연산 없음)
이렇게 총 5단계를 거쳐서 1개의 화면이 그려집니다
앞선 내용들을 읽으면서 어떤 생각이 드셨나요?
저는 공부하면서 이걸 최대 0.000833초 안에 한번해야 한다고? 라는 생각이 들었습니다.
기기가 오래되거나 앱이 연산을 많이 필요로 하는 경우 우리는 흔히 렉걸린다 버벅인다라는 경험을 한번씩 해보셨을겁니다.
이런 현상을 Hitch라고 합니다,
Render Loop의 동작이 다음 VSYNC 이벤트 발생 전까지 완료되지 못하면 이런현상이 발생합니다.
앞서 각 단계별로 작업이 분리 되어 있다고 말했었죠?
hitch도 어떤 작업이 완료되지 못했는지를 기준으로 두가지로 분류됩니다.
Commit Hitch, Render Hitch이렇게 두가지로 분류됩니다.
App 단계의 Commit이 다음 VSYNC이전에 끝나지 않은 경우 발생하는 Hitch를 Commit Hitch라고 합니다.
Render Server 단계의 Render Execute에서 렌더링이 다음 VSYNC전에 마무리 되지 못한 경우 발생하는 Hitch를 Render Hitch라고 합니다.
iOS에서는 화면을 최대 60~120번 새로고침 합니다.
1개의 화면, frame을 그릴때 한번의 RenderLoop가 발생합니다.
Render Loop는 파이프라인 형태로 병렬로 동작하고 3가지 단계로 분류됩니다.
각 연산 단계에서 작업이 다음 VSYNC이전에 끝나지 않는경우 Hitch가 발생합니다.
(이를 사용자는 렉걸린다, 버벅인다라고 느낍니다.)