ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • WWDC) Animation hitch와 render loop
    iOS 2022. 4. 5. 00:04

    https://developer.apple.com/videos/play/tech-talks/10855/

     

    Explore UI animation hitches and the render loop - Tech Talks - Videos - Apple Developer

    Explore how you can improve the performance of your app's user interface by identifying scrolling and animation hitches in your app...

    developer.apple.com

    본 포스트 내용의 출처는 WWDC입니다. 의/오역이 있을 수 있습니다.

     

     

    이 글에선 스크롤링과 애니메이션 hitches에 대해 알아보고 render loop에 대해 자세히 살펴볼 것이다.

     

    우선 hitch가 무엇인지 훑어보고,  렌더 루프와 발생할 수 있는 다양한 유형의 hitch에 대해 알아보자.

     

    Hitch란 무엇인가?

     

    앱을 실행 할 때, 사람들은 화면을 스크롤 하거나 버튼을 누르는 순간 그에 따른 이벤트나 view 계층간 transition의 변화를 기대한다.

    이러한 애니메이션들은 화면상의 사람과 콘텐츠 사이에 시각적 연결 감각을 구축한다.

     

    Animation hitch는 애니메이션 과정에서 끊김을 유발하거나, 연결을 끊어서 혼란을 일으킬 수 있다.

     

    우리가 앱을 이용 하면서, 여러개의 콜렉션 뷰 셀을 스크롤 하는 경우가 있다. 이 때, 스크롤이 끊키는 경우가 있는데 이는 표시되어야 하는 다음 프레임의 준비가 늦기 때문이다.

    즉, hitch는 프레임이 예상보다 화면에 늦게 나타나는 경우를 말한다.

     

    hitches는 render loop가 프레임을 제 시간에 끝내는데 실패 했을때 발생 된다.

     

    Render Loop란?

     

     

    Render Loop는 터치 이벤트가 앱으로 전달된 다음 UI에 대한 변경 사항이 적용되어 프레임을 완성 시키는 운영 체제로 전송되는 연속 프로세스이다.

    이것은 루프(반복) 이고 기기의 주사율(refresh rate)에 따라 일어난다.

     

    아이폰 이나 아이패드의 경우 초당 60프레임 이고, 이는 새로운 프레임이 매 16.67 ms마다 화면에 표시 된다는 뜻이다. 아이패드 프로의 경우 초당 120프레임이고, 8.33 ms로 새로운 프레임이 표시된다.

     

    프레임의 시작에서, 하드웨어는 VSYNC(수직 동기화)라는 이벤트를 생성한다. VSYNS는 새로운 프레임이 준비 되어야 할 시기를 나타난다. display track에서 하이라이트를 통해 deadline(새로운 프레임이 표시되어야 하는 데드라인)을 쉽게 볼 수 있다. Render Loop는 VSYNC에 시간이 맞춰져 있다. 프레임을 준비 하기 위해선 도중에 체크 포인트를 거쳐야 한다.

     

    Render Loop는 세 단계로 나뉘어져 실행된다. 첫 번째 단계는 앱에서 이벤트를 처리하고 UI를 변경하는 단계이다. 이 작업은 다음 VSYNC 이전에 완료되어야 다음 단계를 시작할 수 있다. 다음 단계는 렌더 서버라는 별도의 프로세스에서 발생한다. 여기서 UI가 실제로 렌더링된다. 세 번째이자 마지막 단계는 프레임이 화면에 디스플레잉 하는 작업인데, 이는 다음 VSYNC 이전에 완료되어야 한다.

     

    위의 3단계에서 중요한 주의 사항은 프레임이 화면에 표시되기 전에 2개의 프레임으로 처리된다는 것이다. 우리는 이것을 더블 버퍼링이라고 부르지만, 다른 모드가 있다. hitch를 피하기 위해 시스템은 트리플 버퍼링으로 전환할 수 있으며, 여기서 렌더 서버는 작업을 완료하기 위해 프레임 처리 시간이 하나 더 주어진다. 예비 모드이기 때문에, 우리는 Render Loop의 hitches에 대해 이야기하는 동안 더블 버퍼링에 초점을 맞출 것이다.

     

    전체적으로 전체 렌더 루프는 5단계로 구성된다. 루프는 첫 번째 단계인 이벤트 단계로 시작한다. 여기서 앱은 터치 이벤트를 처리하고 UI의 변경이 필요한지 여부를 결정한다. 다음은 커밋 단계이다. 커밋 단계에서 앱은 UI를 업데이트하고 렌더 서버에 제출하여 렌더링을 한다. 다음 VSYNC에서는 렌더 서버가 제출을 받고, 렌더 준비 단계에서 GPU에 그릴 수 있도록 준비한다. 렌더 실행 단계에서는 GPU가 UI를 최종 이미지로 그려 다음 VSYNC에서 유저에게 프레임을 출력 할 수 있다.

     

    각 단계는 렌더 작업을 포함한, 모든 프레임에서 부드러운 사용자 경험을 제공 하는데 있어서 매우 중요하다. 별도의 프로세스에서 발생 하지만 앱을 대신하여 작동하므로 layer 계층이 제 시간에 처리되고 그려질 수 있도록 하는 것은 사용자의 몫이다. 좀 더 이해를 하기 위해 예를 들어 보자.

     

    이 예제에서 우리는 각 단계를 보기 위해 렌더루프를 통과하여 이 프레임을 따라 갈 것이다.

     

    첫번째는 앱이 이벤트를 전달 받는 이벤트 단계이다. 이 이벤트 들은 터치 이벤트, 네트워크 콜백, 키보드 터치 와 타이머 같은 것들이다. 앱은 레이어 계층에 변경하여 이러한 이벤트 들에 응답 할 수 있다. 예를 들어 앱은 레이어의 배경색을 바꾸거나 레이어의 크기와 위치를 바꿀 수 있다.

     

    하지만 앱이 layer의 bounds를 업데이트 할때, 코어 애니메이션은 setNeedsLayout을 호출한다. setNeedsLayout 함수는 모든 레이어를 식별 하고 이에 응답하여 레이어를 반드시 다시 계산해야 한다.

     

    시스템은 이러한 "필요한 레이아웃" 요청을 통합하고 커밋 단계에서 순서대로 수행하여 중복 작업을 줄인다.

     

    만약 이벤트 단계가 끝날때 자동적으로 시작하는 커밋 단계를 필요로하는 레이아웃이 있다면, 먼저 시스템은 레이아웃이 필요한 모든 레이어를 취하여 부모에서 자식까지 한 번에 하나씩 배치한다.

     

    레이아웃은 일반적인 병목 작업이다. 그렇기 때문에 당신의 앱이 몇 ms안에 작업을 완료한다는 것을 계속 기억해라. 레이블, 이미지 뷰나 drawRect를 overriding하는 뷰 같은 몇몇의 뷰들은 커스텀 드로잉을 요구한다. 만약 이러한 뷰들이 비쥬얼 업데이트를 요구하는 경우, 반드시 setNeedsDisplay를 호출해야 한다. 레이아웃과 마찬가지로 시스템이 레이아웃이 완료 되면 이러한 요청들을 합쳐서 한번에 처리한다.

     

    그리기 작업 도중, 모든 custom 그리기 레이어는 그들이 그려야하는 texture - backed core graphics(core graphics에서 제공 받는텍스쳐? - 잘 모르겠는 부분) 컨텍스트를 전달 받는다.

     

    코어 애니메이션에 한에서, 이제 이 레이어들이 단순히 이미지이다. 모든 레이어가 배치되고 그려졌으므로 변경된 모든 레이어 트리는 수집되어 렌더링을 위해 렌더 서버로 보내진다.

     

    렌더서버에 대해 설명할 차례이다. 렌더 서버는 레이아웃 트리를 진짜 화면에 표시 되는 이미지로 바꿔주는 부분을 담당한다. 

    이 준비 단계에서 렌더 서버는 앱의 레이어 트리를 반복하고 GPU가 실행 할 수 있는 선형 파이브라인을 준비한다.

     

    준비단계의 파이프라인은 최상위 레이어 부터 시작해서, 부모에서 자식으로 형제에서 형제로 뒤에서 앞의 순서로 뷰들을 정렬한다. 

     

    다음, 이 선형 파이프라인은 GPU를 통과하여 각 레이어는 최종 텍스쳐로 합쳐진다. 몇몇의 레이어들은 렌더링을 하는데 시간이 좀 더 걸릴 수 있다. 이는 곧 짧게 설명 할 또 다른 일반적인 병목 현상이다.

    GPU가 실행되어 오른쪽에 있는 이미지를 렌더링 하면 다음 VSYNC에 표시 될 준비가 된다.

     

    각 차례의 Render Loop는 성능에 민감하고 데드라인을 가지고 있다. 데드라인은 다음 VSYNC이다. 목표 프레임률을 달성하고 낮은 입력 대기시간을 유지하기 위해 전체 프로세스가 실제로 모든 프레임에서 병렬로 실행 된다.

     

     

    이런식으로, 파이프 라인은 병렬이 되고, 우리의 앱은 시스템이 이전 프레임을 준비하는 동안 새로운 프레임을 준비 할 수 있다. 이것이 데드라인을 놓치는 것이 중요한 이유이다.

     

     

    Hitch의 타입

     

    이때까지 Render Loop가 어떻게 돌아가는지 보았고, 과연 앱에서 어떤 종류의 hitch들을 볼 수 있는지 알아보자.

     

     
    두개의 주요 유형이 있다.
     
    - 커밋 hitches: 앱 프로세스 내에서 발생
    - 렌더 hitches: 렌더 서버 내에서 발생

    커밋 hitches

    커밋 hitch는 앱이 프로세스 이벤트나 커밋을 처리하는데 오래 걸릴 경우를 말한다.
     
     
    여기서 커밋은 너무 오래 걸리고 데드라인을 놓치므로 다음 VSYNC에서 렌더 서버는 처리 할 것이 없으며 이제 다음 VSYNC가 렌더링을 시작 할 때까지 기다려야 한다. 그리고 지금 우리는 프레임 배송 시간을 한 프레임 늦췄다(위의 on the display 한칸이 늘어난 것). iPhone이나 iPad에서는 ms 단위로 16.67ms이다. 우리는 이 지연 기간을 "hitch time"이라고 부르며 ms로 측정한다.
     

    커밋 작업이 더 길게 걸리고 다음 VSYNC를 지나면 프레임은 두 프레임, 즉 33.34ms 늦는다.

    이 33.34ms는 사용자가 부드러운 스크롤링을 볼 수 없는 시간이다.

     

    커밋 hitches에 대해 더욱 이해하고 당신의 앱에서 이를 어떻게 고치는지 알고 싶다면 "Find and fix hitches in the commit phase"를 확인 해보자.

     

    렌더 hitches

    두번째 종류의 hitch는 렌더 hitch이다. 이는 렌더 서버가 우리의 레이어 트리를 제 시간에 준비하거나 실행 할 수 없을때 발생한다.

     

    여기 렌더 실행 단계가 너무 오래 걸리고 VSYNC 바운더리를 넘어간 경우가 있다. 따라서 프레임은 제 시간에 준비 되지 않았고 녹색 프레임은 예상보다 한 프레임 늦게 표시되었다.

    다시 말하지만, 우리는 16ms의 hitch 타임이 있다.

     

    렌더 hitches와 레이어 트리 최적화에 대해 더 알고 싶다면 "Demystify and eliminate hitches in the render phase"를 확인 해보자.

     

    이제 hitches를 측정하고 정량화 하는 방법에 대해 알아보자.

     

    hitch time(hitch 시간) 측정하기

    우린 이전의 슬라이드에서 hitch time에 대해 알아보았다. 싱글 hitch에 대해 이야기 할 때는 매우 유용하지만 스크롤, 애니메이션 또는 트랜지션과 같은 오래 걸리는 이벤트에 대해 살펴볼 때에는 다루기 어려울 수 있다.

     

    우선적으로, 각 스크롤이나 애니메이션의 시간이 정확하게 동일하지 않으면 비교가 어렵다. 따라서 프레임 수가 동일하다.

    더 안좋은 점은 iOS 디바이스가 항상 스크린을 업데이트 하지 않는다는 점이다. 만약 렌더 서버에 커밋이 전송되지 않느다면, 새 프레임이 전송되지 않는다. 따라서 테스트 및 장치 간에 hitch time을 비교하기가 더욱 어렵다. 그래서 대신에 우리는 "hitch time ratio(히치 시간 비율)"이라는 측정법을 사용한다. Hitch time ratio는 간격의 총 hitch time을 지속 시간으로 나눈 값이다. 전체 시간으로 정규화 되기 때문에 여러 경험을 비교할 수 있다. 초당 hitch ms 단위로 측정되므로 장치가 초당 hitch된 시간(밀리초)을 나타낸다. 이 측정방법을 사용하여 앱 성능을 측정하는 방버에 대해 알아보려면, "What's new in MetricKit"을 통해 확인할 수 있다. 테스트 suite에서 hitch time ratio(히치 시간 비율)을 추적하는 방법은 "Eliminate animation hitches with XCTest"를 참조하여라. 마지막으로 hitch time ratio를 사용할 수 있는 예제를 살펴보자.

     

    여기 30 프레임이 있다. 아이폰에서는 0.5초 정도의 작업이다. 각 프레임이 마감에 도달하면 사용자는 hitch가 없는걸 볼 수 있다. hitch 타임은 0이며, 히치 타임 비율도 0이다.

     

     

    하지만 이제 바닥에 분리된 간격으로 표시 트랙을 볼 수 있다.

    어떤 프레임은 다른 프레임보다 화면에 길게 표시되고, 다른 커밋과 렌더들은 hitch를 유발한다. 이 hitch time을 합하면 100.02ms가 된다. 0.5초 이상 히치 타임의 비율은 200.04ms 이다.

     

     

    이것은 단지 예시일뿐이다. 일반적으로, 위에 나와있는 목표 hitch ratios(히치 비율)은 Apple의 툴에서 권장하고 사용하는 것이다. 이 목표는 0 hitch ms/s 이지만, 5 hitch ms/s 미만은 양호한 것으로 간주되어 사용자가 대부분 눈치채지 못한다. 5~10 hitch ms/s 사이에 유저는 몇개의 인터럽트를 눈치채게 된다. 이것들을 연구해야한다..초당 10 htich ms/s 이상이면 hitch가 사용자 경험에 큰 영향을 주므로 rendering Loop를 최적화 하는 방법을 즉시 연구해야한다.

     

     

    요약

     

     

    오늘 우리는 Render Loop와 새로운 프레임이 사용자에게 어떻게 표시되는지 이야기 해보았다. 우리는 hitch가 무엇이며 commit hitch와 render hitch 라는 두개의 타입에 대해 알아보았다.

    마지막으로 일정 시간동안 사용자가 경험하는 hitch 시간을 측정하기 위해 hitch time ratio (히치 시간 비율)을 정의했다.

    더 다양한 hitch의 종류와 app에서 hitch를 잡고 수정하는 방법에 대한 자세한 내용은 commit hitches와 render hitches 설명을 참조해라.

     

    당신들의 버터같이 부드러운 app을 기대하며...

    댓글

Designed by Tistory.