'전체 글'에 해당되는 글 76건

Android Drawing Process 1

Android 2019. 4. 15. 11:17

(App surface, SF Layer)출처: https://lastyouth.tistory.com/24 [자유로운날개]

 

학기가 끝나서, 지난학기간 한 것들을 포스팅하려고 한다. 언제 다 할지는 미지수지만 그래도 틈틈히 할 예정이다.

 

Smartphone display에 대해 공부를 하다보면 필연적으로 해당 Smartphone 상 동작하는 운영체제에서 어떻게 화면을 그려내는지를 알아야 할 시점이 오게 된다.

 

사실 이전에 수행한 프로젝트 덕분에 Android 운영체제에서 SurfaceFlinger 서비스가 하는 일은 개략적으로 알고 있었으나, SurfaceFlinger는 어찌보면 실제 화면 출력을 담당하는 DisplayController로 보내기 전에 출력될 Layer를 관리하고 필요에 따라서 합성도 해주는 준 최종 관문의 역할을 한다.

 

SurfaceFlinger를 이해하는 것이 분명 Android Drawing Mechanism 을 이해하는데 초석이 되는것은 분명하다. 하지만 SurfaceFlinger는 시스템 서비스로써 여러 Application과 상호작용 한다는 점을 떠올리면, 결국 드로잉의 시작은 Application에서 해야함을 생각할 수 있다.

 

이에 본 포스팅에서는 Application의 Drawing과 그것이 SurfaceFlinger 서비스에 의해 관리되는 과정 일부를 살펴 볼 예정이다.

 

 

위 그림은 사용자의 상호작용에 의해 앱의 화면이 변화할 때, 화면이 다시 갱신되는 과정을 각 계층적으로 나타낸 것이다.

 

앱이 다시 그린 화면은 SurfaceFlinger에 의해 합성되거나 Display Controller에 의해 합성되어 실제 Display에 의해 출력된다. 좀 더 자세히 알아보기 위해 SurfaceFlinger의 역할에 대해 소개한다.

 

 

SurfaceFlinger는 크게 3가지의 역할을 수행하는데, 각각 Layer Management, Vsync Processing 그리고 Screen Refreshing이다.

 

SurfaceFlinger는 하나의 프로세스로 존재하는 시스템 서비스로써 SurfaceFlinger 에 접근하기 위해서는 IPC를 이용해야 하며, 이를 위해 제공하는 인터페이스를 통해 SurfaceFlinger의 기능을 이용한다. SurfaceFlinger는 두 가지 종류의 인터페이스를 지원한다.

 

 

 

이 중 ISurfaceComposerClient는 Client 접미사에서 추측이 가능하듯이 각 어플리케이션마다 관리되는 객체를 컨트롤하기 위한 용도로 사용되며, 주된 기능은 각 어플리케이션 별 Surface를 생성하는 것이다.

 

ISurfaceComposer는 SurfaceFlinger에 의해 관리되는 객체를 컨트롤하기 위한 용도이다.

 

 

이 중 먼저 Layer가 어떻게 관리되는지를 살펴보겠다.

 

Drawing Process (App side)


Layer에 대해 살펴보기 전에 먼저 각 어플리케이션의 View가 어떻게 관리되는지를 알아볼 필요가 있다.

 

 

각 어플리케이션은 여러 뷰를 가질 수 있으며, DecorView를 최종 루트로 가지는 트리 구조로 관리된다.

 

DecorView는 그릴 것이 있는 경우, 할당받은 Surface에 그리게 된다. 따라서 Surface는 각 Application별로 최소 하나는 가지고 있게 된다.

 

그럼 여기서 Surface는 뭐고 Layer는 무엇인가 라는 질문을 할 수 있는데, 엄밀히 말하면 둘은 같은 개념이다. 다음을 보자.

 

 

SurfaceComposerClient로 createSurface 메서드를 호출하면, ISurfaceComposer 인터페이스를 이용해 SurfaceFlinger에 Layer를 하나 만드는 것을 볼 수 있다.

 

즉 Surface는 어플리케이션이 각 소유하는 개념이라면, Layer는 SurfaceFlinger에 의해 관리되는 개념이라고 보면 된다.

 

그러면 이제 각 어플리케이션은 적어도 하나의 Surface를 가지고, 이 Surface는 SurfaceFlinger에 의해 Layer로써 관리된다는 것을 알았다.

 

본격적인 Drawing Process를 설명하기 전 몇 가지 객체에 대해서 설명을 하도록 하겠다.

 

먼저 Choreographer 객체에 대한 설명을 하도록 한다. Choreographer에서는 SurfaceFlinger에서 송신되는 Vsync Event를 요청/수신하여 다음 작업들을 처리한다.

 

