BufferQueue와 gralloc

안드로이드 그래픽 시스템을 이해하는 것은 BufferQueue와 gralloc HAL로 시작됩니다.

BufferQueue 클래스는 Android의 모든 그래픽 중심에 있습니다. BufferQueue의 역할은 간단합니다. 그래픽 데이터 버퍼를 생성하는 생성자(Producer)로부터 해당 버퍼를 처리 하거나 화면에 표현할 수 있는 소비자(Consumer)에 연결하는 것입니다. 시스템을 통해 그래픽 데이터의 버퍼를 이동시키는 거의 모든 것이 BufferQueue에 의존합니다.

gralloc 메모리 할당자는 버퍼 할당을 수행하며 벤더 고유의 HAL(Hardware Abstractraction Layer) 인터페이스를 통해 구현됩니다 (hardware / libhardware / include / hardware / gralloc.h 참조). alloc () 함수는 예상되는 인수인 가로, 세로, 픽셀 형식, 플레그 등을 취합니다.

BufferQueue Producer 와 Consumer

기본 사용법은 간단합니다. 제작자는 너비, 높이, 픽셀 형식 및 사용 플래그를 포함하는 일련의 특성을 지정하는 사용 가능한 버퍼 (dequeueBuffer ())를 요청합니다. 생성자는 버퍼를 채우고 큐에 반환합니다 (queueBuffer ()). 나중에 Consumer는 버퍼 (acquireBuffer ())를 획득하고 버퍼 내용을 사용합니다. Consumer가 완료되면 버퍼를 대기열로 반환합니다 (releaseBuffer ()).

최근 Android 기기는 동기화 프레임워크를 지원하므로 그래픽 데이터를 비동기 적으로 조작 할 수있는 하드웨어 구성 요소와 결합 할 때 시스템에서 멋진 작업을 수행 할 수 있습니다. 예를 들어 Producer는 일련의 OpenGL ES 드로잉 명령을 제출 한 다음 렌더링이 완료되기 전에 출력 버퍼를 대기열에 추가 할 수 있습니다. 버퍼에는 내용 준비가되었을 때 신호를 보내는 펜스(fence)가 있습니다. 두 번째 펜스가 free list로 반환 될 때 버퍼를 따라 가며, Consumer는 내용이 아직 사용 중일 때 버퍼를 릴리즈 할 수 있습니다. 이 방법은 버퍼가 시스템을 통과 할 때 대기 시간과 처리량을 향상시킵니다.

큐의 최대 특성 (예 : 보유 할 수있는 최대 버퍼 수)은 Producer와 Consumer가 공동으로 결정합니다. 그러나 BufferQueue는 필요에 따라 버퍼를 할당합니다. 특성이 변경되지 않으면 버퍼가 유지됩니다. 예를 들어 Producer가 다른 크기의 버퍼를 요청하면 이전 버퍼가 해제되고 필요에 따라 새 버퍼가 할당됩니다.

Producer와 Consumer는 서로 다른 프로세스에서 존재 할 수 있습니다. 현재 Consumer는 항상 데이터 구조를 만들고 소유합니다. 이전 버전의 Android에서는 Producer 측만 바인더 되었었습니다 (즉, Producer는 원격 프로세스에 있지만 Consumer는 대기열이 생성 된 프로세스에 있어야했습니다). Android 4.4 이상 버전은 좀 더 일반적인 구현으로 옮겨졌습니다.

버퍼 내용은 BufferQueue에 의해 절대로 복사되지 않습니다 (많은 데이터를 이동하는 것은 매우 비효율적입니다). 대신 버퍼는 항상 핸들에 의해 전달됩니다.

gralloc HAL usage flags

gralloc 할당자는 단지 네이티브 힙에 메모리를 할당하는 방법이 아닙니다. 일부 상황에서는 할당 된 메모리가 캐시 일관성이 없거나 사용자 공간에서 완전히 액세스 할 수없는 경우가 있습니다. 할당의 성격은 다음과 같은 속성을 포함하는 사용 플래그에 의해 결정됩니다.

  • 소프트웨어 (CPU)에서 메모리에 액세스하는 빈도
  • 하드웨어 (GPU)에서 메모리에 액세스하는 빈도
  • 메모리가 OpenGL ES (GLES) 텍스처로 사용되는지 여부
  • 비디오 엔코더에서 메모리를 사용할 지 여부

예를 들어, 형식이 RGBA 8888 픽셀을 지정하고 버퍼가 소프트웨어에서 액세스 할 것이라는 것을 나타내면 (즉, 애플리케이션이 픽셀을 직접 다룬다는 의미입니다) 할당자는 R-G-B-A 순서로 픽셀 당 4 바이트의 버퍼를 만들어야합니다. 대신에 버퍼가 하드웨어와 GLES 텍스쳐로만 액세스된다면, 할당자는 GLES 드라이버가 원하는 것, BGRA 정렬, 비선형 swizzled 레이아웃, 다른 색 포맷 등을 할 수 있다고 말합니다. 기본 형식은 성능을 향상시킬 수 있습니다.

일부 값은 특정 플랫폼에서 결합 될 수 없습니다. 예를 들어 비디오 엔코더 플래그에 YUV 픽셀이 필요할 수 있으므로 소프트웨어 액세스를 추가하고 RGBA 8888을 지정하면 실패합니다.

gralloc 할당 자에 의해 반환 된 핸들은 Binder를 통해 프로세스간에 전달 될 수 있습니다.

systrace를 통한 BufferQueue 추적

그래픽 버퍼가 어떻게 움직이는지를 이해하려면 systrace를 사용하십시오. 관련 시스템 프레임 워크 코드와 마찬가지로 시스템 수준의 그래픽 코드도 잘 구현되어 있습니다.

systrace를 효과적으로 사용하는 방법에 대한 자세한 설명은 다소 문서가 깁니다. gfx, view 및 sched 태그를 활성화하여 시작하십시오. 또한 추적에 BufferQueues가 표시됩니다.  GRAFIKA의 “Play video (SurfaceView)”을 실행하는 동안 추적을 할 경우, SurfaceView 표시된 행은 주어진 시간에 대기하고 얼마나 많은 버퍼를 알려줍니다.

앱이 활성화되어있는 동안 값이 증가하여 MediaCodec 디코더에 의해 프레임 렌더링이 트리거되고 SurfaceFlinger가 작업을 수행하면서 버퍼를 소비하면서 값이 감소합니다. 30fps로 비디오를 보여줄 때 ~ 60fps 디스플레이가 소스를 쉽게 따라갈 수 있기 때문에 큐의 값은 0에서 1까지 다양합니다. (SurfaceFlinger는 초당 60 회가 아니라 작업이 끝난 후에 만 깨어납니다. 시스템은 작업을하지 않으려 고하며 화면을 업데이트하는 것이 없으면 VSYNC를 완전히 비활성화합니다.)

Grafika의 “Play video (TextureView)”로 전환하고 새로운 추적을 만들면, com.android.grafika / com.android.grafika.PlayMovieActivity라는 행이 표시됩니다. 이것이 바로 또 다른 BufferQueue 인 메인 UI 레이어입니다. TextureView는 (별도의 레이어가 아닌) UI 레이어로 렌더링되기 때문에 여기에 모든 비디오 구동 업데이트가 표시됩니다.

systrace 도구에 대한 자세한 내용은 Systrace 설명서를 참조하십시오.

카테고리: Graphics

2개의 댓글

김정원 · 2022년 9월 2일 9:13 오전

좋은 글 감사합니다!

    Charlezz · 2022년 9월 10일 12:00 오후

    감사합니다!

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.