원문 : https://community.arm.com/developer/tools-software/graphics/b/blog/posts/mali-performance-3-is-egl_5f00_buffer_5f00_preserved-a-good-thing

주의 : 번역이 개판이므로 이상하면 원문을 참고하세요.

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.


이번 주에 필자는 EGL_BUFFER_PRESERVED 를 통해 애플리케이션 프레임버퍼 관리의 영역으로의 전환( diversion )과 어디에서 이 기법을 사용하면 좋을지 결정하는 방법에 대해서 이야기하는 것을 마무리하고 있습니다. 이것은 사용자 인터페이스( user-interface ) 개발과 관련해 고객과 이야기할 때 정기적으로 제기되는 질문이며, 다른 그래픽스 요소들과 마찬가지로 그것의 효율성은 여러분이 수행중인 작업에 달려 있기 때문에, 이 블로그가 그것들을 명확하게 ( 혹은 적어도 약간 덜 혼란스럽게( murky ) ) 만들어 줄 수 있기를 바랍니다.

What is EGL_BUFFER_PRESERVED?

이전 블로그인 [ Mali Performance 2: How to Correctly Handle Framebuffers ] 에서 설명했듯이, 일반적인 상황에서는 윈도우 서피스의 칸텐츠가 한 프레임에서 다음 프레임으로 보존되지 않습니다. Mali 드라이버는 프레임버퍼( framebuffer )의 칸텐츠가 버려진다( discard )고 가정합니다. 그러므로 컬러( color ), 뎁스( depth ), 스텐실( stencil ) 버퍼의 어떠한 스테이트( state )도 유지될 필요가 없습니다. EGL 명세에서는 기본 EGL_SWAP_BEHAVIOR 를 EGL_BUFFER_DESTROYED 로 지정하고 있습니다.

윈도우 서피스를 EGL 을 통해서 생성할 때, 서피스를 EGL_SWAP_BEHAVIOR 를 EGL_BUFFER_PRESERVED 로 구성해서 생성할 수 있습니다. 이는 프레임버퍼의 컬러 데이터가 프레임 N 의 렌더링 끝에서 프레임 N+1 의 렌더링을 위한 컬퍼 버퍼의 시작 컬러로 사용된다는 것을 의미합니다. 이러한 보존은 컬러 버퍼에만 적용된다는 점을 기억하십시오; 뎁스 버퍼와 스텐실 버퍼는 보존되지 않으며, 그것들의 값은 모든 프레임의 끝에서 소실됩니다.

Great, I can render only what changed!

대부분의 사람들이 하는 일반적인 실수는 이 기법이 현존하는 프레임버퍼에 대한 적은 양의 렌더링만을 패치( patch )할 수 있도록 해 준다고 믿는 것입니다. 만약 이전 프레임 이후로 시계의 1 초만이 화면에서 변경되었다고 하면, 그냥 태스크바( taskbar )에서 시계를 수정하기만 하면 될 것입니다. 맞습니까? 아닙니다!

대부분의 실제 시스템들은 N-버퍼링 렌더링 체계( scheme )를 실행하고 있다는 점을 기억하십시오. 일부는 더블( double ) 버퍼링지만, 트리플( triple ) 버퍼링이 일반적이 되고 있습니다. N+1 프레임을 렌더링할 때의 최상위에 추가하고 있는 메모리 버퍼는 N 프레임의 컬러 버퍼가 아니라 N-2 프레임의 컬러 버퍼일 것입니다. EGL_BUFFER_PRESERVED 는, 단순한 패치 연산을 수행하는 것이 아니라, 드라이버로 하여금, 프레임 N 의 컬러 버퍼를 포함하는, 텍스쳐를 적용한( textured ) 사각형을 프레임 N+1 을 위해 작업중인 타일( tile ) 메모리에 렌더링하게 만드는 것입니다.

