티스토리 뷰

728x90
반응형

Intro

Surface, SurfaceHodler, EGLSurface, SurfaceView, GLSurfaceView, SurfaceTexture, TextureView, SurfaceFlinger에 대해 모든 개발자들이 알아야 하는 것
 
이 문서는 안드로이드의 “시스템 레벨” 그래픽 아키텍처의 필수 요소들과, 그것들 애플리케이션 프레임워크와 멀티미디어 시스템에 의해 어떻게 이용되는지를 설명한다. 만일 여러분이 왜 SurfaceView와 TextureView가 그들 각자의 방식으로 동작하는지, 또는 Surface와 EGLSurface가 어떻게 연동하는지가 궁금하다면 제대로 찾아온 것이다.
 
이 문서는 여러분이 안드로이드 디바이스와 애플리케이션 개발에 어느 정도 익숙하다고 가정한다. 하지만, 안드로이드 애플리케이션 프레임워크의 세부적인 지식을 필요로 하지는 않는다. 일부 API 호출들을 언급될 거지만, 다른 공식 도움말과 크게 겹치지는 않는다. 이 문서의 목적은 프레임 렌더링 과정에서 포함된 중요한 개념을 제공하고, 이를 통해 여러분이 애플리케이션을 설계할 때 적절한 선택을 할 수 있도록 해주는 것에 있다. 이러한 목적을 위해 우리는 UI 클래스를 어떻게 사용하는지 보다는 그것이 어떻게 동작하는 지를 Bottom up 방식으로 살펴볼 것이다.
 
이 문서의 이전 섹션은 이후에 나올 섹션의 배경 지식을 포함하기 때문에, 관심있는 섹션을 스킵해서 읽는 것 보다는차례대로 읽는 것을 권장한다. 우리는 안드로이드 그래픽 버퍼에 대한 설명을 시작으로, 합성 및 디스플레이 메카니즘에 대해 설명하고 그런 다음 Compositor에게 그래픽 데이터를 제공하는 고수준 메커니즘을 다룰 것이다.
 
이 문서는 주로 안드로이드 4.4버전(“kitkat”)의 시스템을 기준으로 다룬다. 이보다 이전 버전이나 앞으로 나올 버전 또한 이 문서에서 설명하는 것과 다르게 동작할 것이다. 안드로이드 버전에 특화된 특징들은 문서의 부분에서 언급될 것이다.
 
문서 여러 곳에서 AOSP 소스나 Grafika 소스 코드를 참조할 것이다. Grafika는 테스팅을 위한 구글 오픈 소스 프로젝트이며, 링크는 다음과 같다. 

BufferQueue

안드로이드 그래픽 시스템이 어떻게 동작하는 지를 이해하기 위해 우리는 화면 뒤에서 어떤 일이 벌어지는 지를 살펴봐야 한다. 안드로이드에서 모든 그래픽 요소들의 중심에는 BufferQueue라는 클래스가 있다. 그것의 역할은 아주 간단하다. 그래픽 데이터 버퍼들을 생성하는 컴포넌트 ("생산자")와 이 데이터를 받아서 디스플레이 하거나 추가 프로세싱을 하려는 컴포넌트 ("소비자")를 연결시켜주는 것이다. 생산자와 소비자는 서로 다른 프로세스에 존재할 수도 있다. 시스템 전반에 걸쳐 그래픽 데이터 버퍼를 이동시키는 거의 모든 작업들이 BufferQueue를 통해 처리된다. 
 
기본적인 사용법은 매우 간단하다. 생산자는 일련의 버퍼 특징들(가령, 너비, 높이, 픽셀 포맷, 용도 플래그)을 기술하여, 비어 있는 버퍼를 요청한다 (dequeBuffer()). 생산자는 버퍼를 채운 다음, 이를 다시 큐에 반환한다 (queueBuffer()). 얼마 뒤에, 소비자는 버퍼를 획득하고 (acquireBuffer()), 버퍼 컨텐츠를 활용한다. 소비자는 버퍼를 다 활용했으면, 이를 해제하고 큐에 반환한다 (releaseBuffer()).
 
