하드웨어 컴포저 HAL 구현

하드웨어 컴포저(HWC) HAL은 SurfaceFlinger에서 수신한 레이어를 조합하여 OpenGL ES(GLES)와 GPU에서 수행하는 조합 작업의 양을 줄입니다.

HWC는 오버레이 및 2D 블리터와 같은 객체를 추상화하여 표면을 조합하고, 특수한 창 조합 하드웨어와 통신하여 창을 조합합니다. SurfaceFlinger와 GPU를 조합하는 대신 HWC를 사용하여 창을 조합합니다. 대부분의 GPU는 조합에 최적화되어 있지 않으며 GPU가 SurfaceFlinger의 레이어를 조합할 때 앱은 렌더링에 GPU를 사용할 수 없습니다.

HWC 구현은 다음을 지원해야 합니다.

  • 4개 이상의 오버레이:
    • 상태 표시줄
    • 시스템 표시줄
    • 배경화면/백그라운드
  • 디스플레이보다 큰 레이어(예: 배경화면)
  • 사전 곱셈 연산이 동시에 처리된 픽셀당 알파 블렌딩 및 평면당 알파 블렌딩
  • 보호된 동영상 재생을 위한 하드웨어 경로
  • RGBA 패킹 순서, YUV 형식, 타일링, 혼합 및 스트라이드 속성

HWC를 구현하는 방법은 다음과 같습니다.

  1. 비운영 HWC를 구현하고 모든 조합 작업을 GLES로 전송합니다.
  2. 조합을 HWC에 점진적으로 위임하기 위한 알고리즘을 구현합니다. 예를 들어 HWC의 오버레이 하드웨어에 처음 세 개 또는 네 개의 표면만 위임합니다.
  3. HWC를 최적화합니다. 여기에는 다음과 같은 사항이 포함될 수 있습니다.
    • GPU 부하 감소를 최대화하는 표면을 선택하여 HWC로 전송합니다.
    • 화면이 업데이트되는지 확인합니다. 업데이트되지 않는 경우 HWC 대신 GLES에 조합을 위임하여 전원을 절약합니다. 화면이 다시 업데이트되면 조합을 HWC로 계속 오프로드합니다.
    • 다음과 같은 일반적인 사용 사례를 준비합니다.
      • 상태 표시줄, 시스템 표시줄, 앱 창 및 라이브 배경화면이 포함된 홈 화면
      • 세로 모드 및 가로 모드를 지원하는 전체화면 게임
      • 자막 방송 및 재생 컨트롤이 있는 전체화면 동영상
      • 보호된 동영상 재생
      • 화면 분할 다중 창

HWC 프리미티브

HWC는 조합 작업과 디스플레이 하드웨어와의 상호작용을 나타내기 위한 두 프리미티브로 레이어디스플레이를 제공합니다. 또한 HWC는 VSYNC와 VSYNC 이벤트 발생 시 이를 알릴 대상인 SurfaceFlinger에 대한 콜백을 제어합니다.

HIDL 인터페이스

Android 8.0 이상에서는 HWC와 SurfaceFlinger 간 바인더화된 IPC에 컴포저 HAL이라는 HIDL 인터페이스를 사용합니다. 컴포저 HAL은 레거시 hwcomposer2.h 인터페이스를 대체합니다. 공급업체에서 HWC의 컴포저 HAL 구현을 제공하는 경우 컴포저 HAL은 SurfaceFlinger로부터 직접 HIDL 호출을 받습니다. 공급업체에서 HWC의 레거시 구현을 제공하는 경우 컴포저 HAL은 hwcomposer2.h로부터 함수 포인터를 로드하여 HIDL 호출을 함수 포인터 호출로 전달합니다.

HWC는 특정 디스플레이의 속성을 결정하고, 다양한 디스플레이 구성(예: 4K 또는 1080p 해상도)과 색상 모드(예: 기본 색상 또는 트루 sRGB) 간에 전환하고, 지원되는 경우 디스플레이를 켜거나 끄거나 저전력 모드로 전환할 수 있는 함수를 제공합니다.

함수 포인터

공급업체에서 컴포저 HAL을 직접 구현하면 SurfaceFlinger는 HIDL IPC를 통해 함수를 호출합니다. 예를 들어 레이어를 만들려면 SurfaceFlinger는 컴포저 HAL에서 createLayer()를 호출합니다.

공급업체에서 hwcomposer2.h 인터페이스를 구현하는 경우 컴포저 HAL은 함수 포인터로 호출합니다. hwcomposer2.h 주석에서 HWC 인터페이스 함수는 인터페이스에 이름이 지정된 필드로 존재하지 않는 lowerCamelCase 형태의 이름으로 참조됩니다. 거의 모든 함수는 hwc2_device_t에서 제공하는 getFunction을 통해 함수 포인터를 요청하는 방식으로 로드됩니다. 예를 들어 createLayer 함수는 열거형 값인 HWC2_FUNCTION_CREATE_LAYERgetFunction으로 전달될 때 반환되는 HWC2_PFN_CREATE_LAYER 유형의 함수 포인터입니다.