이전 블로그 중의 하나와 seanellis 의 블로그의 Forward Pixel Kill( FPK )에서 언급했듯이, Mali GPU 패밀리의 최근 멤버들은 겹쳐 그려지는( overdrawn ) 프래그먼트( fragment )들이 GPU 에 대한 주요한 비용이 되기 전에 제거하기 위한 기능을 지원하고 있습니다. 이전 프레임의 최상위에서 겹쳐 그려지는 것이 불투명( 블렌딩( blending ) 이 없고 프래그먼트 셰이더가 "discard" 를 호출하지 않음 )이라면, 겹쳐 그려지는 부분에 대한 리드백( readback )이 억제될 수 있으며, 결과적으로 성능이나 대역폭에 대한 영향이 없어집니다. 또한, EGL_BUFFER_PRESERVED 가 활성화되어 있지만 모든 것을 겹쳐 그리고 싶다면, 그냥 프레임의 렌더링의 시작 부분에서 glClear() 를 호출해서 리드백을 전혀 하지 않도록 할 수 있습니다. 

Is EGL_BUFFER_PRESERVED worth using?

전체 화면 리드백의 필요성을 받아들이면 멀티 프레임 렌덜이 파이프라인의 개념을 생각하기 시작할 때 상대적으로 직관적이 됩니다. 다음 질문은 "사용자 인터페이스 애플리케이션에 EGL_BUFFER_PRESERVED 를 사용해야 하느냐" 는 것입니다.

많은 가치있는 엔지니어링( engineering ) 질문들처럼, 그 대답은 단순하게 "네" 혹은 "아니요" 가 되기보다는 "그때 그때 달라요" 에 가깝습니다.

올바른 시작 컬러를 사용하는 프레임을 생성하기 위한 EGL_BUFFER_PRESERVED 의 비용은 ( FPK 에 의해서 건너 뛰는( killed ) 경우를 제외하면 ) 이전 프레임 데이터에 대한 전체 화면( full-screen ) 로드( load )입니다. 다른 대안은 클리어( clear ) 컬러를 사용해 프레임을 처음부터 다시 렌더링하는 것입니다. EGL_BUFFER_PRESERVED 를 사용하는 것이 좋을지의 여부는 다음의 두 가지 상대적인 비용에 달려 있습니다:

  • 만약 UI 애플리케이션이 투명도를 많이 사용하는 다중의 비압축 레이어들로 구성되어 있다면, EGL_BUFFER_PRESERVED 가 합리적일 것입니다. 하나의 레이어에서 이전 컬러 데이터를 리드백하는 비용은 다중 레이어 + 블렌딩 루트( route )를 통해 처음부터 컬러를 재생성하는 것보다 훨씬 저렴할 것입니다.
  • 만약 대부분이 단일 레이어이며 압축된 텍스쳐를 읽어들이는 단순한 UI 나 2D 게임을 가지고 있다면, EGL_BUFFER_PRESERVED 는 잘못된 것입니다. 이전 프레임의 컬러를 리드백하기 위한 대역폭 부하가 처음부터 프레임을 생성하는 것보다 훨씬 비쌀 것입니다.

이를 항상 칼로 자르듯이 명확하게 나누는 것은 불가능합니다 -- 이 두 개의 극단 사이에는 중간 지대가 존재합니다 -- 그러므로 분석을 수행할 때 주의를 기울여야 합니다. 만약 의심할 여지가 있다면, 제품 플랫폼에서 실행중인 실제 애플리케이션에서, EGL_BUFFER_PRESERVED 를 껐다 켰다 하면서, GPU 성능 카운터들을 사용해 성능을 리뷰( review )하십시오. 실제 장치에서 실제의 경우를 측정하는 것보다 더 나은 답을 주는 것은 없습니다. 이 시리즈의 다른 블로그에서는 애플리케이션 성능 분석을 하는 가이드를 제공하며 앞으로 몇 달 동안 계속해서 이 영역에 더욱 많은 내용들을 추가하도록 하겠습니다.

그런데, 그런 성능 경험을 수행할 때, 최적의 애플리케이션은 EGL_BUFFER_PRESERVED 를 명시적으로 사용( 혹은 비사용 )하도록 설계되어 있는 것이 중요하다는 점을 기억하십시오; 어떤 경로에서든 가장 효율적인 결과를 획득하려면, 일반적으로는 EGL 구성 스위치( configuration switch )를 단순하게 껐다 켰다 하는 것만큼 간단하지는 않습니다( 역주 : 이 옵션이 성능에 큰 영향을 준다는 의미인듯 ).

Mali-DP500 과 같은 ARM Frambuffer Compression( AFBC )가 활성화된 디스플레이 컨트롤러나 Mali-T760 과 같은 GPU 를 사용하는 시스템에서, EGL_BUFFER_PRESERVED 리드백의 대역폭 부하가 엄청나게 줄어들었다는 것을 언급하는 것은 가치가 있습니다. 리드백 대역폭이 압축된 프레임버퍼의 대역폭이므로 일반적으로 비합축 원본보다는 25-50 % 정도 더 작기 때문입니다.

