동기화 프레임워크

동기화 프레임워크는 Android 그래픽 시스템의 여러 비동기 작업 간의 종속 항목을 명시적으로 설명합니다. 프레임워크는 구성요소가 버퍼 해제 시기를 나타낼 수 있게 해주는 API를 제공합니다. 또한 프레임워크는 드라이버 간(커널에서 사용자 공간으로)에, 그리고 사용자 프로세스 자체 간에 동기화 프리미티브를 전달할 수 있게 해줍니다.

예를 들어 애플리케이션은 GPU에서 수행할 작업을 대기열에 등록할 수 있습니다. GPU가 해당 이미지를 그리기 시작합니다. 이미지는 아직 메모리에 그려지지 않았지만 버퍼 포인터, 그리고 GPU 작업이 완료되는 시기를 나타내는 펜스가 창 컴포지터에 전달됩니다. 창 컴포지터가 미리 처리를 시작하고 작업을 디스플레이 컨트롤러에 전달합니다. 마찬가지로 CPU 작업도 미리 수행됩니다. GPU과 완료되면 디스플레이 컨트롤러에 즉시 이미지가 표시됩니다.

동기화 프레임워크 역시 구현자가 자체 하드웨어 구성요소의 동기화 리소스를 활용할 수 있게 해줍니다. 마지막으로 프레임워크는 그래픽 파이프라인에 관한 가시성을 제공하여 디버깅을 돕습니다.

명시적 동기화

명시적 동기화는 버퍼 사용을 마친 그래픽 버퍼의 생산자 및 소비자가 신호를 보낼 수 있게 해줍니다. 명시적 동기화는 커널 공간에 구현됩니다.

명시적 동기화의 이점은 다음과 같습니다.

  • 기기 간의 동작 편차 감소
  • 디버깅 지원 개선
  • 테스트 측정항목 개선

동기화 프레임워크에는 세 가지 객체 유형이 있습니다.

  • sync_timeline
  • sync_pt
  • sync_fence

sync_timeline

sync_timeline은 일정하게 증가하는 타임라인입니다. 공급업체는 GL 컨텍스트, 디스플레이 컨트롤러 또는 2D 블리터 등의 각 드라이버 인스턴스에 이 타임라인을 구현해야 합니다. sync_timeline은 특정 하드웨어와 관련하여 커널에 제출된 작업 수를 계산합니다. sync_timeline은 작업 순서에 관한 보증을 제공하고 하드웨어 관련 구현을 지원합니다.

sync_timeline을 구현할 때는 다음 가이드라인을 따르세요.

  • 모든 드라이버, 타임라인 및 펜스에 유용한 이름을 제공하여 디버깅을 간소화합니다.
  • 디버깅 출력을 쉽게 읽을 수 있도록 타임라인에 timeline_value_strpt_value_str 연산자를 구현합니다.
  • 원하는 경우 채우기 driver_data를 구현하여 GL 라이브러리와 같은 사용자 공간 라이브러리에 비공개 타임라인 데이터에 관한 액세스 권한을 부여합니다. data_driver는 공급업체가 변경할 수 없는 sync_fencesync_pts에 관한 정보를 전달하여 이를 토대로 명령줄을 빌드할 수 있게 해줍니다.
  • 사용자 공간에서 명시적으로 펜스를 생성하거나 알리도록 허용하지 않습니다. 명시적으로 신호/펜스를 생성하면 파이프라인 기능을 중지하는 서비스 거부 공격으로 이어질 수 있습니다.
  • sync_timeline, sync_pt 또는 sync_fence 요소에 명시적으로 액세스하지 않습니다. API는 모든 필수 함수를 제공합니다.

sync_pt

sync_ptsync_timeline의 단일 값 또는 포인트입니다. 포인트는 활성, 신호 받음 및 오류, 이렇게 세 가지 상태를 지닙니다. 포인트는 활성 상태에서 시작되어 신호 받음 또는 오류 상태로 전환됩니다. 예를 들어 이미지 소비자에게 버퍼가 더 이상 필요 없는 경우 버퍼에 다시 작성해도 괜찮다는 내용을 이미지 생산자가 인지할 수 있도록 sync_pt가 신호를 받습니다.

sync_fence

sync_fence는 다른 sync_timeline 상위 요소를 가지는 경우가 많은 sync_pt 값 모음입니다(예: 디스플레이 컨트롤러 및 GPU의 경우). sync_fence, sync_ptsync_timeline은 드라이버 및 사용자 공간에서 종속 항목을 통신하는 데 사용하는 기본 프리미티브입니다. 펜스가 신호를 받으면 펜스가 완료된다는 보장이 있기 전에 모든 명령어가 실행됩니다. 이는 커널 드라이버 또는 하드웨어 블록에서 명령어를 순서대로 실행하기 때문입니다.