1. Input Event Handling

2. Self-Invalidation

3. Animation

 

다음으로 ViewRootImpl은 DecorView와 Choreographer를 연결해주는 일종의 핸들러 역할을 하는 객체로써, DecorView와 Choreographer 사이에서 둘의 요청을 처리하고 중계해주는 역할을 한다.

 

이제 어플리케이션의 Drawing Process를 살펴보도록 하자.

 

실제 그리기는 현재 foreground에 표시되고 있는 어플리케이션에서 그릴 것이 생김으로써 시작된다. 무언가 그릴 것이 생기면 ViewRootImpl 객체 내에 있는 scheduleTraversal 메서드가 호출된다.

 

scheduleTraversal 메서드 내부에서는 Choreographer 객체에게 다음 vsync를 예약해달라는 요청을 보내게 된다. 이 작업이 아래 그림에서 Invalidate와 Vsync scheduling에 해당된다.

 

 

 

 

Vsync 예약 요청을 받은 Choreographer는 SurfaceFlinger로 하여금 다음 Vsync 도착 시 알려달라고 요청한다.

 

다음 Vsync signal이 도착하면 Choreographer는 ViewRootImpl의 performTraversal 메서드를 호출한다. performTraversal 메서드 내부에서는 다시 그려야 될 부분을 measure하고, layout을 재 구성한 다음, performDraw 메서드를 호출하여 그리기를 수행한다.

 

 

ViewRootImpl 객체에서는 performDraw 메서드에 의해 호출되는 draw 메서드가 최종적으로 DecorView로 하여금 할당받은 Surface에 그리게 한다. 

 

이 때, HardwareRenderer를 이용하는 경우와 Software Reneder를 이용하는 경우로 나뉘게 된다.

 

전자는 GPU를 이용하여 Surface에 그리는 것이며, 후자는 CPU로 그리는 것이다. Android 3.0 버전 이후로 하드웨어 가속을 지원하면서 이후 Android에서는 GPU를 이용하여 그리는 것을 기본값으로 한다.

 

GPU든 CPU든 Surface에 View의 변한 부분을 그리는 것은 동일하지만, 그리는 방법이 다른데, 이 부분에 대한 구체적인 내용은 다음을 참조하면 된다.

 

https://developer.android.com/guide/topics/graphics/hardware-accel.html#model

 

간략히 살펴보면, CPU Drawing(Software Rendering)은 Decorview 에서 시작하여 자식 뷰로 내려가면서 그려내는 방법을 사용하고, GPU Drawing은 뷰가 직접 그리는 것이 아닌 DisplayList 라는 추가적인 객체를 이용하여 그리기 작업을 수행하는 것으로 보인다.

 

그리기가 완료되면, 할당받은 Surface가 변했으므로, 이를 SurfaceFlinger에게 통지해야 한다.

 

Layer Management


각 어플리케이션이 Surface를 생성하는 과정과, Surface에 필요한 부분을 그리는 방법을 설명하였다. 위에서도 언급했듯이, 각 어플리케이션은 적어도 하나의 Surface를 가지고, 이 Surface는 SurfaceFlinger에 의해 Layer로써 관리된다는 것을 알았다. 이를 다시 그림으로 나타내면 다음과 같다.

 

 

그러면 Layer와 Surface는 동등한 개념이지만, 존재하는 위치가 다르다. Surface는 어떻게 다시 그려진 내용을 Layer로 하여금 알게 하는 것일까?

 

이를 알기 위해서 실제 그림에 대한 데이터가 존재하는 위치는 다른 데 있다는 것을 알아야 한다. Surface와 Layer는 추상화된 객체일 뿐이며, 실제 화면에 대한 raw data가 존재하는 곳은 따로 있다. 이를 Buffer라고 한다.

 

USB 디버깅이 허용된 디바이스를 연결하고 쉘에 adb shell dumpsys SurfaceFlinger 를 입력하면 현재 표시되는 화면에 대한 SurfaceFlinger의 상태에 대해 표시가 되는데 여기에 보면 다음과 같은 부분을 찾을 수 있다.

 

 

이 부분이 할당된 Buffer로써 실제 화면에 대한 정보를 담고있다. 각 Surface가 Buffer를 하나씩 소유하게 되면, 메모리가 남아나지 않을 것이므로, 어느 정도의 Buffer를 할당해 두고 필요할 때 마다 획득하여 사용하도록 설계되었다. 즉 Producer, Consumer 모델을 따른다고 볼 수 있다.

 

