퉁탕퉁탕 만들어보자

Android Rendering? 본문

Computer/Android

Android Rendering?

호숀티 2022. 5. 8. 12:55
반응형

이전 포스팅 : View lifecycle https://hosyonty.tistory.com/entry/Android-View

 

Android View LifeCycle

View의 수명은 3가지로 구성된다. * Attachment / Detachment * Traversals * State save/restore Attachment 이미 window에 속한 ViewGroup에 view가 add될 때, onAttachedToWindow() 콜백을 받게된다. 이때 view..

hosyonty.tistory.com

 

뷰트리를 트레버스 하면서 메저링하고, 레이아웃에서 알맞은 포지션에 두고, 드로우까지 거친 뒤에는 어떻게 그려지는지 계속 알아보자

 

 

우리가 잘 아는 모든일들은 UI Thread에서 일어난다. 이 모든게 다 된 다음,

1. 이 결과물을 Render Thread에서 이것들을 sync 한다. RenderThread는 UI Thread와는 별개로 GPU 쪽과 통신하는 일을 한다.

2. render thread에서 execute해서 java layer에서 일어난 모든 결과물들을 native 로 바꾼다.

3. Graphics의 GPU에서 buffer를 얻어 온다. 

4. buffer에 이 모든 information들을 쓴다.

5. GPU command를 사용해서 issue 하고, 

6. swap buffer함

7. Graphics에서 composite을 함. 

 

 

실제 예를 들어서 살펴보자

간단한 Recycler뷰가 있는 window의 뷰 하이어러키는 다음과 같이 되어있다.

상위와 좌측은 우리가 그린건 아니고, 우측 ContentFrameLayout 부터 우리 앱의 Activity 부분이다.

 

여기서 유저가 특정 item을 클릭했다고 해보자

click Input이 들어온다. (ACTION_DOWN -> ACTION_UP)

item의 onClick에서 BG컬러를 바꾸는 작업을 했다고 해보자. View의 setBackgroundColor()함수를 콜하면 그 안에서 여러 작업들을 한 뒤 invalidate()를 해서 새로 그리게 한다.

invalidate는 직접 새로 그리는 작업을 하는건 아니고 뷰 하이어러키에 re-draw하라고 알려주는 역할을 한다.

 

그러면 이제 item2에서 invalidate() 가 호출되면 부모의 부모를 타고 하이어러키 하단에서 위로 invalidateChild()가 호출된다. 다 올라가고 나서 최종적으로 DecorView의 ViewRoot에서 scheduleTraversals(), 즉 무언가 새로그려졌으니 traversal을 하기 위해 schedule한다.

 

Traversal을 새로 한다는것은, frame을 그리기 위해서 모든 단계의 rendering (measure, layout, draw)를 수행한다는 것이다.

맨 위에서부터 draw()가 불린다.

draw에서 getDisplayList()를 통해서 displayList를 가져오는데, DisplayList는 view의 rendering commands들이다. 예를 들면 item2를 그린다면 rect, text 이렇게 네모와 text를 그려라 이런 것이다.

이번에는 트리의 위 -> 아래로 item2에 닿을때까지 getDisplayList()가 불리고, item2가 draw()되며 onDraw()가 불리면서 끝나게 된다.!

 

item2에서 의 displayList를 보면 rect를 그리고, text를 그린다.

 

Tree의 draw를 다 한뒤 그린것을 GPU로 전부 sync하는 Sync 단계가 일어나게 된다.

UI Thread에서 일어나야할 displayList를 모두 복사하고, Damage Area에 대한 정보를 같이 넘겨주는데, 

위의 그림에서 같이 넘겨준다고 되어있는 Damage Area는 item2를 뜻하는데, frame에서 변경된것이 오직 item2라는 것이다. 그 위의 다른 것들은 다시 그릴 필요가 없다.

 

Hardware bitmap이라는건 새로운 타입의 bitmap인데, Android Oreo 에서 추가되었다.

일반적으로 bitmap이라고 하면 java side에서 memory를 allocate하고 이제 그려야할 때가 되면 GPU쪽으로 bitmap의 카피를 만드는데, 이게 RAM을 2배 쓰기 때문에 메모리도 많이 쓰고 + 시간도 많이 쓰게된다. 

HW bitmap은 Oreo에서 추가되었는데, 

자바쪽의 수식은 유지할 수 있고, bitmap은 메모리상에 java쪽에는 없고 GPU쪽에만 갖고있을 수 있다.

 

만약에 bitmap을 수정하지 않을 거라면, 메모리 측면에서 매우 효율적이다.

 

RenderThread는 Lollipop에서 나왔는데, display List들을 GPU쪽으로 issue하는데 사용되는 thread이다. native code이고, Java쪽을 call하는 일은 없다.

 

싱크가 완료되면, DLOps (display list operation)에서 optimization을 수행한다. 그리고 이제 Reordered Ops에서 그 순서가 재조정된다.

 

Reorder는 이를테면 item1에 대해서 Rect를 그리고, text를 그리고 item2에 대해서 Rect를 그리고, text를 그리고를 하는것보다 Rect를 한꺼번에 싹 그리고, 그다음 text를 싹 한꺼번에 그리고 하는것이 빠르다. 

텍스트의 컬러와 폰트가 모두 같다면 다른 draw text 콜을 따로 부를 필요가 없이 single call로 전체 스크린의 text를 한꺼번에 그릴 수 있다.

 

그래서 위 그림에서 operation 순서를 보면 fill, "item 0" 텍스트 그리기가 교차되어있는데 Reordered Ops를 보면 Fill을 한꺼번에 쭉 하고 "Item 0"~ Text를 그리는걸 한꺼번에 하는식으로 순서가 재조정 되어있다.

 

그 다음, Clip Reject를 통해서 실제 damaged item - 새로 그려야만 하는 item을 꺼낸다. 나머지는 새로 그릴 필요가 없다. 

 

이제 여기서 get buffer로 GPU의 Buffer를 가져오고 (사실은 request하지 않고 GPU가 buffer를 줌. 좀더 자세히 말하자면 Surface Flinger가 Buffer를 줌) 그다음 이 glCommand()들을 buffer에 넣는다. (fill, text... 이런것들이 모두 glCommand()이다.) 그리고 이제 buffer를 swap한다. 이제 rendering을 할 준비는 다 했다. swap buffer은 이제 뒤에 있는 버퍼를 앞에 있는 버퍼로 swap한다는것이다. 

 

그다음 SurfaceFlinger에서 composite step을 수행한다.

App의 layer와 status bar, navi bar들을 HW Compositor에서 composite하고 이제 Display에 넘겨주면 모든 화면 조합 그리기가 완성되었다!!

 

출처:

https://www.youtube.com/watch?v=zdQRIYOST64

728x90
반응형

'Computer > Android' 카테고리의 다른 글

Android Webview Javascript interface  (1) 2022.05.08
View Binding  (0) 2022.05.08
Android View LifeCycle  (0) 2022.05.07
Android RecyclerView  (0) 2022.05.05
ViewModel  (0) 2022.04.24