2016-06-02 2 views
7

라이브 카메라 데이터 (Y 평면에서)에 대해 CPU 측 읽기 전용 프로세스를 수행 한 다음 GPU에서 렌더링해야합니다. 처리가 완료 될 때까지 프레임이 렌더링되어서는 안됩니다. (그래서 항상 카메라에서 최신 프레임을 렌더링하고 싶지는 않습니다. CPU 쪽에서 처리가 끝난 최신 프레임 만 렌더링합니다.) 렌더링은 카메라 처리와 분리되어 카메라 프레임이 그보다 낮은 속도로 도착하더라도 60FPS를 목표로합니다.안드로이드에서 제로 복사 카메라 처리 및 렌더링 파이프 라인

관련 그러나 높은 수준의 질문에 이상있다 : Lowest overhead camera to CPU to GPU approach on android

좀 더 구체적으로 현재 설정을 설명하기 위해 우리가 버퍼 중 하나 "무료"입니다 카메라 데이터에 대한 응용 프로그램 측 버퍼 풀을 가지고, "in display"또는 "pending display"를 선택하십시오. 카메라의 새로운 프레임이 도착하면 사용 가능한 버퍼를 확보하고 거기에 실제 데이터가있는 경우 프레임 참조 (또는 시스템 참조 버퍼 풀에있는 참조)를 저장하고 처리 한 다음 결과를 버퍼에 저장합니다. 버퍼를 "pending display"로 설정하십시오. 렌더러 스레드에서 렌더링 루프의 시작 부분에 버퍼링 "표시 대기 중"이 있으면 대신 "디스플레이 중"으로 래칭하여 카메라를 렌더링하고 동일한 루프에서 계산 된 처리 된 정보를 사용하여 다른 내용을 렌더링합니다 카메라 프레임입니다.

위의 질문에 대한 @ fadden의 답변 덕분에 이제 Android camera2 API의 "병렬 출력"기능이 다양한 출력 대기열 사이의 버퍼를 공유하므로 데이터 사본을 포함하지 않아야합니다. 현대 안드로이드.

의견에는 SurfaceTexture 및 ImageReader 출력을 동시에 래치 할 수 있으며 처리가 완료 될 때까지 "버퍼에 앉는다"는 제안이있었습니다. 불행히도 필자는 여전히 60 FPS로 구동하고 싶은 분리 된 렌더링으로 인해 내 경우에는 적용 할 수 없다고 생각하며, 이전 프레임에 대한 액세스가 필요하지만 새로운 프레임이 처리되지 않는 것을 보장하기 위해 계속 처리해야합니다. 싱크 아웃

마음에 떠오르는 한 가지 해결책은 여러 개의 SurfaceTexture (각 3 개의 앱 사이드 버퍼에 하나씩)를 사용하는 것입니다. 우리가 새로운 카메라 프레임을 얻었을 때, 우리는 앱 측 풀에서 무료 버퍼를 얻을 수있었습니다. 그런 다음 ImageReader에 acquireLatestImage()을 호출하여 처리 할 데이터를 얻은 다음 사용 가능한 버퍼의 SurfaceTexture에 updateTexImage()을 호출합니다. 렌더링시 우리는 "디스플레이에있는"버퍼의 SufaceTexture가 GL에 바인드 된 버퍼인지 확인해야합니다. 대부분의 경우 동기화가 이루어져야합니다 (@fadden이 updateTexImage()acquireLatestImage()을 호출하는 경합이 있다고 설명했기 때문에). 하지만 그 시간 창은 희소성을 줄 수있을만큼 작아야하고 어쨌든 버퍼의 타임 스탬프를 사용하여 수정 및 수정 가능할 수 있습니다.

SurfaceTexture가 GL 컨텍스트에 바인딩 된 경우에만 updateTexImage()이 호출 될 수 있다는 점에 유의하십시오. 카메라 처리 스레드에서도 GL 컨텍스트가 필요하므로 SurfaceTexture에서 updateTexImage()을 수행 할 수 있습니다. 렌더링 스레드가 여전히 "디스플레이중인"버퍼의 SurfaceTexture에서 렌더링 할 수있는 동안 "자유"버퍼에 저장됩니다.

