본문 바로가기

Linux/Android

젤리빈 - 프로젝트 버터 핵심 사항 - jank 수정법

출처: http://www.iamroot.org/xe/77421#4 댓글

Romain과 Chet의 설명을 들었는데 프로젝트 버터 (젤리빈의 부드러움을 담당한 프로젝트)에서 vsync나 트리플버퍼링가 사용되었다는 지적을 하였습니다. 가끔 프레임이 넘어가는 현상을 그들은 'jank'라고 불렀는데요. jank가 생기는 이유를 두가지로 설명했습니다.


1. 불규칙적인 이벤트 처리에 의해 두프레임 이상이 희생되는 경우.

    한프레임이 다 그려지기 직전에 이벤트 처리가 시작되면 이 이벤트는 무조건 2프레임 이상의 처리가 소요되는 거죠.


2. 한 프레임을 그리기 위해 두 프레임 이상의 소요되는 경우. 

    한 프레임을 그리기 위해 두 프레임 이상 소요되는 경우 ICS의 더블 버퍼링에서는 갱신하지 못하고 다음 턴까지 기다리는 경우가 있었습니다.


1번에 대한 해답은 이벤트를 읽어 디스플레이 리스트(HC부터 도입된 요소인데요. draw를 최상위 계층부터 타고 내려오지 않기 위해 뷰 객체에서 가까운 곳에 누적된 드로잉 정보를 모아두는 것입니다. 메모리는 더 많이 소모되지만 탐색 비용은 줄어들죠.)의 갱신을 하는 것을 항상 같은 시간 간격으로 하는 것입니다. 그럼 너무 늦게 시작해서 이번 프레임이 지난 다음에야 갱신될 수 있는 현상은 사라지게 되죠.


2번에 대한 해답은 한 프레임 더 여유가 있게 버퍼를 3개두는 트리플 버퍼링입니다. 이로서 그리는 시간이 더 걸려 2프레임이 넘어가는 일이 생기더라도 프레임이 건너 뛰는 일은 없어지게 된거죠.


PS: 디스플레이 리스트에 대한 자세한 글은 여기 ( http://dalinaum-kr.tumblr.com/post/19629843795/android-display-list )를 참고하세요 . 아래글

안드로이드 렌더링의 특징: 디스플레이 리스트

안드로이드 3.0(허니컴)부터 렌더링 부분이 많이 향상되었습니다. 하드웨어 가속이라는 특성도 그 중의 하나이죠. 또 다른 변화 중 하나는 디스플레이 리스트가 추가된 점입니다. 진저브레드까지 안드로이드는 변화를 전파하는 모델을 가지고 있었습니다.

이전 모델: invalidate가 상단까지 전파되는 모습.

이런 모델은 변경 사항을 ViewRoot까지 전파하는 문제가 있었습니다.

draw가 아래로 전파되는 모습.

전파되어 올라간 후 다시 아래로 내려오면서 그려야 합니다.

이런 모델은 하나의 뷰 변경이 꽤 많은 코드를 접근하게 하여 비효율적이었습니다. 하나의 뷰를 다시 그리기 위한 정보가 상단의 유아이 요소까지 올라가야만 얻을 수 있기 때문에 변경 내역을 전파하고 다시 그려야만 했습니다.

이 비효율에 대한 대안은 디스플레이 리스트를 사용하는 것입니다.

DisplayList의 예

뷰가 위와 같은 목록을 가짐에 따라 갱신하는 과정은 매우 간단합니다.

DisplayList로 build하는 상황

디스플레이 리스트는 각 UI요소를 그리기 위한 정보를 리스트로 모아둔 것입니다. UI 요소의 변경이 발생했을 때 위로 올라갈 필요가 없습니다. 그냥 해당 UI 요소의 디스플레이 리스트를 확인하고 사용하면 됩니다. 그리기 위해 필요한 정보가 한 곳에 모여있습니다.

더 깊은 내용을 알고 싶은 분들을 위해 조금 더 내려가겠습니다.

  • 디스플레이 리스트는 DisplayList와 GLES20DisplayList 객체를 구현되어 있습니다.
  • DisplayList는 추상 객체로 녹화의 시작과 끝을 지정할 수 있습니다.
  • GLES20DisplayList는 구체화된 객체로 화면 구성을 위한 비트맵 등의 리소스를 가지고 있습니다.
  • GLES20DisplayList는 녹화의 시작(start())에 GLES20RecordingCanvas를 생성하여 반환합니다.
  • GLES20RecordingCanvas는 GLES20Canvas의 자식 클래스로 풀(pool)을 만들어 자신을 관리합니다.
  • GLES20RecordingCanvas는 GLES20DisplayList의 비트맵 리스트에 접근할 수 있습니다.
  • GLES20Canvas는 DisplayRenderer를 생성합니다.
  • DisplayRenderer는 뷰를 위한 그림 연산들을 순차적인 목록으로 보관합니다.
  • DisplayRenderer는 다시 뷰를 그릴 (replay()) 수 있습니다.
  • DisplayRenderer는 OpenGLRenderer를 사용하여 하드웨어 가속을 사용합니다.
  • DisplayRenderer와 OpenGLRenderer는 상속관계가 아닙니다.
  • OpenGLRenderer가 GPU를 사용하여 실제 렌더링을 합니다. (SKIA의 간소화된 버전)

코드는 아래 디렉토리에서 확인하십시요.

  • frameworks/base/core/java/android/view
  • frameworks/base/core/jni/android
  • frameworks/base/core/jni/android/graphics
  • frameworks/base/libs/hwui

이 글의 이미지는 Romain Guy와 Chet Haase의 Google I/O 2011 Android Accelerated Rendering에서 발췌하였습니다.