각 Surface와 Layer를 이어주기 위해 BufferQueue가 사용된다. 이름에서 알 수 있듯이 BufferQueue에는 3개의 Buffer가 들어있다. 

 

Buffer는 누가 사용중이냐에 따라서 4가지의 상태를 가지는데, 각각 다음과 같다.

 

1. Free : Queue 안에 존재하며, Surface 혹은 Layer에 의해 사용되고 있지 않으며, 그려지지도 않은 상태

 

2. dequeued : Queue에서 나왔으며, Surface에 의해 점유 중인 상태

 

3. queued : Surface에 의해 쓰여진 buffer가 Queue 안에 있는 상태

 

4. acquired : queued 상태인 buffer가 Layer에 의해 점유 중인 상태

 

그러면 이제 실제로 Surface와 Layer가 어떻게 통신하는지 그림으로 보면서 알아보자.

그림에는 SurfaceFlinger가 소유한 Layer는 표시하지 않았다.

 

초기 상태에는 free buffer가 queue에 3개 들어있는 상태로 시작한다. queue에는 buffer 자체가 들어있는 것이 아니라 buffer의 handle이 들어가 있다.

 

 

여기서 어플리케이션이 소유한 surface가 invalidate 되면, surface는 buffer의 핸들을 획득한다. 이 시점에서 buffer의 상태는 dequeued가 된다.

 

획득한 buffer의 핸들이 가리키는 곳에 변경된 내용을 그린다. 그리는 주체는 앞서 설명한 CPU 혹은 GPU 중 하나가 된다.

 

 

변경된 buffer 핸들을 다시 queue에 넣는다. 이 시점에서 buffer의 상태는 queued가 된다.

 

다음 vsync가 도달하면, Surface는 다시 그리기 작업을 수행하게 되고, SurfaceFlinger는 queued된 buffer가 queue 내에 존재하게 되므로, 이제 Layer Composing 을 수행 하기 위해 해당 buffer를 획득한다.



출처: https://lastyouth.tistory.com/24 [자유로운날개]

블로그 이미지

kuku_dass

,

Android 그래픽 시스템의 발전 과정.


허니컴 이전에는 Surface Flinger 에서만 GPU 사용


허니컴이 Tablet 용 Framework 이다보니 늘어난 pixel 에 대응하기 위해서 GPU 사용이 필요하게 됨.

onDraw() 이후에 실제 그리는 부분을 CPU 에서 하는 것이 아니라 이제는 GPU 에서 하게 됨.


[android] TextureView 에 대한 이야기


기존 View 는 한 View 가 invalidate 가 되면, dirty check를 한 후, parent 로 올라가면서 invalidate 를 쭉 호출하게 되고, 다시 dirty check 된 녀석까지 draw 를 수행하여 그리게 된다.


[android] TextureView 에 대한 이야기


* 기존 View 의 문제

1. UI 스레드에서만 그릴 수 있다.

2. View 의 계층 구조를 타야 한다.

3. 실시간으로 그리기 어렵다.





* 대안으로 등장한 녀석이 SurfaceView

SurfaceView 의 경우 확대, 축소, 비트맵 캡쳐가 안된다.


* 또 다시 대안으로 등장한 녀석이 GLSurfaceView

GLSurfaceView 는 빨리 전환하게 되면 죽는다.


* 또 다른 대안은 RenderScript

RenderScript 는 젤리빈부터 deprecated 되었다.




TextureView 의 등장


SurfaceTexture, TextureView, SurfaceTextureListener 가 한 팀( Set )으로 작동.

이 중 SurfaceTexture 는 framework 가 관리한다.

TextureView 는 일반 View 처럼 사용한다.

Listener 만 잘 override 해서 구현하면 사용이 간편하다.


TextureView 는 Camera 나 OpenGL 에 주로 사용한다.



출처: http://aroundck.tistory.com/2075 [돼지왕 왕돼지 놀이터]

블로그 이미지

kuku_dass

,

Touch Event 와 관련된 메소드는 dispatchTouchEvent, onTouchEvent 가 있다.


가장 최상위에 있는 dispatchTouchEvent가 호출이 되고, 해당 메소드는 하위의 dispatchTouchEvent를 호출한다.


예)

 Activity

  ViewGroup

   View


이런식으로 되어있다고하면, Activity.dispatchTouchEvent -> ViewGroup.dispatchTouchEvent -> View.dispatchTouchEvent

이렇게 호출을 아래로 내려간다.


가장 최하위 View 의 dispatchTouchEvent 까지 도착하게 되면, 이때 View.onTouch , View.onTouchEvent 가 호출된다.

View.onTouch, onTouchEvent 에서 들어온 이벤트를 보고, 처리할지 말지를 판단한 후, 처리를 할거면 (intercept) return true.