그래서, 질문에 :

  1. 이 합리적인 접근 방식처럼 보이는가?
  2. SurfaceTexture는 기본적으로 공유 버퍼 풀을 둘러싼 가벼운 래퍼입니까? 아니면 제한된 하드웨어 리소스를 소비합니까?
  3. SurfaceTexture 호출이 충분히 저렴하여 여러 것을 사용하면 데이터를 복사하는 것보다 여전히 큰 이점이 있습니까?
  4. 각기 다른 SurfaceTexture를 사용하여 구별되는 GL 문맥이있는 두 개의 스레드가 작동 할 계획입니까 아니면 고통과 버그가 많은 드라이버가 필요한지 묻고 싶습니까?

나는 그것이 가야할만큼 유망한 것으로 들린다. 아무리 나쁜 생각이라도 간과 해 버린 내부 세부 사항을 알고있는 사람 (기본적으로 @fadden!)이 있으면 여기에 질문 할 가치가 있다고 생각했습니다.

답변

7

흥미로운 질문입니다. 독립적 인 상황과 여러 개의 스레드를 갖는

배경 물건

매우 일반적입니다. 하드웨어 가속 View 렌더링을 사용하는 모든 앱은 메인 스레드에서 GLES 컨텍스트를 가지므로 GLSurfaceView를 사용하는 앱 (SurfaceView 또는 TextureView 및 독립 렌더링 스레드가있는 자체 EGL 롤)은 여러 컨텍스트를 활발히 사용합니다.

모든 TextureView는 내부에 SurfaceTexture를 가지고 있으므로, 여러 TextureView를 사용하는 모든 응용 프로그램은 단일 스레드에서 여러 SurfaceTexture를 갖습니다. (실제 구현에서는 had a bug 프레임 워크가 여러 TextureViews에서 문제를 일으켰지 만 드라이버 문제가 아닌 상위 레벨 문제였습니다.)

SurfaceTexture,/k/a GLConsumer는 전체적으로 많은 작업을 수행하지 않습니다. 처리. 프레임이 소스 (귀하의 경우에는 카메라)에서 도착하면 일부 EGL 함수를 사용하여 버퍼를 "외부"텍스처로 "래핑"합니다. EGL 컨텍스트가 작동하지 않으면이 EGL 조작을 수행 할 수 없으며, 이는 왜 SurfaceTexture를 하나에 연결해야하는지, 잘못된 컨텍스트가 현재 인 경우 새 프레임을 텍스처에 넣을 수없는 이유입니다. the implementation of updateTexImage()에서 볼 때 버퍼 대기열과 텍스처 및 울타리가있는 매우 복잡한 작업을하고 있지만 픽셀 데이터를 복사 할 필요는 없습니다. 실제로 묶는 유일한 시스템 리소스는 RAM입니다. 고해상도 이미지를 캡처하는 경우에는 그다지 중요하지 않습니다.

연결

EGL 컨텍스트 스레드 사이에서 이동 될 수 있지만, 한 번에 하나 개의 스레드에서 "현재"가 될 수 있습니다. 여러 스레드에서 동시에 액세스하려면 많은 바람직하지 않은 동기화가 필요합니다. 주어진 스레드는 하나의 "현재"컨텍스트를 가진다. OpenGL API는 글로벌 상태의 단일 스레드에서 멀티 스레드로 발전했으며, 단지 상태를 스레드 로컬 저장소로 다시 작성하지 않고 "현재"라는 개념으로 발전 시켰습니다.

텍스쳐를 포함하여 텍스쳐를 포함하여 어떤 것들을 공유하는 EGL 컨텍스트를 만들 수는 있지만 이러한 컨텍스트가 다른 스레드에있는 경우 텍스쳐가 업데이트 될 때 매우주의해야합니다. Grafika는 getting it wrong의 좋은 예를 제공합니다.

SurfaceTexture는 제작자 - 소비자 구조를 갖는 BufferQueues 위에 구축됩니다. SurfaceTextures의 재미있는 점은 양면을 포함하기 때문에 소비자가 멀리있는 SurfaceView와는 달리 한쪽에서 데이터를 공급하고 다른 한쪽으로 끌어낼 수 있다는 것입니다. 모든 Surface와 마찬가지로 Binder IPC 위에 구축되므로 하나의 스레드에서 Surface를 공급할 수 있으며 다른 스레드 (또는 프로세스)에서는 안전하게 updateTexImage()을 전달할 수 있습니다. API는 소비자 측 (프로세스)에서 SurfaceTexture를 만든 다음 제작자 (예 : mediaserver 프로세스에서 주로 실행되는 카메라)에 대한 참조를 전달하도록 구성됩니다.

당신이 지속적으로 연결하고 BufferQueues을 분리하는 경우 당신은 오버 헤드의 무리를 유도합니다

구현. 따라서 3 개의 SurfaceTexture가 버퍼를 수신하기를 원한다면, 3 개의 모든 SurfaceTexture를 Camera2의 출력에 연결하고 모두가 "버퍼 방송"을 수신하도록해야합니다. 그런 다음 updateTexImage()을 라운드 로빈 방식으로 사용합니다. SurfaceTexture의 BufferQueue는 "비동기"모드로 실행되기 때문에 대기열을 "소모"할 필요없이 항상 각 호출마다 최신 프레임을 가져야합니다.

Lollipop-era BufferQueue 다중 출력이 변경되고 Camera2가 도입되기 전에는 이러한 배치가 불가능했습니다. 이전에이 접근법을 사용해 본 사람이 있는지는 알 수 없습니다.

모든 SurfaceTexture는 동일한 UI E 컨텍스트에,보기 UI 스레드 이외의 스레드에 이상적으로 연결되므로 현재의 내용을 다룰 필요가 없습니다.

새로운 OpenGL을 ES 텍스처 객체가 생성되고 채워집니다 : 다른 스레드에서 두 번째 상황에서 텍스처에 액세스하려면, 당신은 명시 적으로이 방법을 지원하는의 SurfaceTexture attach/detach API 호출을 사용해야합니다 detachFromGLContext()를 마지막으로 호출 할 당시의 SurfaceTexture 이미지 프레임.

EGL 컨텍스트를 전환하는 것은 소비자 측 작업이며 생산자 측 작업 인 카메라 연결에 영향을주지 않습니다. 컨텍스트 사이에서 SurfaceTexture를 이동하는 데 소요되는 오버 헤드는 updateTexImage()보다 작아야하지만 스레드간에 통신 할 때 동기화를 보장하기 위해 일반적인 단계를 수행해야합니다.

이미지 판독기가 getTimestamp() 호출이 부족하여 너무 좋지 않습니다. 그러면 카메라에서 버퍼를 일치시키는 작업이 크게 단순 해집니다. 출력 버퍼를 사용하여 여러 SurfaceTextures

결론

가능하지만 까다 롭다. 나는 한 ST가 스레드/컨텍스트 A에서 프레임을 수신하는 데 사용되는 반면 다른 ST는 스레드/컨텍스트 B에서 렌더링하는 데 사용되지만 실제 운영 중이므로 탁구 버퍼 접근법에 잠재적 인 이점을 볼 수 있습니다 시간 나는 당신이 타이밍을 채우려하지 않는 한 부가적인 버퍼링에 가치가 있다고 생각하지 않는다.

항상 그렇듯이 Android System-Level Graphics Architecture doc을 권장합니다.

+0

큰 설명에 감사 드리며 실행 가능한 접근 방식처럼 들립니다. 주말에 내가 조롱 할거야. 아마도 이것을 github에 넣을 것입니까? – tangobravo

+0

"동기화를위한 일반적인 단계"에 따르면 렌더 스레드가 렌더링을 위해 첨부하기 전에'updateTexImage()'를 호출하는 스레드가 반드시'detachFromGLContext()'를 호출 했어야합니다. 연결된 grafika 버그에서 언급 한 것처럼 텍스처가 공유되도록 설정된 경우에 필요한 'glFinish()'요구 사항은 없습니까? – tangobravo

+0

API 19의 이미지에는 ['getTimestamp()'] (https://developer.android.com/reference/android/media/Image.html#getTimestamp())가 있습니다. 타임 스탬프를 SurfaceTexture로 사용하지만 접근 방식을 테스트 할 때이를 확인할 것입니다. – tangobravo