화면 하나에도 여러 주체가 참여합니다
앱이 렌더링할 공간을 얻고, GPU 등이 픽셀을 만들고, SurfaceFlinger와 Hardware Composer가 표시할 구성을 준비합니다. 이 과정에서 “버퍼를 보냈다”는 말은 주소, 소유권, 작업 완료 중 무엇을 뜻하는지 분명해야 합니다.
| 이름 | 주요 역할 | 같은 뜻이 아닌 것 |
|---|---|---|
| BufferQueue | producer와 consumer 사이에서 버퍼 사용 순서를 관리합니다. | 픽셀을 처리하는 GPU 그 자체 |
| Gralloc | 크기·format·usage에 맞는 graphics buffer의 할당·매핑을 지원합니다. | 모든 버퍼가 단순한 선형 RGBA 배열이라는 보장 |
| SurfaceFlinger | 여러 layer를 받아 합성을 준비합니다. | 앱의 모든 그리기 함수를 대신 실행하는 주체 |
| Hardware Composer | 표시 장치의 합성 기능을 framework와 연결합니다. | 항상 GPU 합성을 전부 없애 주는 장치 |
WindowManager의 창 배치·정책과 SurfaceFlinger의 layer 합성을 구분해야 합니다. 실제 버퍼가 거치는 경로와 HAL interface 세대는 Android branch와 기기의 구성에 따라 확인합니다. 오래된 framebuffer 직접 접근 예를 모든 현대 기기의 경로로 옮기지 않습니다. SurfaceFlinger와 WindowManager
한 버퍼의 사용 권한이 돌아오는 과정
1. 버퍼를 얻습니다
producer가 dequeueBuffer로 사용할 버퍼를 받습니다. 앞선 소비 작업이 남아 있다면 함께 받은 fence가 완료될 때까지 새 쓰기를 시작할 수 없습니다.
2. 새 내용을 제출합니다
producer가 렌더링 작업을 예약하고 queueBuffer로 버퍼와 완료를 나타내는 fence를 넘깁니다. CPU 함수가 반환해도 GPU 쓰기는 진행 중일 수 있습니다.
3. 소비자가 사용합니다
consumer가 acquireBuffer로 버퍼를 얻습니다. 읽기를 시작하기 전에 생산 측의 쓰기 완료 조건을 만족해야 합니다. 이 대기를 GPU 등 하드웨어 작업의 의존 관계로 넘길 수도 있습니다.
4. 다음 사용을 준비합니다
consumer가 releaseBuffer로 돌려보냅니다. 앞선 읽기가 아직 끝나지 않았다면 release fence가 다음 쓰기의 시작을 제한합니다.
단계 이동은 버퍼의 사용 권한과 작업 의존 관계를 보여 줍니다. 화면 픽셀이 매번 CPU 복사로 이동하거나 네 함수가 GPU 완료까지 모두 기다린다는 뜻은 아닙니다.
BufferQueue는 버퍼 내용을 통째로 복사해서 전달하는 대신 handle로 연결합니다. 그러나 이것만으로 전체 영상 경로에 format 변환이나 합성용 복사가 전혀 없다고 결론 내릴 수는 없습니다. BufferQueue와 Gralloc Android graphics 동기화
buffer와 fence는 서로 다른 질문에 답합니다
| 객체·상태 | 답하는 질문 |
|---|---|
| buffer handle / dma-buf FD | 어느 공유 버퍼를 사용할 수 있습니까? |
| acquire fence | 이 버퍼를 읽기 전에 기다려야 할 앞선 쓰기가 끝났습니까? |
| release fence | 앞선 소비자가 사용을 끝내 새 내용을 써도 됩니까? |
| queue에 들어간 상태 | 소비자에게 제출됐습니까? 작업 완료나 실제 표시와 동일하지 않습니다. |
FD는 해당 프로세스의 파일 descriptor입니다. CPU 물리 주소를 그대로 담은 정수로 취급하면 안 됩니다. dma-buf는 여러 driver가 버퍼를 공유하는 틀이고, dma-fence는 비동기 작업 완료를 나타내는 틀입니다. 각각의 장치는 자기 DMA mapping 조건을 만족해야 합니다. dma-buf·dma-fence·dma-resv
공유 버퍼를 CPU에서 mmap으로 읽는 경로라면 cache 접근 규칙도 필요합니다. DMA_BUF_IOCTL_SYNC의 START·END는 CPU 접근의 coherency 처리를 위한 것이며, 다른 주체와 동시에 써도 된다는 mutex를 제공하지 않습니다. fence 의존 관계와 사용 권한을 함께 지켜야 합니다.
화면이 깨지거나 오래된 프레임이 보일 때
- 크기·stride·pixel format과 실제 allocation의 layout을 먼저 맞춥니다.
- producer의 제출 시점과 쓰기 완료 fence의 signal 시점을 나누어 봅니다.
- consumer가 버퍼를 놓는 시점과 다음 쓰기 시작 사이의 의존 관계를 확인합니다.
- BufferQueue의 대기, GPU 실행, 합성, 표시 시점을 한 시간축에 연결합니다.
queueBuffer 직후의 로그만으로 “화면에 표시됐습니다”라고 적으면 표시 지연을 찾기 어렵습니다. 반대로 fence가 늦게 완료된다는 관찰만으로 GPU 연산이 느리다고 단정할 수도 없습니다. 그 작업이 기다리는 앞선 의존 관계를 따라가야 합니다.