Android Graphics(2) - Surfaceflinger 동작방식
해당 글은 이전글과 이어지는 내용으로 이전글을 참고하시면 이해하는데 도움이 될 것입니다.
해당 시리즈는 실제 깊은 코드 수준보단 간단한 이론을 바탕으로 글을 작성했기 때문에, 내용에 구현방식이 존재하지 않거나 누락된 내용이 존재할 수 있습니다. 하지만 Android 그래픽 파이프라인에 대한 일부를 이해할 수 있을 것입니다.
SurfaceFlinger
SurfaceFlinger
는 Android내 화면을 어떻게 그릴지에 대해 기여하는 컴포넌트로 여러 앱과 시스템 UI가 생성한 그래픽 버퍼들을 받아서 하나의 프레임으로 합성하고 디스플레이 하드웨어에 전달해주는
역할을 합니다.
이전 포스트에서 Surface에 구조에 대해 설명하면서 Producer와 Consumer의 의미에 대한 언급을 하였는데, 여기서 SurfaceFlinger
는 Producer가 생산한 그래픽 버퍼 데이터를 소비하는
Consumer의 역할을 수행합니다. SurfaceFlinger에 대해 자세하게 설명하기전 몇 가지의 용어를 정리하고 내용을 이어나가도록 하겠습니다.
WindowManagerService
SurfaceFlinger
에 대한 구조나 동작방식을 자세하게 알기 위해선 먼저 WindowManagerService
에 대해 이해해야합니다.
WindowManagerService
는 화면에 UI 컴포넌트들을 어떻게 보여줄지와 어떤 순서로 겹쳐질지를 결정하고 조율하는 역할을 합니다. 이때 여기서 관리하는 UI 컴포넌트를 Window
라고 합니다.
WindowManagerService는 또한 터치나 키보드 입력이 어디로 전달되어야하고 어떤 Window가 현재 포커스를 가지고 있어야 하는지와 같은 인터렉션 관리 역할을 수행합니다.
우리가 알고있는
WindowManager
라는 용어는 이런WindowManagerService
와 상호작용하는 Android의 인터페이스를 의미합니다.
SurfaceControl
SurfaceControl
은 Android에서 그래픽 레이어의 시각적 속성을 제어하고 관리하는 데 사용되는 객체로 특정 Surface가 화면에 어떻게 보여질지에 대한 시각적 속성들을 포함하고 제어합니다. 해당
요소들의 예시는 대표적으로 위치, 크기, z-order, 투명도등이 존재합니다.
SurfaceControl
의 속성 변경은 원자적으로 동작하며, WindowManagerService
같은 컴포넌트에 의해 SurfaceControl
의 속성 변경을 수행합니다. 이러한 모든 속성들은
추후 SurfaceFlinger
가 해당 Surface를 다른 Surface들과 합성할 때 어떻게 처리해야 하는지에 대한 정보가 됩니다.
레이어(layer)
레이어는 Surface
와 SurfaceControl
의 조합으로 Surface가 보유한 BufferQueue
와 위치,사이즈, Z-order같은 메타데이터를 포함하고 있습니다.
SurfaceFlinger의 동작 방식
SurfaceFlinger는 크게 Surface 생성과 합성의 역할을 수행합니다. 아래에서부터는 SurfaceFlinger의 2가지역할을 중점으로 SurfaceFlinger 동작 방식에 대한 내용을 풀어나가겠습니다.
Surface 생성
이전 포스트에서 Surface와 BufferQueue에 대한 부분을 다루었지만, 근본적으로 Surface가 어떻게 생성되고 어떻게 생산자와 소비자에게 연결되는지에 대한 내용은 언급되지 않았습니다. Surface가
생성되는 방식은 여러가지가 있지만, 그 중 화면을 표시하기 위해 생성되는 Surface
는 WindowManagerService
와 Surfaceflinger
의 상호작용에 의해 생성됩니다.
먼저 애플리케이션은 무엇인가를 화면에 그리려고 할 때 가장 먼저 WindowManger.addView()
같은 메서드를 호출하여 WindowManager
에게 UI를 보여줄 Window
를 생성해 달라
요청합니다. 해당 요청에는 Window
의 타입, 크기, 위치, 그리고 Z-Order등의 정보가 포함됩니다. 요청을 전달받은 WindowManagerService
는 주어진 바탕으로 Window
객체를
생성합니다.
하지만 아직 생성된 Window
에는 프레임을 그릴 수 있는 방법이 존재하지 않습니다. Window
에 그림을 그릴 수 있도록 WindowManagerService
는 Window
의 정보를
포함하여 SurfaceFlinger
에게 새로운 Surface
를 생성해달라고 요청을 전달합니다. 이때 해당 요청은 SurfaceControl
이라는 객체를 통해 이루어지게 됩니다.
SurfaceFlinger
는 ServiceControl
을 통해 전달된 정보을 받아서 새로운 레이어 객체와 연결된 Surface
객체를 생성합니다. 이때의 Surface
는 내부적으로
BufferQueue와 연결됩니다. 그 후 SurfaceFlinger
는 생성된 Surface
에 대한 정보를 다시 WindowManagerService
로 전달하는 과정을 끝으로 각 컴포넌트 간의 연결이
마무리 됩니다.
합성
SurfaceFlinger
는 BufferQueue의 Consumer 역할로서 그래픽 버퍼를 획득할 수 있습니다. 하지만 SurfaceFlinger
는 오로지 하나의 그래픽 버퍼만을 소비하는것이 아닙니다.
일반적으로 디바이스는 시스템 바와 액티비티에서 노출되는 화면 그리고 토스트 메시지등 Window를 보유한 컴포넌트들이 존재하는데, 해당 컴포넌들의 Window는 모두 각자의 Surface를 보유하고 버퍼를 생산해내는
역할을 수행합니다. 이때 SurfaceFlinger
는 여러 Surface
에서 생산되는 그래픽 버퍼의 데이터를 적절하게 조합하여 디스플레이 하드웨어에 넘겨주어야만 합니다. 이러한 과정을 합성이라고
표현합니다.
합성의 방식은 단순한 병합이 아닌, 각 레이어의 위치, 크기, 투명도들을 고려하여 적절한 화면으로 구성해야합니다. 사전에 SurfaceFlinger
는 WindowManagerService
와의 협력을 통해 각
화면 컴포넌트들에 대한 독립적인 레이어 정보를 가지고 있습니다. 이러한 상태에서 SurfaceFlinger
는 특정 신호가 올 때마다 자신이 관리하는 모든 레이어의 Surface에서 최신 그래픽 버퍼를 가져옵니다.
가져온 레이어들의 그래픽 버퍼들은 Z-Order에 맞춰 순서대로 쌓아 올리고 위치 및 투명도 등의 속성을 고려하여 픽셀들을 배치 및 블렌딩하는 작업을 수행합니다. 이렇게 최종적으로 합성된 단일 이미지를 디스플레이로
보냅니다.
HWC(Hardware Composer)
HWC
는 Hardware Composer의 약자로 Android 그래픽 시스템에서 디스플레이 합성을 수행하는 하드웨어 모듈로 최신 출시되는 대부분의 안드로이드 기기에 탑재되어 있습니다. 디스플레이 합성에
있어서 HWC
를 통한 처리 방식은 소프트웨어 방식보다 전력적인 측면에서 효율적이며, 지연과 버벅임이 없는 UI를 만들어 낼 수 있습니다. 이러한 이유로 SurfaceFlinger
는 화면을 업데이트해야 할
때 자신이 가지고 있는 모든 레이어 정보를 HWC
에게 최대한 전달하여 합성 연산을 위임합니다.
HWC
는 대부분의 레이어에 대한 합성 처리를 자신이 직접 수행하려고 하지만 일부 복잡해서 하드웨어적으로 처리할 수 없는 레이어의 경우 SurfaceFlinger
에게 해당 레이어의 합성을 맡깁니다.
SurfaceFlinger
는 이를 GPU를 사용하여 처리하며, 최종적으로 처리한 결과는 SurfaceFlinger
또는 HWC
가 나머지 레이어들과 함께 통합하여 디스플레이에 출력합니다. HWC
가 잘
처리할 수 있는 레이어는 단순한 배경화면과 같이 사각형 형태로이루어진 불투명한 영역들입니다. 반면 블렌딩이나 특정 효과가 있는 들어있는 레이어들은 SurfaceFlinger
가 GPU를 통해 처리를 합니다.
이제까지의 내용들을 정리하자면 SurfaceFlinger
와 HWC
모두 레이어를 합성할 수 있지만, 효율성 적인 측면에서 합성 연산을 HWC
에게 대부분을 위임하고 일부 복잡한
레이어는 SurfaceFlinger
가 처리하여 최종적으로 디스플레이에 출력되는 방식으로 수행됩니다.
안드로이드 개발자 옵션에는
하드웨어 레이어 업데이트 표시
라는 항목이 존재합니다. 이는 현재 HWC가 어떤 레이어를 직접 처리하고 있는지 시각적으로 확인해볼 수 있습니다.
VSync(Vertical Synchronization, 수직 동기화)
SurfaceFlinger
는 어떤 타이밍에 합성을 수행할까에 대한 부분은 Vsync
메커니즘과 연관이 되어있습니다. 안드로이드의 기본적인 렌더링 방식은 VSync
를 통해 동기화하는 과정으로 진행됩니다.
Android 시스템이 VSync
신호에 맞춰 프레임을 그릴 준비를 한다면, SurfaceFlinger
또한 VSync
신호에 맞춰 여러 레이어들을 합성하여 디스플레이로 전달합니다.
여기서 VSync
는 수직 동기화라고 불리며, Vertical Synchronization의 약자입니다. 컴퓨터 그래픽스에서는 Screen Tearing이라는 현상이 발생할 수 있는데, Vsync
는 이러한
현상을 방지하고 부드러운 화면 출력을 도와주는 역할을 합니다. VSync
는 화면 모니터의 재생률에 맞춰 GPU의 렌더링 속도를 동기화하는 기술로 렌더링 속도가 빠르더라도 모니터 재생률에 맞춰 고정되지만,
안정적으로 프레임을 제공할 수 있다는 특징이 존재합니다.
Screen Tearing은 디스플레이에서 발생되는 문제중 하나로 화면이 찢어진 것처럼 보이는 현상을 의미합니다. 해당 현상은 GPU의 프레임 생성속도와 모니터의 화면 재생률이 동기화되지 않았을 때 발생합니다. 대부분의 모니터는 화면을 위에서 아래로 순차적으로 그리는 방식으로 이미지를 표시합니다. 예를 들어, 프론트 버퍼의 그림이 디스플레이에 출력되는 동안 백 버퍼에 새로운 프레임이 빠르게 생성되고 그, 프레임을 프론트 버퍼로 넘겨버리게 될 경우 도중에 프레임이 바뀌게 되는 것이기 때문에 화면 상단과 하단의 다른 이미지를 동시에 표시하게 됩니다. 해당 문제에 대한 자세한 내용은 [해당 링크]를 참고하시면 좋습니다.
1편에 이어 지금까지 Surface
와 SurfaceFlinger
를 중심으로 Android 화면이 어떻게 구성되고 표시되는지 살펴보았습니다. 애플리케이션을 통해 생성된 그래픽 데이터가 여러 컴포넌트간에 상호
작용하여 디스플레이 출력하는 복잡한 과정을 정리하였는데, Android 그래픽 렌더링 파이프라인이 어떻게 구성되는지 간략하게나마 이해하는데 도움이 되었을 것이라 생각합니다.