대부분의 최신 안드로이드 기기들은 "sync framework"를 지원한다. 이것은 시스템이 그래픽 데이터를 비동기적으로 처리할 수 있는 하드웨어 컴포넌트들을 서로 연결해서 동작시킬 때 많은 도움을 준다. 예를 들어, 생산자가 일련의 OpenGL ES 드로잉 명령어들을 내보낸 다음, 렌더링이 완료되기 전에 출력 버퍼(Output Buffer)를 큐에 넣는다. 그 출력 버퍼에는 컨텐츠가 준비됐을 때 신호를 보내는 fence가 포함되어 있다. 버퍼에 포함된 두번째 fence는 프리 리스트에 반환된 버퍼 정보를  알려준다. 그래서 소비자는 컨텐츠가 여전히 사용중인 동안에도 버퍼를 해제할 수 있다 (역자주-실제 해제가 가능한 시점에 fence에서 시그널을 발생시킨다). 이러한 접근법은 버퍼가 시스템 전반에 걸쳐 이동되기 때문에 지연 시간과 처리 속도를 향상시킨다.
 
(역자주) Sync Framework는 BufferQueue의 Buffer 핸들링 동작을 비동기로 처리할 수 있도록 해주는 Framework다. 이러한 작업을 처리하기 위해, 위에서 설명한 fence가 활용된다.
 
BufferQueue의 몇몇 특징(가령, BufferQueue가 가질 수 있는 최대 버퍼 개수 등)은 생산자와 소비자가 공동으로 결정한다. 
 
BufferQueue는 그들에게 필요한 버퍼를 할당할 책임이 있다. 버퍼들은 그 특징들이 변하지 않는 다면 그대로 유지된다. 예를 들어, 생산자가 기존과 다른 크기를 가진 버퍼를 요청했다면, 기존 버퍼들은 모두 free가 되고, 요청한 크기의 새로운 버퍼들이 할당될 것이다.
 
이러한 버퍼의 데이터 구조는 항상 소비자에 의해 생성이 되고, "소유"된다. 안드로이드 4.3에서는 생산자는 리모트 프로세스에 존재할 수 있었지만, 소비자는 큐가 생성된 프로세스 존재해야만 했다. 이것은 안드로이드 4.4에서 보다 일반적인 구현 방식으로 발전했다.
 
버퍼의 컨텐츠들은 BufferQueue에 의해 절대 복사되지 않는다. 대용량 데이터를 복사해서 이동하는 것은 매우 비효율적이다. 대신, 버퍼들은 항상 핸들을 통해 전달된다.

gralloc HAL

실제 버퍼 할당은 "gralloc" 라는 메모리 할당자에 의해 처리된다. 이것은 HW 벤더별 HAL 인터페이스를  통해 구현된다. (hardware/libhardware/include/hardware/gralloc.h 참조)
alloc() 함수는 여러분의 예상처럼 할당될 버퍼의 너비, 높이, 픽셀 포맷, 그리고 용도 플래그를 인자로 받는다. 
 
gralloc 할당자는 네이티비 힙 상에 메모리를 할당하는 또다른 방식이 아니다. 몇몇 상황에서, 할당된 메모리들은 캐시 일관성(Cache-coherent)이 유지되지 않을수도 있고, 또는 User Space에서 전혀 액세스하지 못할 수 있다. 이러한 할당된 버퍼의 특성들은 용도 플래그 인자에 의해 결정되며, 다음과 같은 특성들을 포함하고 있다. 
  • Software(CPU)에서 얼마나 자주 메모리를 액세스 할건지
  • Hardware(GPU)에서 얼마나 자주 메모리를 액세스 할건지
  • OpenGL ES("GLES") 텍스처로서 메모리가 이용될 것인지
  • 비디오 인코더로서 메모리가 이용될 것인지
예를 들면, 여러분이 픽셀 포맷을 RGBA 8888로 정하고 버퍼가 소프트웨어에서 액세스 될 것(여러분의 애플리케이션이 픽셀을 직접 접근하는 것을 의미)이라고 설정했다면, 할당자는 R-G-B-A 순서로 픽셀마다 4바이트를 가진 버퍼를 생성해야 한다. 만약 버퍼가 하드웨어에서만 접근이 가능하고 GLES 텍스처로서 활용된다면, 할당자는 GLES 드라이버가 원하는 용도 (B-G-R-A 오더링과 넌리니어 "스위즐링" 레이아웃, 별도의 컬러 포맷 등)을 처리할 것이다. 하드웨어에 적합한 포맷을 사용하면 성능을 향상시킬 수 있다.
 
특정 플랫폼에서 일부 값을은 서로 조합시킬 수 없다. 예를 들어, "video encoder" 플래그는 YUV 픽셀을 요구하는데, 여기에 추가로 RGBA 8888 픽셀 포맷을 활용하는 "software access" 플래그을 설정하는 것은 실패할 것이다. 
 
gralloc 할당자에 의해 반환된 핸들은 바인더를 통해 프로세스 사이에서 서로 전달할 수 있다. 
반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday