2014-04-30 2 views
9

스크롤하는 동안 일부 프레임이 사라지는 것을 감지하여 응용 프로그램의 성능을 조사하고 있습니다. 나는 systrace (실행중인 Nexus 4에서 4.3)를 실행하여 출력에서 ​​interesting section을 발견했습니다.Android 트리플 버퍼링 - 예상되는 동작입니까?

처음에는 문제가 없습니다. Zooming in on the left section을 사용하면 모든 vsync에서 드로잉이 시작되고 여유 시간을두고 완료되며 다음 vsync까지 기다릴 수 있습니다. 버퍼가 3 배이기 때문에 버퍼가 끝나면 다음 vsync에 게시됩니다.

확대 된 스크린 샷의 네 번째 vsync에서 앱이 작동하고 다음 vsync 시간에 그리기 작업이 완료되지 않습니다. 그러나 이전의 드로잉이 프레임을 앞섰기 때문에 어떤 프레임도 드롭하지 않습니다.

이 문제가 발생한 후에 그리기 작업은 손실 된 vsync를 보완하지 못합니다. 대신, vsync 당 하나의 그리기 작업 만 시작되고 이제는 더 이상 한 프레임을 그리지 않습니다.

Zooming in on the right section이 앱은 더 많은 작업을 수행하고 다른 vsync를 놓칩니다. 앞으로 프레임을 그리지 않았으므로 프레임이 실제로 여기에 드롭됩니다. 그런 다음 한 프레임 앞으로 돌아갑니다.

이 예상되는 동작입니까? 제 생각에 트리플 버퍼링을 사용하면 vsync를 놓친 경우 복구 할 수있었습니다. 그러나이 동작은 두 개의 vsync가 누락 될 때마다 프레임을 삭제하는 것처럼 보입니다. 디스플레이를 소비하는 것보다


후속 질문 this screenshot의 오른쪽에

  1. 는 응용 프로그램은 실제로 빠른 버퍼를 렌더링한다. performTraversals # 1 (스크린 샷으로 표시) 동안 버퍼 A가 표시되고 버퍼 B가 렌더링되고 있다고 가정 해 봅시다. # 1은 vsync보다 오래 전에 완료되고 버퍼 B를 대기열에 넣습니다. 이 시점에서 앱이 즉시 버퍼 C 렌더링을 시작할 수 없습니까? 대신, performTraversals # 2는 다음 vsync 때까지 시작되지 않아 그 사이에 소중한 시간을 낭비합니다.

  2. 비슷한 맥락에서 나는 waitForever on the left side here에 대한 필요성에 대해 다소 혼란스러워합니다. 버퍼 A가 표시되고 버퍼 B가 대기열에 있고 버퍼 C가 렌더링되고 있다고 가정 해 봅시다. 버퍼 C가 렌더링을 완료하면 큐에 즉시 추가되지 않는 이유는 무엇입니까? 대신 버퍼 B가 큐에서 제거 될 때까지 waitForever를 수행합니다. 버퍼 C는 버퍼 C를 추가하기 때문에 응용 프로그램이 버퍼를 얼마나 빠르게 렌더링하는지에 관계없이 큐는 항상 크기 1로 유지됩니다.

답변

8

제공되는 버퍼링 양은 버퍼를 가득 채우는 경우에만 중요합니다. 이는 디스플레이가 소비하는 것보다 빠르게 렌더링한다는 것을 의미합니다.

레이블이 이미지에 나타나지 않지만 초록색 vsync 행 위에있는 자주색 행이 BufferQueue 상태라고 추측합니다. 일반적으로 언제든지 0 또는 1 개의 전체 버퍼가 있음을 알 수 있습니다. 왼쪽의 "왼쪽 확대"이미지에서 두 개의 버퍼가 있음을 볼 수 있지만 그 이후에는 하나의 버퍼 만 있고 화면의 3/4이 매우 짧은 자주색 막대가 보입니다. 단지 겨우 프레임을 제 시간에 렌더링한다는 것을 나타냅니다.

배경에 대해서는 this postthis post을 참조하십시오.추가 질문

업데이트 ...

the other post의 세부 사항 거의 표면에 긁힌. 우리는 더 깊이 나아가 야합니다.

