- Hitch? : 프레임이 기대보다 늦게 스크린에 나타나는 순간
- 스크롤, 애니메이션, 트랜지션 등의 상황
- 이는 렌더링 루프가 제 시간에 결과를 반환하지 못했기 때문
- 렌더링 루프
- 터치 이벤트 - 앱(UI변경) - OS(프레임 수신 후 화면에 띄움)로 전달 되는 과정의 무한 루프
- 프레임 전달 과정은 갱신 주기에 맞춰서 이루어져야 함
- 아이폰, 아이패드: 60Hz(1프레임 당 16.67ms)
- 아이패드 프로: 120Hz(1프레임 당 8.33ms)
- 갱신주기마다 전달되는 VSYNC 신호에 맞춰서 프레임이 교체된다.
- 따라서 다음 VSYNC 주기가 오기 전까지 프레임이 준비되지 않으면 hitch가 일어난다.
- 이는 3가지 단계로 나눌 수 있다. 각 단계는 다음 VSYNC 이전까지 완료되어야 한다.
- App: 이벤트를 핸들링하고, UI에 대한 변경[이 일어난다.
- Render Server: UI를 실제로 렌더링하는 별도의 프로세스
- on the Display: 렌더링된 UI를 실제 화면에 띄움
- 각 단계별로 VSYNC 간격 하나를 가져가기 때문에 2개의 간격만큼 여유를 가질 수 있는데 이를 더블 버퍼링이라고 한다.
- 만약 렌더링이 길어지면, 다음 프레임은 실제 보여지는 것보다 3프레임 전에 준비되는 것이 되는데, 이것은 트리플 버퍼링이다.(제대로 된 상황은 아니다)
- 각 단계는 좀 더 세부적으로 나눌 수 있다.
- App
- Event
- 터치, 네트워킹, 키보드, 타이머 등
- 업데이트 대상인 데이터의 업데이트가 일어나면, Core Animation은 setNeedsLayout()을 호출함
- 실제 업데이트는 Commit Phase에서 일어남 -> 중복 작업을 방지하기 위해서
- Commit
- 레이아웃을 재계산할 필요가 있다면, 이벤트 페이즈가 끝나면 자동으로 시작
- 재계산이 필요한 레이어를 찾은 다음에, 이를 부모-자식 순으로 배치함
- 주요 보틀넥
- 커스텀 드로잉이 필요한 뷰에서는, setNeedsDisplay() 사용해야 한다. 이는 레이아웃 작업이 끝난 뒤에 일괄적으로 수행된다.
- 모든 드로잉은 자신이 그리는 레이어의 텍스쳐 기반CGContext를 전달 받는다.
- 코어 애니메이션이 다루는 영역에서 변경된 레이어들은 이미지 상태로 존재한다.
- 변경 상태에 대한 트리가 완성되면, 그 상태로 렌더링 서버로 전송된다.
- Render
- Render Prepare
- 변경 상태 트리를 순회하면서 GPU 렌더러로 넘기기 위한 선형적인 구조로 바꾸게 된다. -> 깊이 우선 탐색
- Render Execute
- GPU에 넘어가서 처리되어 최종 텍스쳐로 합쳐지게 된다.
- 주요 병목 지점(2)
- Display
- 각 단계는 VSYNC 라는 데드라인을 가지며, 각 작업이 병렬적으로 이루어진다.
- Render Loop의 자세한 동작
- 일어날 수 있는 Hitch의 종류
- Commit hitch: 앱이 commit을 너무 늦게 하거나 이벤트 처리가 너무 느리다
- commit기회는 매 VSYNC 뿐이므로, 한번 놓치면 기다려야 한다.
- 처리가 길어지면 길어질수록, 렌더링 서버가 놀게 된다.
- Render Hitch: 렌더링 서버가 제 시간에 렌더링을 마치지 못했다.
- 렌더링 서버의 마감 시간도 VSYNC이므로, 이때까지 렌더링을 못하면, 1프레임을 그냥 넘겨야 한다
- hitch Time을 측정하는 방법
- 전체 Hitch Time을 계산하는 방법
- 프레임마다 소요 시간이 다르고, 타겟의 갱신 주기도 다르고, 기대하는 프레임 수도 다르다.(Commit이 없으면 프레임 갱신이 없다.) -> 즉, 재현이 어렵고 비교 가능하지 않다.
- hitch time ratio
- 전체 hitch time을 개별적인 간격에 대해 normalize한 것 -> 초당 hitch타임
- MetricKit 확인하기
- 대략적인 기준
- Good - 5ms/s
- Warning - 5..<10 ms/s
- Critical - >=10 ms/s