처리하지 않을거면 return false 를 한다.

return 된 값은 View.dispatchTouchEvent 로 가게되고 여기서는 Touch 메소드로 부터 받은 리턴값을 그대로 리턴한다.


ViewGroup.dispatchTouchEvent 로 결과값이 넘어오면, 결과값이 true면 이미 View 쪽에서 이벤트가 처리되었음을 의미하기때문에

자신도 그대로 true를 리턴한다.


만약 View 에서 처리되지 않았다면, (return false) 자신의 onTouch, onTouchEvent로 이벤트를 보낸다.




[출처] : http://dktfrmaster.blogspot.kr/2016/09/blog-post_26.html

[안드로이드] 터치 이벤트 흐름

안드로이드는 뷰의 터치 이벤트를 처리하는 3가지 방법이 있다.
 - 엑티비티에서 onTouchEvent 메서드 재정의
 - 이벤트를 처리하고자 하는 뷰의 서브클래스에서 onTouchEvent 메서드 재정의
 - 처리하고자 하는 뷰에 View.OnTouchListener 리스너 인터페이스를 등록

이벤트 처리의 흐름

엑티비티에서의 이벤트 처리

엑티비티는 onTouchEvent 메서드를 재정의하면 모든 뷰의 터치이벤트를 받을 수 있다.

뷰에서의 이벤트 처리

onTouchEvent 메서드를 재정의하거나, OnTouchListener 인터페이스를 등록함으로써, 해당 뷰의 이벤트만 처리할 수 있다.
 - onTouchEvent 메서드를 재정의하려면 상속받아서 서브클래스를 구성해야 하기에 번거롭다.
 - OnTouchListener를 등록하면 인터페이스를 구현한 메서드에서 터치이벤트를 받을 수 있으므로 상속이 필요없다.
만약 2가지 방법이 다 구현되어 있다면??
 - OnTouchListener의 메서드(onTouch) -> onTouchEvent의 순서로 호출된다.
 - onTouch 메서드가 true를 리턴하면, onTouchEvent는 이벤트를 받지 못한다. 

Activity, ViewGroup, View 사이의 이벤트 처리

화면이 다음과 같이 구성되어 있을 때, 기본적인 터치 이벤트의 흐름은 다음과 같다.
Activity(dispatchTouchEvent)              : 하위 dispatchTouchEvent 호출
  ViewGroup(dispatchTouchEvent)       : onInterceptTouchEvent 호출
    ViewGroup(onInterceptTouchEvent) : 하위 dispatchTouchEvent 호출
    View(dispatchTouchEvent)              : 자신의 이벤트 핸들러 메서드 호출
      View(onTouch)                           : 이벤트 처리 & 처리여부 리턴
      View(onTouchEvent)                    : 이벤트 처리 & 처리여부 리턴
    View(dispatchTouchEvent)              : 자신의 이벤트 핸들러 메서드의 결과값을 리턴
    ViewGroup(onTouch)                     : 이벤트 처리 & 처리여부 리턴
    ViewGroup(onTouchEvent)             : 이벤트 처리 & 처리여부 리턴
  ViewGroup(dispatchTouchEvent)       : 자신 or 하위 레이어의 결과값을 리턴
  Activity(onTouchEvent)                    : 이벤트 처리 & 처리여부 리턴
Activity(dispatchTouchEvent)              : 자신 or 하위 레이어의 결과값을 리턴

- Aictivity, ViewGroup, View는 모두 dispatchTouchEventonTouchEvent 메서드를
  가지고 있다.
- ViewGroup에는 추가적으로 onInterceptTouchEvent 메서드가 있다.
- 이벤트가 발생하면 각 레이어의 dispatchTouchEvent가 가장 먼저 호출된다.
  dispatchTouchEvent의 역할은 하위 레이어의 dispatchTouchEvent를 호출하고,
  하위 레이어가 터치 이벤트를 처리했는지 결과를 받아서 처리하지 않은 경우
  자기 자신의 이벤트 처리 메서드(onTouch, onTouchEvent) 메서드로 이벤트를 보낸다.
  그리고는 이벤트 처리 여부를 상위 dispatchTouchEvent에 리턴한다.
- onInterceptTouchEvent는 ViewGroup의 dispatchTouchEvent의 로직을 대신 담당하여,
  자신에게 속한 하위뷰에게 이벤트를 전달할지 결정한다.
- 모든 메서드는 리턴값이 boolean형인데 true일 경우 이벤트를 처리했음을 의미하고,
  false일 경우 이벤트가 처리되지 않았음을 의미한다.


블로그 이미지

kuku_dass

,