(App surface, SF Layer)출처: https://lastyouth.tistory.com/24 [자유로운날개]
학기가 끝나서, 지난학기간 한 것들을 포스팅하려고 한다. 언제 다 할지는 미지수지만 그래도 틈틈히 할 예정이다.
Smartphone display에 대해 공부를 하다보면 필연적으로 해당 Smartphone 상 동작하는 운영체제에서 어떻게 화면을 그려내는지를 알아야 할 시점이 오게 된다.
사실 이전에 수행한 프로젝트 덕분에 Android 운영체제에서 SurfaceFlinger 서비스가 하는 일은 개략적으로 알고 있었으나, SurfaceFlinger는 어찌보면 실제 화면 출력을 담당하는 DisplayController로 보내기 전에 출력될 Layer를 관리하고 필요에 따라서 합성도 해주는 준 최종 관문의 역할을 한다.
SurfaceFlinger를 이해하는 것이 분명 Android Drawing Mechanism 을 이해하는데 초석이 되는것은 분명하다. 하지만 SurfaceFlinger는 시스템 서비스로써 여러 Application과 상호작용 한다는 점을 떠올리면, 결국 드로잉의 시작은 Application에서 해야함을 생각할 수 있다.
이에 본 포스팅에서는 Application의 Drawing과 그것이 SurfaceFlinger 서비스에 의해 관리되는 과정 일부를 살펴 볼 예정이다.
위 그림은 사용자의 상호작용에 의해 앱의 화면이 변화할 때, 화면이 다시 갱신되는 과정을 각 계층적으로 나타낸 것이다.
앱이 다시 그린 화면은 SurfaceFlinger에 의해 합성되거나 Display Controller에 의해 합성되어 실제 Display에 의해 출력된다. 좀 더 자세히 알아보기 위해 SurfaceFlinger의 역할에 대해 소개한다.
SurfaceFlinger는 크게 3가지의 역할을 수행하는데, 각각 Layer Management, Vsync Processing 그리고 Screen Refreshing이다.
SurfaceFlinger는 하나의 프로세스로 존재하는 시스템 서비스로써 SurfaceFlinger 에 접근하기 위해서는 IPC를 이용해야 하며, 이를 위해 제공하는 인터페이스를 통해 SurfaceFlinger의 기능을 이용한다. SurfaceFlinger는 두 가지 종류의 인터페이스를 지원한다.
이 중 ISurfaceComposerClient는 Client 접미사에서 추측이 가능하듯이 각 어플리케이션마다 관리되는 객체를 컨트롤하기 위한 용도로 사용되며, 주된 기능은 각 어플리케이션 별 Surface를 생성하는 것이다.
ISurfaceComposer는 SurfaceFlinger에 의해 관리되는 객체를 컨트롤하기 위한 용도이다.
이 중 먼저 Layer가 어떻게 관리되는지를 살펴보겠다.
Drawing Process (App side)
Layer에 대해 살펴보기 전에 먼저 각 어플리케이션의 View가 어떻게 관리되는지를 알아볼 필요가 있다.
각 어플리케이션은 여러 뷰를 가질 수 있으며, DecorView를 최종 루트로 가지는 트리 구조로 관리된다.
DecorView는 그릴 것이 있는 경우, 할당받은 Surface에 그리게 된다. 따라서 Surface는 각 Application별로 최소 하나는 가지고 있게 된다.
그럼 여기서 Surface는 뭐고 Layer는 무엇인가 라는 질문을 할 수 있는데, 엄밀히 말하면 둘은 같은 개념이다. 다음을 보자.
SurfaceComposerClient로 createSurface 메서드를 호출하면, ISurfaceComposer 인터페이스를 이용해 SurfaceFlinger에 Layer를 하나 만드는 것을 볼 수 있다.
즉 Surface는 어플리케이션이 각 소유하는 개념이라면, Layer는 SurfaceFlinger에 의해 관리되는 개념이라고 보면 된다.
그러면 이제 각 어플리케이션은 적어도 하나의 Surface를 가지고, 이 Surface는 SurfaceFlinger에 의해 Layer로써 관리된다는 것을 알았다.
본격적인 Drawing Process를 설명하기 전 몇 가지 객체에 대해서 설명을 하도록 하겠다.
먼저 Choreographer 객체에 대한 설명을 하도록 한다. Choreographer에서는 SurfaceFlinger에서 송신되는 Vsync Event를 요청/수신하여 다음 작업들을 처리한다.
1. Input Event Handling
2. Self-Invalidation
3. Animation
다음으로 ViewRootImpl은 DecorView와 Choreographer를 연결해주는 일종의 핸들러 역할을 하는 객체로써, DecorView와 Choreographer 사이에서 둘의 요청을 처리하고 중계해주는 역할을 한다.
이제 어플리케이션의 Drawing Process를 살펴보도록 하자.
실제 그리기는 현재 foreground에 표시되고 있는 어플리케이션에서 그릴 것이 생김으로써 시작된다. 무언가 그릴 것이 생기면 ViewRootImpl 객체 내에 있는 scheduleTraversal 메서드가 호출된다.
scheduleTraversal 메서드 내부에서는 Choreographer 객체에게 다음 vsync를 예약해달라는 요청을 보내게 된다. 이 작업이 아래 그림에서 Invalidate와 Vsync scheduling에 해당된다.
Vsync 예약 요청을 받은 Choreographer는 SurfaceFlinger로 하여금 다음 Vsync 도착 시 알려달라고 요청한다.
다음 Vsync signal이 도착하면 Choreographer는 ViewRootImpl의 performTraversal 메서드를 호출한다. performTraversal 메서드 내부에서는 다시 그려야 될 부분을 measure하고, layout을 재 구성한 다음, performDraw 메서드를 호출하여 그리기를 수행한다.
ViewRootImpl 객체에서는 performDraw 메서드에 의해 호출되는 draw 메서드가 최종적으로 DecorView로 하여금 할당받은 Surface에 그리게 한다.
이 때, HardwareRenderer를 이용하는 경우와 Software Reneder를 이용하는 경우로 나뉘게 된다.
전자는 GPU를 이용하여 Surface에 그리는 것이며, 후자는 CPU로 그리는 것이다. Android 3.0 버전 이후로 하드웨어 가속을 지원하면서 이후 Android에서는 GPU를 이용하여 그리는 것을 기본값으로 한다.
GPU든 CPU든 Surface에 View의 변한 부분을 그리는 것은 동일하지만, 그리는 방법이 다른데, 이 부분에 대한 구체적인 내용은 다음을 참조하면 된다.
https://developer.android.com/guide/topics/graphics/hardware-accel.html#model
간략히 살펴보면, CPU Drawing(Software Rendering)은 Decorview 에서 시작하여 자식 뷰로 내려가면서 그려내는 방법을 사용하고, GPU Drawing은 뷰가 직접 그리는 것이 아닌 DisplayList 라는 추가적인 객체를 이용하여 그리기 작업을 수행하는 것으로 보인다.
그리기가 완료되면, 할당받은 Surface가 변했으므로, 이를 SurfaceFlinger에게 통지해야 한다.
Layer Management
각 어플리케이션이 Surface를 생성하는 과정과, Surface에 필요한 부분을 그리는 방법을 설명하였다. 위에서도 언급했듯이, 각 어플리케이션은 적어도 하나의 Surface를 가지고, 이 Surface는 SurfaceFlinger에 의해 Layer로써 관리된다는 것을 알았다. 이를 다시 그림으로 나타내면 다음과 같다.
그러면 Layer와 Surface는 동등한 개념이지만, 존재하는 위치가 다르다. Surface는 어떻게 다시 그려진 내용을 Layer로 하여금 알게 하는 것일까?
이를 알기 위해서 실제 그림에 대한 데이터가 존재하는 위치는 다른 데 있다는 것을 알아야 한다. Surface와 Layer는 추상화된 객체일 뿐이며, 실제 화면에 대한 raw data가 존재하는 곳은 따로 있다. 이를 Buffer라고 한다.
USB 디버깅이 허용된 디바이스를 연결하고 쉘에 adb shell dumpsys SurfaceFlinger 를 입력하면 현재 표시되는 화면에 대한 SurfaceFlinger의 상태에 대해 표시가 되는데 여기에 보면 다음과 같은 부분을 찾을 수 있다.
이 부분이 할당된 Buffer로써 실제 화면에 대한 정보를 담고있다. 각 Surface가 Buffer를 하나씩 소유하게 되면, 메모리가 남아나지 않을 것이므로, 어느 정도의 Buffer를 할당해 두고 필요할 때 마다 획득하여 사용하도록 설계되었다. 즉 Producer, Consumer 모델을 따른다고 볼 수 있다.
각 Surface와 Layer를 이어주기 위해 BufferQueue가 사용된다. 이름에서 알 수 있듯이 BufferQueue에는 3개의 Buffer가 들어있다.
Buffer는 누가 사용중이냐에 따라서 4가지의 상태를 가지는데, 각각 다음과 같다.
1. Free : Queue 안에 존재하며, Surface 혹은 Layer에 의해 사용되고 있지 않으며, 그려지지도 않은 상태
2. dequeued : Queue에서 나왔으며, Surface에 의해 점유 중인 상태
3. queued : Surface에 의해 쓰여진 buffer가 Queue 안에 있는 상태
4. acquired : queued 상태인 buffer가 Layer에 의해 점유 중인 상태
그러면 이제 실제로 Surface와 Layer가 어떻게 통신하는지 그림으로 보면서 알아보자.
그림에는 SurfaceFlinger가 소유한 Layer는 표시하지 않았다.
초기 상태에는 free buffer가 queue에 3개 들어있는 상태로 시작한다. queue에는 buffer 자체가 들어있는 것이 아니라 buffer의 handle이 들어가 있다.
여기서 어플리케이션이 소유한 surface가 invalidate 되면, surface는 buffer의 핸들을 획득한다. 이 시점에서 buffer의 상태는 dequeued가 된다.
획득한 buffer의 핸들이 가리키는 곳에 변경된 내용을 그린다. 그리는 주체는 앞서 설명한 CPU 혹은 GPU 중 하나가 된다.
변경된 buffer 핸들을 다시 queue에 넣는다. 이 시점에서 buffer의 상태는 queued가 된다.
다음 vsync가 도달하면, Surface는 다시 그리기 작업을 수행하게 되고, SurfaceFlinger는 queued된 buffer가 queue 내에 존재하게 되므로, 이제 Layer Composing 을 수행 하기 위해 해당 buffer를 획득한다.
출처: https://lastyouth.tistory.com/24 [자유로운날개]
'Android' 카테고리의 다른 글
Android 그래픽 시스템의 발전 과정 (0) | 2017.12.19 |
---|---|
안드로이드 터치 이벤트 흐름 (0) | 2017.12.19 |
Android Camera2 API Step By Step : Preview (0) | 2017.11.26 |
Async작업을 위한 handler, looper, Thread! (0) | 2017.10.25 |
안드로이드 기초 쌓기 - 해상도와 DP (0) | 2017.07.13 |