systrace에 표시된 BufferQueue 수는 대기중인 버퍼의 수, 즉 버퍼에있는 내용의 버퍼 수입니다. SurfaceFlinger가 표시 할 버퍼를 잡으면 즉시 버퍼를 해제하고 상태를 "free"로 변경합니다. 디스플레이가 버퍼에서 직접 렌더링되므로 (스크래치 버퍼로 합성하여 표시하는 것과는 대조적으로) 버퍼가 오버레이에 표시 될 때 특히 유용합니다.

다시 말해서, 디스플레이가 화면에 표시하기 위해 활발하게 데이터를 읽는 버퍼는 BufferQueue에서 "사용 가능"으로 표시됩니다. 버퍼에는 처음에 "활성"인 연관된 펜스가 있습니다. 활성화 된 동안 아무도 버퍼 내용을 수정할 수 없습니다. 디스플레이가 더 이상 버퍼를 필요로하지 않을 때, 울타리에 신호를 보냅니다.

추적 왼쪽에있는 코드가 waitForever() 인 이유는 울타리가 신호를 기다리고 있기 때문입니다. VSYNC가 안타를 때 디스플레이가 다른 버퍼로 전환하고 펜스에 신호를 보내면 앱에서 버퍼를 즉시 사용할 수 있습니다. 이것은 SurfaceFlinger가 깨어나서 버퍼가 더 이상 사용되지 않는 것을보아야 할 때 기다릴 필요가있을 때 발생하는 지연을 제거합니다. BufferQueue를 통해 IPC를 보내 버퍼를 해제하는 등의 작업을 수행합니다.

waitForever()은 뒤에서 떨어지지 않을 때만 나타납니다 (트레이스의 왼쪽과 오른쪽). 내가 왜 대기열에 단 하나의 전체 버퍼가있을 때 왜 그런 일이 일어나는지 잘 모르겠다. 이미 알려야하는 가장 오래된 버퍼를 큐에서 빼내야한다.

결론은 트리플 버퍼링을 위해 BufferQueue가 2를 넘지 않는다는 것입니다.

모든 장치가 위에서 설명한대로 작동하지 않습니다. Nexus 7 (2012)은 '명시 적 동기화'메커니즘을 사용하지 않으며 ICS 이전 기기에는 BufferQueues가 전혀 없습니다.

번호가 매겨진 스크린 샷으로 돌아가서 네, 앱에서 performTraversals()를 실행할 수있는 '1'과 '2'사이에 충분한 시간이 있습니다. 앱이 무엇을하고 있는지 알지 못해도 확실하게 말할 수는 없지만 모든 VSYNC를 깨우고 작동하는 Choreographer에 기반한 애니메이션주기가 있다고 생각합니다. 그것보다 더 자주 실행되지 않습니다.

당신이 꾸러미를 이라면 가능한 빨리 렌더링 할 때 ("큐에 넣기") BufferQueue 역압에 의존하여 게임 속도를 조절할 때 어떤 모습인지 알 수 있습니다.

4.4를 실행하는 N4와 4.4를 실행하는 N4를 비교하는 것이 특히 흥미 롭습니다. 4.3에서 트레이스는 당신과 비슷합니다. 큐는 1로 크게 움직이며, 0으로 규칙적으로 떨어지고 가끔씩 2로 상승합니다. 4.4에서 대기열은 거의 항상 2이고 가끔씩 1로 떨어집니다. 두 경우 모두 수면에서 eglSwapBuffers(); 4.3에서 흔적은 보통 waitForever()을 보여 주지만 4.4에서는 dequeueBuffer()을 보여줍니다. (이유는 알지 못합니다.)

업데이트 2 : 4.3과 4.4의 차이점은 Nexus 4 드라이버 변경 인 것 같습니다. 4.3 드라이버는 이전 dequeueBuffer 호출을 사용하여 dequeueBuffer_DEPRECATED() (Surface.cpp line 112)이되었습니다. 이전 인터페이스는 울타리를 "out"매개 변수로 사용하지 않으므로 호출은 waitForever()을 호출해야합니다.더 새로운 인터페이스는 방아쇠를 GL 드라이버에 되돌려줍니다. GL 드라이버는 기다릴 필요가있을 때 기다립니다.

업데이트 3 : 더욱 자세한 설명은 here입니다.

+0

답변 해 주셔서 감사합니다. 링크 된 게시물은 systrace가 버퍼 대기열을 표시한다는 것을 알고 있으므로 매우 유용합니다. 나는 아직도 몇 가지 질문이 있지만 (위에 추가). –

+0

답변 추가. – fadden

+0

일부 업데이트되었습니다. – fadden

관련 문제