동기화 프레임워크는 여러 소비자나 생산자가 버퍼 사용을 마쳤을 때 이를 신호로 보내어 다른 함수 매개변수와 종속 항목 정보를 통신할 수 있게 해줍니다. 펜스는 파일 설명자에 의해 지원되며 커널 공간에서 사용자 공간으로 전달됩니다. 예를 들어 펜스에는 별개의 이미지 소비자 2개가 버퍼 판독을 마쳤을 때 이를 표시하는 2개의 sync_pt 값이 포함됩니다. 펜스가 신호를 받으면 이미지 생산자는 두 소비자 모두 소비를 마쳤음을 인지합니다.

펜스는 sync_pt 값처럼 포인트 상태에 따라 활성에서 시작하여 상태를 변경합니다. 모든 sync_pt 값이 신호를 받으면 sync_fence도 신호를 받습니다. 한 개의 sync_pt가 오류 상태로 전환되면 전체 sync_fence가 오류 상태를 취합니다.

sync_fence의 멤버십은 펜스를 생성한 후에도 변경할 수 없습니다. 펜스에서 2개 이상의 포인트를 가져오려면 2개의 고유한 펜스가 세 번째 펜스에 추가된 포인트에서 병합이 이루어집니다. 이러한 포인트 중 하나가 원래 펜스에서 신호를 받았고 나머지 하나는 신호를 받지 않은 경우 세 번째 펜스도 신호 받음 상태를 취하지 않습니다.

명시적인 동기화를 구현하려면 다음을 따르세요.

  • 특정 하드웨어 드라이버의 동기화 프레임워크를 구현하는 커널-공간 하위 시스템입니다. 펜스를 인지해야 하는 드라이버는 하드웨어 컴포저에 액세스하거나 통신하는 모든 요소인 경우가 일반적입니다. 키 파일에는 다음이 포함됩니다.
    • 핵심 구현:
      • kernel/common/include/linux/sync.h
      • kernel/common/drivers/base/sync.c
    • kernel/common/Documentation/sync.txt의 문서
    • platform/system/core/libsync의 커널 공간과 통신하기 위한 라이브러리
  • 공급업체는 적절한 동기화 펜스를 HAL의 validateDisplay()presentDisplay() 함수에 관한 매개변수로 제공해야 합니다.
  • 2개의 펜스 관련 GL 확장 프로그램(EGL_ANDROID_native_fence_syncEGL_ANDROID_wait_sync)과 그래픽 드라이버의 펜스 지원

우수사례: 디스플레이 드라이버 구현

동기화 함수를 지원하는 API를 사용하려면 디스플레이 버퍼 함수를 지닌 디스플레이 드라이버를 개발해야 합니다. 동기화 프레임이 존재하기 전에는 이 함수가 dma-buf 객체를 수신하고 디스플레이에 이러한 버퍼를 배치하고 버퍼가 표시되는 동안 차단합니다. 예:

/*
 * assumes buffer is ready to be displayed.  returns when buffer is no longer on
 * screen.
 */
void display_buffer(struct dma_buf *buffer);

동기화 프레임의 경우에는 display_buffer 함수가 좀 더 복잡합니다. 버퍼를 디스플레이에 배치하는 동안에는 버퍼 준비 시기를 나타내는 펜스에 버퍼가 연결됩니다. 펜스가 사라지면 작업을 대기열에 등록하여 시작할 수 있습니다.

펜스가 사라진 후에 작업을 대기열에 등록하고 시작해도 차단이 발생하지는 않습니다. 즉시 자체 펜스로 돌아가면 버퍼가 디스플레이에서 제거되는 시기가 보장됩니다. 버퍼를 대기열에 등록하는 동안 커널은 동기화 프레임워크에 관한 종속 항목을 나열합니다.

/*
 * displays buffer when fence is signaled.  returns immediately with a fence
 * that signals when buffer is no longer displayed.
 */
struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence
*fence);

동기화 통합

이 섹션에서는 커널 공간 동기화 프레임워크를 Android 프레임워크의 사용자 공간 부분과 통합하는 방법, 그리고 서로 간에 통신해야 하는 드라이버에 관해 설명합니다. 커널 공간 객체는 사용자 공간에서 파일 설명자로 표현됩니다.

통합 규칙

Android HAL 인터페이스 규칙 준수:

  • API가 sync_pt를 참조하는 파일 설명자를 제공하는 경우 API를 사용하는 공급업체 드라이버 또는 HAL이 파일 설명자를 닫아야 합니다.
  • 공급업체 드라이버 또는 HAL이 API 함수에 관한 sync_pt를 포함하는 파일 설명자를 전달하는 경우에는 공급업체 드라이버나 HAL이 파일 설명자를 닫으면 안 됩니다.
  • 계속해서 펜스 파일 설명자를 사용하려면 공급업체 드라이버 또는 HAL이 설명자를 복제해야 합니다.