컴포저 HAL 함수와 HWC 함수의 패스 스루 함수에 관한 자세한 내용은 composer를 참조하세요. HWC 함수 포인터에 관한 자세한 내용은 hwcomposer2.h를 참조하세요.

레이어 및 디스플레이 핸들

레이어와 디스플레이는 HWC를 통해 생성된 핸들에 의해 조작됩니다. 핸들은 SurfaceFlinger에 대해 불투명합니다.

SurfaceFlinger를 통해 새 레이어를 만들 때, SurfaceFlinger는 직접 구현하는 경우 Layer 유형을, 패스 스루로 구현하는 경우 hwc2_layer_t 유형을 반환하는 createLayer를 호출합니다. SurfaceFlinger를 통해 해당 레이어의 속성을 수정할 때, SurfaceFlinger는 수정에 필요한 다른 정보와 함께 hwc2_layer_t 값을 적절한 수정 함수에 전달합니다. hwc2_layer_t 유형은 포인터나 색인을 포함할 수 있을 정도로 큽니다.

물리적 디스플레이는 핫플러그를 통해 생성됩니다. 물리적 디스플레이가 핫플러그되면 HWC는 핸들을 만들고 핫플러그 콜백을 통해 핸들을 SurfaceFlinger에 전달합니다. 가상 디스플레이는 SurfaceFlinger를 통해 createVirtualDisplay()를 호출하여 디스플레이를 요청하는 방식으로 생성됩니다. HWC에서 가상 디스플레이 조합을 지원하는 경우 핸들을 반환합니다. 그런 다음 SurfaceFlinger는 디스플레이의 조합을 HWC에 위임합니다. HWC에서 가상 디스플레이 조합을 지원하지 않는 경우 SurfaceFlinger가 핸들을 만들고 디스플레이를 조합합니다.

디스플레이 조합 작업

조합할 새 콘텐츠가 있는 경우 SurfaceFlinger는 VSYNC당 1회씩 절전 모드를 해제합니다. 이 새 콘텐츠는 앱의 새 이미지 버퍼 또는 하나 이상 레이어의 속성 변경사항일 수 있습니다. SurfaceFlinger는 다음과 같은 경우에 절전 모드를 해제합니다.

  1. 처리할 트랜잭션이 있는 경우
  2. 래치할 새 그래픽 버퍼가 있는 경우
  3. 1단계 또는 2단계에서 디스플레이 콘텐츠가 변경되어 새 조합을 처리하는 경우

SurfaceFlinger는 새 조합을 처리하기 위해 필요에 따라 레이어를 생성하고 제거하거나 레이어 상태를 수정합니다. 또한 setLayerBuffer 또는 setLayerColor와 같은 호출을 사용하여 레이어를 현재 콘텐츠로 업데이트합니다. 모든 레이어가 업데이트된 후, SurfaceFlinger는 validateDisplay를 호출하여 레이어의 상태를 검사하고 조합이 진행되는 방식을 결정하도록 HWC에 지시합니다. 기본적으로 SurfaceFlinger는 레이어가 HWC에 의해 조합되도록 모든 레이어를 구성하려고 시도합니다. 하지만 경우에 따라 SurfaceFlinger는 GPU 폴백을 통해 레이어를 조합합니다.

validateDisplay 호출 후, SurfaceFlinger는 getChangedCompositionTypes를 호출하여 HWC에서 조합을 처리하기 전에 변경된 레이어 조합 유형을 요청하는지 확인합니다. SurfaceFlinger는 변경사항을 수락하기 위해 acceptDisplayChanges를 호출합니다.

SurfaceFlinger 조합으로 표시된 레이어가 있는 경우 SurfaceFlinger가 이를 타겟 버퍼로 조합합니다. 그런 다음 SurfaceFlinger가 setClientTarget을 호출하여 버퍼를 디스플레이에 제공함으로써 버퍼는 화면에 표시되거나 SurfaceFlinger 조합으로 표시되지 않은 레이어와 추가로 조합될 수 있습니다. SurfaceFlinger 조합으로 표시된 레이어가 없으면 SurfaceFlinger는 조합 단계를 건너뜁니다.

마지막으로 SurfaceFlinger는 presentDisplay를 호출하여 조합 프로세스를 완료하고 최종 결과를 표시하도록 HWC에 지시합니다.

다중 디스플레이