A Better Future?

EGL_BUFFER_PRESERVED 의 동작은 훌륭한 아이디어입니다. 그리고 많은 경우에 여전히 유용하지만, 그것의 이론적인 이점들의 많은 부분들이 N 버퍼링 시스템에서 사라졌습니다. 왜냐하면 이것은 이전 프레임 데이터에 대한 전체 화면 리드백을 필요로 하기 때문입니다.

우리는, 이용가능한 애플리케이션 및 버퍼 보존 체계가 특정 플랫폼에서 N 버퍼 메모리 모델을 명시적으로 노출( 및 이용 )하면, 애플리케이션 -- 특히 사용자 인터페이스 -- 이 훨씬 더 효율적이 될 수 있다고 생각합니다. 만약 애플리케이션이 그 시스템이 더블 버퍼링을 사용하고 있다는 것을 알고 있고 현재 스테이트와 두 프레임 전의 스테이트 사이의 델타를 알고 있다면, 구조적으로 이상적인 렌더링과 변경된 메모리 영역에 대한 합성( compositing )에 가까워질 수 있습니다. 이것은 잠재적으로 대부분 안정적인 상태( steady-state )의 사용자 인터페이스를 위해 에너지 소비와 메모리 대역폭을 줄여줄 수 있습니다.

EGL_KHR_partial_update

EGL_KHR_partial_update 익스텐션( extension )은 애플리케이션이 N 버퍼링 레벨의 시스템-- 버퍼 에이지( buffer age ) --에 대한 질의를 할 수 있도록 허용하고, 그 정보와 버퍼가 마지막으로 렌더링된 후에 애플리케이션에서 변경된 부분에 대한 지식을 사용해, GPU 에 의해 렌더링되어야 하는 "dirty rectangles" 화면 영역을 식별할 수 있도록 허용하기 위해서 설계되었습니다.

이 익스텐션에서 버퍼 에이징 기능은 EGL_EXT_buffer_age 익스텐션에 의해 제공되는 것과 매우 유사합니다만, 타일 기반 렌더링의 경우에는 어떤 타일을 완전히 떨어뜨릴( drop ) 수 있는지 미리 알려 주는 dirty rectangle 을 제공합니다. 왜냐하면 그것들은 수정되지 않는 것이 보장되기 때문입니다. 만약 이 두 개의 익스텐션 중 하나를 선택해야 한다면, 최적의 성능을 위해서는 EGL_KHR_partial_update 기능을 사용하세요; 이러한 이유로 Mali 드라이버는 EGL_EXT_buffer_age 를 노출하지 않습니다.

EGL_KHR_swap_buffers_with_damage

EGL_KHR_swap_buffers_with_damage 익스텐션은 애플리케이션을 위해 시스템 합성 처리에 dirty rectangle 힌트들을 제공하기 위한 수단을 제공합니다. 이는 N 버퍼링 합성기( compositor )들과 디스플레이 컨트롤러들이 EGL_KHR_partial_update 으로부터 클라이언트 렌더링이 획득하는 최적화로부터 이득을 취할 수 있게 해 줍니다.

Do I need to use both extensions to get full benefit?

네, EGL_KHR_partial_update 는 GPU 를 버퍼 생성자( producer )로서 사용하여 애플리케이션이 렌더링하는 내용을 최적화합니다; EGL_KHR_swap_buffers_with_damage 는 버퍼 소비자로서 사용되는 디스플레이에 유효한 출력 이미지를 보낼 수 있도록 하기 위해 시스템 합성기가 리프레시( refresh )해야 하는 내용을 최적화합니다. 각각의 경우에 애플리케이션이 지정해야 하는 damage rectangle 은 일반적으로 다릅니다. 그러므로 두 개의 익스텐션이 모두 필요합니다.

Tune In Next Time

프레임버퍼 관리에 대한 짧은 전환이 마무리되었습니다. 그래서 다음 시간에는 Mali 에서 ARM DS-5 Streamline 을 사용해 애플리케이션 성능 병목과 최적화 기회에 대해서 살펴 보도록 하겠습니다.

TTFN,

Pete

Further reading:


+ Recent posts