펜스 객체는 BufferQueue를 통해 전달될 때마다 이름이 변경됩니다. 커널 펜스 지원은 펜스가 이름으로 문자열을 취하도록 허용합니다. 따라서 동기화 프레임워크가 창 이름, 그리고 펜스 이름을 지정하기 위해 대기열에 등록 중인 버퍼 색인(예: SurfaceView:0)을 사용합니다. 이는 디버깅에서 /d/sync의 출력과 버그 신고에서 이름이 표시되는 동안 교착 상태의 출처를 식별하는 데 유용합니다.

ANativeWindow 통합

ANativeWindow는 펜스를 인지합니다. dequeueBuffer, queueBuffercancelBuffer에는 펜스 매개변수가 있습니다.

OpenGL ES 통합

OpenGL ES 동기화 통합은 2개의 EGL 확장 프로그램에 의지합니다.

  • EGL_ANDROID_native_fence_syncEGLSyncKHR 객체에서 네이티브 Android 펜스 파일 설명자를 래핑하거나 생성할 수 있는 방법을 제공합니다.
  • EGL_ANDROID_wait_sync는 CPU 측이 아닌 GPU 측에서 GPU가 EGLSyncKHR을 기다리도록 할 수 있습니다. EGL_ANDROID_wait_sync 확장 프로그램은 EGL_KHR_wait_sync 확장 프로그램과 동일합니다.

이러한 확장 프로그램을 독립적으로 사용하려면 EGL_ANDROID_native_fence_sync 확장 프로그램과 함께 관련 커널 지원까지 함께 구현합니다. 그런 다음 드라이버에 EGL_ANDROID_wait_sync 확장 프로그램을 사용 설정합니다. EGL_ANDROID_native_fence_sync 확장 프로그램은 고유한 네이티브 펜스 EGLSyncKHR 객체 유형으로 구성됩니다. 결과적으로는 기존 EGLSyncKHR 객체 유형에 적용되는 확장 프로그램이 EGL_ANDROID_native_fence에 적용되지 않을 수도 있으며, 이 경우 원치 않는 상호작용을 피할 수 있습니다.

EGL_ANDROID_native_fence_sync 확장 프로그램은 해당하는 네이티브 펜스 파일 설명자 속성을 사용합니다. 이 속성은 생성 시에만 설정할 수 있고 기존 동기화 객체에서 계속해서 직접 쿼리할 수 없습니다. 이 속성은 두 모드 중 하나로 설정할 수 있습니다.

  • 유효한 펜스 파일 설명자는 기존 네이티브 Android 펜스 파일 설명자를 EGLSyncKHR 객체에 래핑합니다.
  • -1EGLSyncKHR 객체에서 네이티브 펜스 파일 설명자를 생성합니다.

DupNativeFenceFD() 함수 호출을 사용하여 EGLSyncKHR 객체를 네이티브 Android 펜스 파일 설명자에서 추출합니다. 이는 설정된 속성을 쿼리하는 경우와 결과가 같지만 수신자가 펜스를 닫고 연산을 복제해야 한다는 규칙을 준수합니다. 마지막으로 EGLSyncKHR 객체를 삭제하면 객체가 내부 펜스 속성을 닫습니다.

하드웨어 컴포저 통합

하드웨어 컴포저는 세 가지 유형의 동기화 펜스를 처리합니다.

  • Acquire 펜스는 입력 버퍼와 함께 setLayerBuffersetClientTarget 호출에 전달됩니다. 이는 버퍼에 관한 작성 대기를 나타내며 SurfaceFlinger 또는 HWC가 연결된 버퍼에서 판독을 시도하여 합성을 실행하기 전에 신호를 보내야 합니다.
  • Release 펜스presentDisplay에 관한 호출 후에 getReleaseFences 호출을 사용하여 검색됩니다. 이는 같은 레이어에 관한 이전 버퍼의 판독 대기를 나타냅니다. Release 펜스는 HWC가 더 이상 이전 버퍼를 사용하지 않을 때 신호를 보냅니다. 이는 현재 버퍼가 디스플레이의 이전 버퍼를 대체했기 때문입니다. Release 펜스는 현재 합성 도중에 대체되는 이전 버퍼와 함께 앱으로 다시 전달됩니다. 앱은 Release 펜스가 신호를 보낼 때까지 기다렸다가 앱으로 돌아온 버퍼에 새 콘텐츠를 작성해야 합니다.
  • Present 펜스presentDisplay에 관한 호출의 일부로 프레임당 하나씩 반환됩니다. Present 펜스는 이 프레임의 합성이 완료되거나 이전 프레임의 합성 결과가 더 이상 필요하지 않은 경우를 나타냅니다. 실제 디스플레이의 경우 현재 프레임이 화면에 표시되면 presentDisplay가 Present 펜스를 반환합니다. Present 펜스가 반환된 후에는 SurfaceFlinger 대상 버퍼에 다시 안전하게 작성할 수 있습니다(해당하는 경우). 가상 디스플레이의 경우 출력 버퍼에서 안전하게 읽을 수 있게 되면 Present 펜스가 반환됩니다.