Android 10은 다중 물리적 디스플레이를 지원합니다. Android 7.0 이상에서 사용하기 위해 HWC 구현을 설계하는 경우 HWC 정의에는 없는 다음과 같은 제한사항이 있습니다.

  • 정확하게 하나의 내부 디스플레이가 있다고 가정합니다. 내부 디스플레이는 부팅 중에 초기 핫플러그가 보고하는 디스플레이입니다. 내부 디스플레이가 핫플러그된 후에는 연결을 해제할 수 없습니다.
  • 내부 디스플레이 외에도 기기가 정상 작동하는 동안 외부 디스플레이는 몇 개든지 핫플러그될 수 있습니다. 프레임워크는 첫 번째 내부 디스플레이 이후의 모든 핫플러그를 외부 디스플레이로 가정하므로 내부 디스플레이가 추가되면 Display.TYPE_BUILT_IN이 아닌 Display.TYPE_HDMI로 잘못 분류됩니다.

위에 설명된 SurfaceFlinger 작업은 디스플레이별로 처리되지만, 한 디스플레이의 콘텐츠만 업데이트되는 경우에도 모든 활성 디스플레이에 대해 순차적으로 처리됩니다.

예를 들어 외부 디스플레이가 업데이트되면 순서는 다음과 같습니다.

    // In Android 9 and lower:

    // Update state for internal display
    // Update state for external display
    validateDisplay(<internal display>)
    validateDisplay(<external display>)
    presentDisplay(<internal display>)
    presentDisplay(<external display>)

    // In Android 10 and higher:

    // Update state for internal display
    // Update state for external display
    validateInternal(<internal display>)
    presentInternal(<internal display>)
    validateExternal(<external display>)
    presentExternal(<external display>)
    

가상 디스플레이 조합

가상 디스플레이 조합은 외부 디스플레이 조합과 유사합니다. 가상 디스플레이 조합과 실제 디스플레이 조합의 차이점은 가상 디스플레이가 화면이 아닌 Gralloc 버퍼에 출력을 전송한다는 점입니다. 하드웨어 컴포저(HWC)는 출력을 버퍼에 기록하고, 완료 펜스를 제공하고, 버퍼를 소비자(예: 동영상 인코더, GPU, CPU)에게 보냅니다. 디스플레이 파이프라인이 메모리에 쓰는 경우 가상 디스플레이는 2D/블리터 또는 오버레이를 사용할 수 있습니다.

모드

SurfaceFlinger가 validateDisplay() HWC 메서드를 호출하고 나면 각 프레임은 다음 세 가지 모드 중 하나에 해당합니다.

  • GLES - GPU가 모든 레이어를 조합하여 출력 버퍼에 직접 씁니다. HWC는 조합에 관여하지 않습니다.
  • MIXED - GPU는 프레임 버퍼로 일부 레이어를 조합하고 HWC는 프레임 버퍼와 남은 레이어를 조합하여 출력 버퍼에 직접 씁니다.
  • HWC - HWC는 모든 레이어를 조합하고 출력 버퍼에 직접 씁니다.

출력 형식

가상 디스플레이 버퍼의 출력 형식은 모드에 따라 다릅니다.

  • GLES 모드 - EGL 드라이버는 출력 버퍼 형식을 dequeueBuffer()로 설정합니다(일반적으로 RGBA_8888). 소비자는 드라이버가 설정한 출력 형식을 수용할 수 있어야 합니다. 그러지 않으면 버퍼를 읽을 수 없습니다.
  • MIXED 및 HWC 모드 - CPU 액세스가 필요한 소비자가 형식을 설정합니다. 그렇지 않으면 형식은 IMPLEMENTATION_DEFINED이며 Gralloc은 사용 플래그에 따라 최상의 형식을 설정합니다. 예를 들어 소비자가 동영상 인코더이고 HWC가 형식을 효율적으로 작성할 수 있는 경우 Gralloc은 YCbCr 형식을 설정합니다.

동기화 펜스

동기화 펜스는 Android 그래픽 시스템에서 중요한 부분입니다. 펜스는 CPU 작업을 동시 GPU 작업과 독립적으로 진행하며, 실제 종속성이 있는 경우에만 차단합니다.

예를 들어 앱이 GPU에서 생성 중인 버퍼를 제출할 때 동기화 펜스 객체도 제출합니다. 이 펜스는 GPU가 버퍼에 쓰기를 완료하면 신호를 보냅니다.

HWC를 사용하려면 버퍼가 표시되기 전에 GPU에서 버퍼 쓰기를 완료해야 합니다. 동기화 펜스는 버퍼와 함께 그래픽 파이프라인을 통과하고 버퍼가 기록될 때 신호를 보냅니다. 버퍼가 표시되기 전에 HWC는 동기화 펜스가 신호를 보냈는지 확인하고, 신호를 보낸 경우에는 버퍼를 표시합니다.

동기화 펜스에 관한 자세한 내용은 하드웨어 컴포저 통합을 참조하세요.