코딩하기 좋은날
Android Graphics 번역 5편 - Surface와 SurfaceHolder, canvasRendering 본문
참조: https://source.android.com/devices/graphics/arch-sh#canvashttps://source.android.com/devices/graphics/surfaceflinger-windowmanager
Surface
- screen compositor에 의해 관리되는 raw buffer에 대한 처리를 한다. Surface는 보통 image buffer의 consumer로 부터 만들어진다. — image buffer의 예는 SurfaceTexture, MediaRecorder, android.renderscript.Allocation, android.opengl.EGL14 , android.media.MediaPlayer , android.hardware.camera2.CameraDevice 등
- Surface는 할당된 cosumer에게 weak reference로 동작한다. 자체적으로 상위 consumer가 회수되는 것을 방지하지 않는다.
- Surface는 BufferQueue의 생산자(producer)역할을 하며, SurfaceFlinger는 항상은 아니지만, 흔히 이러한 BufferQueue의 소비자(Consumer)역할을 한다. Surfac에 렌더링하면, 그 결과는 버퍼 형태로 생성이 되고 이것은 소비자에게 전달 된다. Surface는 우리가 drawing 할 수 있는 단순한 메모리 영역은 아니다.
- Display용 Surface를 위한 BufferQueue는 보통 트리플 버퍼링으로 구성된다. 그러나 버퍼들은 요청이 있을때만 할당되며, 만약 생산자가 버퍼를 느리게 생성한다면(예를 들자면, 60fps 디스플레이에서 30fps 속도로 렌더링이 처리되는 경우), Queue에는 2개의 할당된 버퍼만 있으면 된다. dumpsys SurfaceFlinger 명령어의 출력 결과를 통해 모든 레이어와 연관된 버퍼들에 대한 정보를 확인할 수 있다.
SurfaceHolder
Surface와 연관된 작업을 위해서는 SurfaceHolder, 특히 SurfaceView가 필요하다. Suface가 compositor에 의해 관리되는 버퍼를 표현한다면, SurfaceHolder는 앱에 의해 관리되고, Surface의 크기나 포맷 같은 고수준의 정보를 관리한다.
일반적으로 말하면, View와 관련된 어떠한 작업을 하든 SurfaceHolder를 포함한다. MediaCodec 같은 다른 몇몇 API들은 Surface상에서 동작한다. 여기서 쉽게 SurfaceHolder를 통해 Surface를 얻을 수 있다. 따라서 SurfaceHolder를 얻을 수 있다면, Surface또한 얻을 수 있다.
CanvasRendering
과거에는 모든 rendering이 소프트웨어에서 처리 됐다. 그리고 요즘도 이런 방식으로 처리 할 수 있다. rendering의 low-level 구현은 Skia 그래픽스 라이브러리에 의해 제공된다. Skia는 byte들을 적절하게 버퍼의 형태로 구성한다. 그리고 버퍼가 두 클라이언트에 의해 한번에 업데이트 되는 것을 방지하기 위해, 버퍼에 액세스 하기위해서는 lockcanvas()를 사용해야 한다. lockcanvas()는 버퍼에 lock을 걸고 drawing을 처리하기 위한 Canvas를 리턴한다. 그리고 unlockCanvasAndPost()는 버퍼에 lock을 해제하고, 그것을 compositor에게 전달한다. — 위의 두 함수는 SurfaceHolder 인터페이스의 함수이다.
시간이 지남에 따라, 범용 3D엔진을 가진 디바이스가 출시되면서 안드로이드는 렌더링에 OpenGL ES를 적용했다. 때문에 예전 API를 사용한 앱 뿐만 아니라 프레임워크 코드가 제대로 동작하게 하는 것이 중요했다. 따라서 이를 위해 하드웨어 가속 Canvas API가 등장했다(이때가 Android 3.0(API level11)이다. 다음문서에서 확인할 수 있듯 이 API들의 지원에는 우여곡절들이 있었다. View의 onDraw() 함수에 제공된 Canvas는 하드웨어 가속이 가능하지만, App이 lockCanvas() 함수를 가지고 직접 Surface에 lock을 걸어서 얻은 Canvas와는 다르다는 것을 특히 주의해야 한다.
만약 Canvas에 접근을 위해 Surface에 lock을 걸었다면, CPU 렌더러는 BufferQueue의 생산자 쪽에 연결하고, Surface가 소멸될 때까지 연결을 지속한다. 대부분의 다른 생산자(ex. GLES)는 Surface에 연결을 끊거나 재연결을 할 수 있지만, Canvas 기반 CPU렌더러는 그렇게 할 수 없다. 따라서 Canvas에 lock을 걸었다면, GLES로 Surface에 drawing을 할 수 없거나 Surface로 비디오 디코더에서 출력된 프레임을 보낼 수 없다는 것을 의미한다.
처음에는 생산자가 BufferQueue에 버퍼를 요청하고, 이것은 할당과 동시에 0으로 초기화된다. 초기화는 프로세스간 부주의로 데이터가 공유되는 것을 방지하기 위해 필요하다. 만약 버퍼를 재사용 한다면 이전의 컨테츠들은 여전히 존재 할 것이다. 만약 아무것도 그리지 않고 반복해서 lockcanvas()와 unlockCanvasAndPost()를 호출한다면, 이전에 렌더링됐던 프레임이 계속 반복될 것이다.
애플리케이션이 Canvas를 이용하지 않고 Surface에 직접 drawing하는 주요 방법은 OpenL ES를 사용하는 것이다. 이것은 EGLSurface와 OpenGL ES절에서 설명할 것이다.
위의 이미지는 안드로이드의 그래픽 서브 시스템을 도식화한 것이다. Canvas가 HWUI 또는 SKIA를 이용해서 Surface에 요청하는 구조이다.
Software 기반 drawing vs Hardware acclerated drawing
Software 기반 drawing model은 다음과 같은 순서를 가진다.
- Invalidate the hierarchy
- Draw the hierarchy
Hardware acclerated drawing model은 다음과 같은 순서를 가진다.
- Invalidate the hierarchy
- Record and update display lists
- Draw the display lists
소프트웨어 기반 방식은, 실제로 변경되지 않은 View에 대해서도 계층 구조로 인해 View를 다시 그려야하는 문제가 있었다. 또한 custom view에서 view의 draw코드에 영향을 미치는 동작을 할때마다 invalidate를 항상 호출해주어야 한다.
반면 Hardware acclerated drawing 방식은 display list를 관리한다. redrawing이 그려져야 하는 View(dirty region 이 포함되어 있는)들을 display list에서 관리하며 display list에 있는 view들만 실제로 redrawing이 진행된다. 또한, rotation이나 alpha 값 변경등의 동작에서 invalidate()를 호출해주지 않아도 자동으로 수행되기 때문에 애니메이션에도 이점이 있다.
'Android' 카테고리의 다른 글
Android Graphics 번역 7편 - SurfaceTexture와 TextureView (0) | 2022.07.24 |
---|---|
Android Graphics 번역 6편 - SurfaceView와 GLSurfaceView (0) | 2022.07.24 |
Android Graphics 번역 4편 - SurfaceFlinger와 Hardware composer (0) | 2022.07.05 |
Android Graphics 번역 3편 - BufferQueue and Gralloc (0) | 2022.07.05 |
Android Graphics 번역 2편 - Architecture (0) | 2022.07.02 |