플러터의 렌더링 구조?
(본 글은 공부하며 적은 글로서 100% 맞음을 보장할 수 없습니다. 틀린 곳은 조언해주시면 감사하겠습니다!)
Dart Developers Korea 오픈채팅방 https://open.kakao.com/o/gYyufB6
지난 글에(https://brunch.co.kr/@myner/6) 이어서 Flutter 렌더링 원리를 보자!
이번 글에서는 Flutter 프레임워크의 View Tree의 생성 및 관리 메커니즘, Layout, Rendering 원리 및 성능 최적화 등에 대한 이야기를 해볼까 한다.
지난 글에서 봤지만
Flutter System Overview는 아래 그림처럼 구성된다.
플러터는 웹뷰 또는 시스템 플랫폼과 함께 제공되는 컨트롤을 사용하지 않고 고성능 렌더링 엔진인 Skia를 사용하여 그림 자체를 그린다.
개발 언어로 dart를 기본으로 사용하고 렌더링 엔진은 C, C++을 사용한다.
Flutter 렌더링 파이프라인
특별한 것은 없다. 여느 플랫폼들과 비슷하다. 안드로이드나 크로미움을 분석해봐도 위의 파이프라인 논리적 구조는 크게 다르지 않다.
Vsync 이벤트가 발생하면 빌드 -> 레이아웃 -> 페인트를 통해서 Layer Tree를 만든다. 이후 rasterize를 하고 compositor를 이용해서 합성하고 이를 gpu로 보내어 실제로 그려낼 것으로 예상된다.
Flutter는 GPU에 뷰 데이터를 제공하고 GPU의 VSync 신호는 UI 스레드와 동기화되며 UI 스레드는 Dart를 사용하여 추상 뷰 구조를 만든다. 이 데이터 구조는 GPU 스레드에 계층화되어 있다.
뷰 데이터는 Skia 엔진에 제공되어 GPU 데이터로 렌더링 되며 OpenGL 또는 Vulkan을 통해 GPU에 제공됩니다. 따라서 Flutter는 디스플레이, 비디오 컨트롤러 및 GPU의 특정 작업에 신경을 쓰지 않으며 GPU에서 전송 한 VSync 신호를 처리하고 가능한 한 빨리 두 VSync 신호 사이의 보기 데이터를 계산 및 합성 한 다음 GPU에 데이터를 제공한다.
그럼 궁금했을 것이다.
왜 Dart를 사용하는 걸까?
다트의 성능이 좋다. JIT 모드에서 Dart는 기본적으로 JavaScript와 같은 속도이다. 그러나 다트는 AOT를 지원한다. AOT 모드에서 실행될 때 JavaScript는 따라잡을 수 없다. 속도 증가는 높은 프레임 속도에서 View 데이터 계산에 유용하다.
네이티브 바인딩.
Android에서는 v8의 기본 바인딩이 제대로 작동하지만 iOS의 JavaScriptCore가 작동하지 않으므로 자바 스크립트를 사용하는 경우 Flutter 인프라의 코드 패턴을 통일하기가 어렵다. 다트의 네이티브 바인딩은 다트 라이브러리와 잘 작동한다.
Fuchsia OS.
Fuchsia OS에 내장된 응용 프로그램 브라우저는 다트 (Dart) 언어를 사용하는 응용 프로그램의 개발 언어이다. 사실, Flutter는 Fuchisa OS의 애플리케이션 프레임 워크 개념의 하위 집합입니다. (Flutter 소스 코드와 빌드 툴체인은 많은 Fuchsia 매크로로 넘쳐난다.)
다트는 포괄적인 패키지 관리 및 많은 기능을 갖춘 Type 안전 언어이다. Google은 JavaScript를 대체할 수 있는 언어를 개발하기 위해 수많은 프로그래밍 언어의 디자이너를 모았다. 따라서 Fuchsia OS에는 Dart가 내장되어 있다. 다트는 임베디드 lib 응용 프로그램으로 임베디드 될 수 있으며 시스템 업그레이드로만 업데이트할 수 있다. 이는 이점 중 하나입니다.
VSync 동기화
뷰를 동적으로 만들려면 뷰 렌더링을 구현하는 것만으로는 충분하지 않으며, 하드웨어에서 VSync 신호를 보내고 VSync 신호를 계산하는시기를 알아야 하며 GPU에 데이터를 제공하여 동적 인터페이스를 구축해야 한다. 그러나 각 플랫폼의 VSync 신호 획득 방법은 동일하지 않다.
Flutter Engine 구성
필자의 이해에 따르면 Flutter Engine은 크게 다트 UI, 런타임, 셸이라는 세 부분으로 나눌 수 있다.
다트 UI
다트 UI는 C ++ 구현되었고 상위 계층으로 제공되는 window, text, canvas, geometry 등의 범용 그래픽 기능을 제공한다. Compositing은 Skia를 사용한다. LayerTree는 웹 브라우저나 여타 플랫폼에 관심이 있다면 한 번쯤 들어 봤을 것이다. Drawing을 위한 추상적인 Layer 들의 Tree구조이다.
런타임
Flutter Engine의 런타임은 Delegate 모드를 사용한다. 플랫폼 관련 코드가 Shell 부분에 전달된다.
아래 그림에서 빨간색 상자는 런타임 섹션에 속한다.
셸
셸은 엔진에서 LayerTree를 가져와서 render 메서드를 호출하는 래퍼를 제공합니다. 다트 UI는 다트에 네이티브 그래픽을 호출하는 기능을 제공합니다.
Tree 구성
Flutter 앱을 만들고 runApp을 호출하면 rootWidget을 rootElement의 자식으로 rootElement에 전달하고, Element트리를 생성하고, Element트리에서 Render 트리를 생성한다.
위젯은 렌더링 콘텐츠를 저장하며, 레이아웃 정보를 볼 수 있고 위젯 등록정보 변경은 불가능하다
Element는 콘텍스트를 저장하고, 요소를 통해 View 트리를 탐색하며 Element는 Widget과 RenderObject를 모두 보유한다. RenderObject는 레이아웃, 위젯의 레이아웃 속성을 기반으로 한 Paint 위젯이다.
이렇게 트리를 구성하고 어떻게 그리는 것일까?
그 부분에 대해서는 다음(https://brunch.co.kr/@myner/7)에 알아보도록 하자
<참고문헌>
https://flutter.io/docs/resources/technical-overview
https://github.com/flutter/flutter
https://github.com/flutter/engine
https://docs.flutter.io/index.html
https://zhuanlan.zhihu.com/p/36861174
https://tech.meituan.com/waimai_flutter_practice.html
https://www.yuque.com/xytech/flutter/hc0xq7
https://www.yuque.com/xytech/flutter/tge705
https://www.jianshu.com/p/e6cd8584fdbb?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
https://flutter.io/docs/development/platform-integration/platform-channels