Open35
Flutter内部コードを探る
Flutterの実行ファイルを起動したとき何が起こるのか
Flutter for Windows のコードを読む
用語
- {app}: Flutterアプリのルートディレクトリ
- {flutter}: flutter/flutterのルートディレクトリ
- {engine}: flutter/engineのルートディレクトリ
{app}/windows/runner/main.cppにmain関数であるwWinMain()
が存在
-
in
wWinMain()
-
run_loop: RunLoop
を作成 -
project: DartProject
を作成 -
window: FlutterWindow
を作成 - ウィンドウサイズを指定してwindowにset
-
window.CreateAndShow()
を呼ぶ -
run_loop.Run()
でメインループに入る
-
-
FlutterWindowの定義は{app}/windows/runner/flutter_window.h
- 実装は{app}/windows/runner/flutter_window.cpp
- FlutterWindowは
Win32Window
を継承している
-
Win32Window
- コンストラクタでは特に何もしない
-
Win32Window::CreateAndShow
-
CreateWindow
を呼び出して画面を作成 -
Win32Window::OnCreate()
を呼ぶ
-
-
-
FlutterViewController
を作成 -
run_loop_: RunLoop
のRegisterFlutterInstance(flutter_controller_->engine())
でエンジンを登録 -
Win32Window::SetChildContent()
でcontrollerの画面を登録
-
-
-
RunLoop::Run()
でメインループを起動- ループ内で
RunLoop::ProcessFlutterMessages()
を呼ぶ-
FlutterEngine::ProcessMessages()
を呼ぶ- これはもう使っていない...?(#24232)
-
- ループ内で
-
-
FlutterViewControllerの定義は{engine}/shell/platform/windows/client_wrapper/include/flutter/flutter_view_controller.h
- 実装は{engine}/shell/platform/windows/client_wrapper/flutter_view_controller.cc
- コンストラクタ内
-
engine_: FlutterEngine
を作成 -
controller_: FlutterDesktopViewControllerRef
を作成。FlutterDesktopViewControllerCreate
で作る -
view_: FlutterView
を作成
-
- FlutterEngineの定義は{engine}/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h
- 実装は{engine}/shell/platform/windows/client_wrapper/flutter_engine.cc
-
engine_: FlutterDesktopEngineRef
をFlutterDesktopEngineCreate
で作成-
FlutterDesktopEngineCreate
-
FlutterWindowsEngine
を作成
-
-
-
- 実装は{engine}/shell/platform/windows/client_wrapper/flutter_engine.cc
- FlutterWindowsEngineの定義は{engine}/shell/platform/windows/flutter_windows_engine.h
- 実装は{engine}/shell/platform/windows/flutter_windows_engine.cc
-
task_runner_: TaskRunner = TaskRunner::Create()
でタスクランナーを作成 -
FlutterDesktopMessenger
の作成 -
FlutterDesktopPluginRegistrar
の作成 -
BinaryMessengerImpl
の作成 -
IncomingMessageDispatcher
の作成 -
FlutterWindowsTextureRegistrar::ResolveGlFunctions
の実行 -
FlutterWindowsTextureRegistrar
を作成 -
surface_manager: AngleSurfaceManager
を作成 -
WindowProcDelegateManagerWin32
を作成 -
BasicMessageChannel
を作成
-
- 実装は{engine}/shell/platform/windows/flutter_windows_engine.cc
- FlutterViewの定義は {engine}/shell/platform/windows/client_wrapper/include/flutter/flutter_view.h
- 実装どこ...?(ヘッダファイルだけで完結してる?)
- TaskRunnerの定義は {engine}/shell/platform/windows/task_runner.h
- 実装は{engine}/shell/platform/windows/task_runner_win32.cc
-
TaskRunner::Create()
はTaskRunnerWin32
(TaskRunnerを継承)を吐き出す - コンストラクタ内
-
task_runner_window_: TaskRunnerWin32Window
を作成 - `task_runner_window_のAddDelegateにthisを入れる
-
-
TaskRunnerWin32::ProcessTasks()
が呼ばれるとqueueに積まれたタスクを実行する
- TaskRunnerWin32Windowの定義は{engine}/shell/platform/windows/task_runner_win32_window.h
- 実装は{engine}/shell/platform/windows/task_runner_win32_window.cc
- コンストラクタ内
- Windows APIを主に叩いている
-
CreateWindowEx
でウィンドウを作成(大きさはゼロ?)
-
TaskRunnerWin32Window::ProcessTasks()
を呼ぶとAddDelegateで登録したデリゲートを実行する
-
FlutterEngine::Run
-
FlutterDesktopEngineRun
-
FlluterWindowsEngine::RunWithEntryPoint
- AOTコンパイルの場合、
FlutterProjectBundle::LoadAotData()
を呼ぶ-
Embedder::CreateAotData()
を呼ぶ(Embedder::FlutterCreateAotData()
が実装)-
Dart_LoadELF()
を呼んでaotデータを読み込む
-
-
- TaskRunnerの設定を
FlutterProjectArgs
に乗せる- PlatformTaskRunnerの設定は
FlutterTaskRunnerDescription
へ - 他のRunnerの設定は
FlutterCustomTaskRunner
へ - FlutterEngineProcTable.Run()
-
FlutterEngineGetProcAddresses(&embedder_api_);
内でFlutterEngineProcTable
の各メソッドを指定(RunはFlutterEngineRun
が指定)-
FlutterEngineInitialize
を呼び出し- PlatformViewとかの設定
- Textureとかの設定
- Rasterizerとかの設定
-
flutter::EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost()
の実行 - 諸々の設定を使って
EmbedderEngine
を作成
-
FlutterEngineInitialized
を呼ぶ-
EmbedderEngine::LaunchShell()
を呼ぶ-
Shell::Create()
を呼ぶ
-
-
EmbedderEngine::NotifyCreated()
を呼ぶ -
EmbedderEngine::RunRootIsolate()
を呼ぶ
-
- engine_outにEmbedderEngineを入れて返す
-
- PlatformTaskRunnerの設定は
-
FlutterWindowsEngine.engine_
にEmbedderEngineが入る
- AOTコンパイルの場合、
-
-
- FlutterViewController()
- FlutterWindowEngineを作成
- FlutterDesktopViewControllerCreate
- FlutterWIndowEngine::RunWithEntryPoint
- ...
- FlutterWIndowEngine::RunWithEntryPoint
-
EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost
-
CreateEmbedderManagedThreadHost
を実行-
CreateEmbedderTaskRunner
でPlatformTaskRunnerとRenderTaskRunnerを作成 -
ThreadHost
で残りのTaskRunner(IOとUI、前で失敗したらRasterも)を作成-
fml::Thread
で指定されたスレッドをstd::Threadで作成- MessageLoopを起動
-
-
{engine}/common/TaskRunners
に4つのTaskRunnerをセット(これはセットするだけで中では特に何もしていない) -
EmbedderThreadHost
に4つのTaskRunnerをセット?-
EmbedderThreadHost::PostTask()
を呼ぶことで指定したスレッドにタスクを流せる?
-
-
- EmbedderThreadHostをreturn
-
-
FlutterDesktopViewControllerCreate(engine)
-
window_wrapper: FlutterWindowWin32
を作成-
FlutterWindowWin32
-
WindowWin32
とWindowBindingHandler
を継承 - コンストラクタ内で
WindowWin32::InitializeChild
を呼び出し-
WindowWin32::InitializeChild
-
CreateWindowEx()
で指定された大きさのウィンドウを作成
-
-
-
-
-
state: FlutterDesktopViewControllerState
を作成-
FlutterDesktopViewControllerState
はview::FlutterWindowsViewを持つだけの構造体
-
-
state.view
にFlutterWindowsView
をwindow_wrapper
を渡し作成して代入 -
state.view
にengineをセット- engineをセット
-
FlutterWindowsEngine::SetView()
にthisを入れる - platform_handler_とcursor_handler_を作成
-
FlutterWindowsView::CreateRenderSurface
を呼ぶ-
FlutterWindowsEngine.surface_manager.CreateSurface
を呼ぶ-
AngleSurfaceManager::CreateSurface
- EGLのsurfaceを作成する
-
-
- engineの
RunWithEntryPoint()
が呼ばれていなければ呼ぶ -
FlutterWindowsView::SendInitialBounds
を呼ぶ - stateを返す
-
-
FlutterWindowsView
-
WindowBindingHandler::SetView()
にthisを入れる- これでWindowBindingHandlerから来たイベントを
FlutterWindowsView
のメソッドで受け取れるようになる
- これでWindowBindingHandlerから来たイベントを
-
-
DartProject
- assets_path、icu_data_path、aot_library_pathを指定
- Pathを保持しているだけのクラス
-
Shell::Create()
-
Shell::PerformInitializationTasks()
を呼ぶ- logの設定とか
-
SkGraphics::Init()
を呼んでSkiaを初期化 -
fml::icu::InitializeICU(settings.icu_data_path)
を呼んでICU Dataを初期化 - キャッシュする
-
DartVMRef::Create()
でDartVMを作成-
DartVM::Create()
を呼ぶ-
DartVMData::Create()
を呼ぶ- vmとisolateのsnapshotを読み込むor作成する
-
new DartVM()
-
SkExecutor::SetDefault()
を呼ぶ - DartVMの初期化やら設定やら
dart::bin::BootstrapDartIo()
-
DartUI::InitForGlobal()
- 各クラスの
RegisterNatives()
を呼んでdartのメソッドとc++のメソッドを接続する-
{engine}/lib/ui/
辺りに存在
-
- 各クラスの
DartVMInitializer::Initialize()
- debugフラグの設定やら
-
-
-
-
Shell::CreateWithSnapshot()
を呼ぶ-
fml::TaskRunner::RunNowOrPostTask
にPlatform Task Runnerを指定して実行- Platform Task Runner上で
Shell::CreateShellOnPlatformThread()
を実行
- Platform Task Runner上で
- 作ったshellを返す
-
-
-
Shell::CreateShellOnPlatformThread()
-
new Shell()
でshell作成-
DisplayManager
を作成 -
serviec_protocol_handlers_
を設定- ハンドラの種類は
{engine}/runtime/service_protocol.h
に
- ハンドラの種類は
-
- Raster Task Runnerで
Rasterizer
を作成 - Platform Viewを作成
- IO Task Runnerで
ShellIOManager
を作成 - UI Task Runnerで
Engine
を作成 -
Shell::SetUp()
を呼ぶ-
PlatformView::CreateExternalViewEmbedder()
を呼んでRasterizerにセットする - UI Task Runnerで
Engine::SetupDefaultFontManager()
を呼ぶ
-
-
-
EmbedderEngine::RunRootIsolate()
-
Shell::RunEngine()
- UI Task Runnerで
Engine::Run()
を実行-
Engine::UpdateAssetManager()
を実行 -
RuntimeController::LaunchRootIsolate()
を呼ぶ-
DartIsolate::CreateRunningRootIsolate()
を呼ぶ-
DartIsolate::CreateRootIsolate()
でIsolateを作成 -
IsolateConfiguration::PrepareIsolate()
を実行 -
DartIsolate::RunFromLibrary()
を実行- dartのmain関数を指定
-
DartIsolate::InvokeMainEntrypoint()
をmain関数として実行
-
-
Shell::OnRootIsolateCreated()
を実行
-
-
- UI Task Runnerで
-
- main.dart
-
runApp()
を呼ぶ-
WidgetsFlutterBinding.ensureInitialized()
を呼ぶ-
WidgetFlutterBinding
のインスタンスを作成- in
BindingBase
:initInstances()
を呼ぶ-
GestureBinding.initInstances()
を呼ぶ -
SchedulerBinding.initInstances()
を呼ぶ -
ServicesBinding.initInstances()
を呼ぶ -
PaintingBinding.initInstances()
を呼ぶ -
SemanticsBinding.initInstances()
を呼ぶ -
RendererBinding.initInstances()
を呼ぶ- PipelineOwnerを作成
-
RendererBinding.initRenderView
を呼ぶ-
RendererBinding.createViewConfiguration()
を呼びconfigurationを作る- sizeは画面サイズをdpiに変換した値
-
renderView: RenderView
を作成- Renderツリーの根
-
RendererBinding.initRenderView()
呼ぶ-
RenderView.prepareInitialFrame()
呼ぶ-
RenderObject.scheduleInitialLayout()
呼ぶ- PipelineOwnerの_nodesNeedingLayoutにrenderViewを追加
-
RenderView._updateMatricesAndCreateNewRootLayer()
でrootLayerを作成 -
RenderObject.scheduleInitialPaint(rootLayer)
呼ぶ- _layerHandle.layerにrootLayerをセット
- PipelineOwnerの_nodesNeedingPaintにrenderViewを追加
-
-
-
-
WidgetsBinding.initInstances()
を呼ぶ
-
- in
BindingBase
:initServiceExtensions()
を呼ぶ- デバッグ用の諸々
- in
-
-
WidgetsBinding.scheduleAttachRootWidget()
-
-
-
WidgetsBinding.attachRootWidget()
-
RenderObjectToWidgetAdapter
を作成- Widgetツリーの根となるObject
-
createElement()
でRenderObjectToWidgetElement
を生成 -
createRenderObject()
でrenderViewを返す
-
RenderObjectToWidgetAdapter.attachToRenderTree()
を呼ぶ-
element = RenderObjectToWidgetAdapter.createElement()
を呼ぶ - elementのownerにBuildOwnerを指定
-
BuildOwner.buildScope()
呼ぶ- 渡されたcallbackを呼ぶ
-
RenderObjectToWidgetElement.mount(null,null)
呼ぶ- `RootRenderObjectElement.mount(null,null)`呼ぶ
-
RenderObjectElement.mount(null,null)
呼ぶ- `Element.mount(null,null)呼ぶ
-
Element._updateInheritance()
呼ぶ -
Element._inheritedWidgets
を更新
-
-
widget.createRenderObject()
を呼ぶ-
renderView
が返ってくる
-
-
RenderObjectElement.attachRenderObject(null)
を呼ぶ- slotがnullなので特に何もしないと思う
- `Element.mount(null,null)呼ぶ
-
-
RenderObjectToWidgetElement._rebuild()
呼ぶ- {MyApp}のツリーを作成
- ElementツリーとRenderツリーが作られる
- {MyApp}のツリーを作成
- `RootRenderObjectElement.mount(null,null)`呼ぶ
-
- 渡されたcallbackを呼ぶ
-
-
_renderViewElement: RenderObjectToWidgetElement
にセット -
SchedulerBinding.ensureVisualUpdate()
を実行-
SchedulerBinding.scheduleFrame()
呼ぶ-
SchedulerBinding.ensureFrameCallbacksRegistered()
呼ぶ-
window.onBeginFrame
とwindow.onDrawFrame
をセット
-
-
window.scheduleFrame()
呼ぶ
-
-
-
-
window.scheduleFrame()
-
PlatformDispatcher.scheduleFrame()
-> {engine}内のScheduleFrame()
が呼ばれる-
UIDartState.platform_configuration.client.ScheduleFrame()
呼ぶ -
UIDartState
はいつか作ったメインのDartIsolate
だと思われる -
platform_configuration.client: PlatformConfigurationClient
はRuntimeController
である-
RuntimeController.ScheduleFrame()
内-
Engine.ScheduleFrame()
を呼ぶ-
Animator.requestFrame(true)
を呼ぶ- UI Task Runner内で
Animator.AwaitVSync()
呼ぶ-
VSyncWaiter
でVSyncを待ってからAnimator.BeginFrame()
呼ぶ
-
- UI Task Runner内で
-
-
-
-
-
-
PlatformConfiguration.BeginFrame()
- {engine}/lib/ui/hooks.dartの
_beginFrame()
を呼ぶ-
PlatformDispatcher.onBeginFrame()
呼ぶ-
SchedulerBinding.handleBeginFrame()
呼ぶ-
SchedulerBinding._transientCallbacks
を実行- 最初は登録がないはず
-
-
-
PlatformDispatcher._updateFrameData()
呼ぶ-
PlatformDispatcher.onFrameDataChanged()
呼ぶ
-
-
- {engine}/lib/ui/hooks.dartの
_drawFrame()
を呼ぶ-
PlatformDispatcher.onDrawFrame()
呼ぶ-
SchedulerBinding.handleDrawFrame()
呼ぶ-
SchedulerBinding._persistendCallbacks
を実行 -
SchedulerBinding._postFrameCallbacks
を実行
-
-
-
- {engine}/lib/ui/hooks.dartの
-
RendererBinding.drawFrame()
-
PipelineOwner.flushLayout()
呼ぶ- 再計算の必要があるRenderObjectのレイアウトをすべて更新する
- RenderObject.sizeに大きさ、RenderObject.parentData.offsetにオフセットを書き込む
- _nodesNeedingLayoutにはrenderViewがいる
-
renderView._layoutWithoutResize()
呼ぶ -> RenderObject._layoutWithoutResize()-
RenderView.performLayout()
呼ぶ- childのRenderObject.layout({画面サイズ}.tight)呼ぶ
- RenderObject.markNeedsSemanticsUpdate()呼ぶ
- RenderObject.markNeedsPaint()呼ぶ
-
-
PipelineOwner.flushCompositingBits()
呼ぶ- Renderツリーの
RenderObject.needsCompositing
を更新
- Renderツリーの
-
PipelineOwner.flushPaint()
呼ぶ- RenderObject._layerHandle.layerにpaint
- 描画結果は
layer.picture
に -
_nodesNeedingPaint
ごとに処理する -
renderView
は_nodesNeedingPaint
内に存在 -
renderView._layerHandle.layer!.attached
はtrue-
PaintingContext.repaintCompositedChild(renderView)
呼ぶ
-
-
RenderView.compositeFrame()
呼ぶ-
builder = ui.SceneBuilder()
でSceneBuilderを作成 -
scene = RenderView.layer.buildScene(builder)
でsceneを作成 -
_window.render(scene)
でsceneをengineに送る
-
-
PipelineOwner.flushSemantics()
呼ぶ
-
コメントメモ:
- Element.updateChild(): 子を新しい設定で更新するメソッド。このメソッドはWidgetシステムのコアである。
下の場合を考えよう。
runApp(Align(
child: RichText(
text: const TextSpan(
children: [TextSpan(text: "Hello, "), TextSpan(text: "world!")]),
textDirection: TextDirection.ltr,
),
));
-
RenderObjectToWidgetElement._rebuild()
-
Element.updateChild(null, {MyApp}, Object())
呼ぶ- 子を更新するメソッド
-
newChild = Element.inflateWidget({MyApp}, Object())
-
newChild = Align.createElement()
-
newChild.mount(RenderObjectToWidgetElement, Object())
- これは
SingleChildRenderObjectElement.mount()
-
RenderObjectElement.mount()
呼ぶ-
Element.mount()
呼ぶ- parentとnewSlotを保存
- lifecycleStateをActiveに
- ownerをparentから継承
- globalKeyをownerに登録
- Element._updateInheritance()呼ぶ
- 親の持つinheritedWidgetsを受け取る
-
Align.createRenderObject(this)
を呼んでrenderObjectとする -
RenderObjectElement.attachRenderObject(Object())
呼ぶ- newSlotを_slotに保存
- 親を伝って一番近いRenderObjectElementを探す
- RenderObjectToWidgetElementが見つかる
- RenderObjectToWidgetElement.insertRenderObjectChild(renderObject, newSlot)呼ぶ
- renderObject[renderView]のchildにrenderObjectを保存
- parentDataを更新(FlexibleやPositionedに使われている?)
-
-
Element.updateChild(null, RichText, null)
呼ぶ-
newChild = Element.inflateWidget(RichText, null)
=MultiChildRenderObjectElement(widgets = TextSpan[])
-
newChild[MultiChildRenderObjectElement].mount({AlignのElement}, null)
-
RenderObjectElement.mount()
Element.mount()
-
renderObject = RichText.createRenderObject(this)
=RenderParagraph
-
RenderObjectElement.attachRenderObject(null)
-
{AlignのElement}[SinglechildRenderObjectElement].insertRenderObjectChild(renderObject, null)
- {AlignのElement}のrenderObjectの子にrenderObjectを追加
-
- widget.children{RichText内のTextSpanたち)それぞれを
Element.inflateWidget(children[i],IndexedSlot())
で呼び出す- がTextSpanはここに入っていないので使われない
-
-
-
-
- これは
-
-
ここまでで以下のツリーが作成されている(parent -> child)
- RenderObjectToWidgetAdapter
- Align
- RichText
- Align
- RenderObjectToWidgetElement
- SingleChildRenderObjectElement
- MultiChildRenderObjectElement
- SingleChildRenderObjectElement
- RenderView
- RenderPositionedBox
- RenderParagraph
- RenderPositionedBox
続き
-
RenderView.performLayout()
- RenderPositionedBox.layout({画面サイズ}.tight) ->
RenderObject.layout
- RenderObject._relayoutBoundaryを指定(自分)
- RenderPositionedBox.sizedByParentはFalseのはず
- RenderPositionedBox.performLayout()呼ぶ
- child.layout({画面サイズ}.loosen, parentUsesSize: true)呼ぶ -> RenderObject.layout
- RenderObject._relayoutBoundaryは親と同じやつ
- visitChildren(_cleanChildRelayoutBoundary);も呼ぶんじゃないかなぁ
- RenderBox.performResize()呼ぶ
- RenderParagraph.computeDryLayout(constraints)呼ぶ
- RenderParagraph._layoutChildren(constraints, dry: true)呼ぶ
- childrenはないので空の配列が返る?
- TextPainter.setPlaceholderDimensions([])呼ぶ
- 空なのでなにもしない
- RenderParagraph._layoutText()に幅の制約を与えて呼ぶ
- TextPainter.layout()に幅の制約を与えて呼ぶ
- builder = ui.ParagraphBuilder()
- TextSpan.build(builder)呼ぶ
- childrenそれぞれのTextSpan.build(builder)呼ぶ
- ui.ParagraphBuilder.addText()呼ぶ
- {engine}: ParagraphBuilder::addText()呼ぶ
-
ParagraphBuilderTxt::addText()
呼ぶ- vectorにテキストを追加している
-
- {engine}: ParagraphBuilder::addText()呼ぶ
- ui.ParagraphBuilder.addText()呼ぶ
- childrenそれぞれのTextSpan.build(builder)呼ぶ
- ui.ParagraphBuilder.build()呼ぶ
-
ParagraphBuilderTxt::Build()
を呼んで追加されたテキストを持つtxt::Paragraphを作成 -
Paragraph::Create()
呼ぶ
-
- Paragraph.layout()を幅の制約をつけて呼ぶ
- {engine}
Paragraph::layout
が呼ばれる
- {engine}
- newWidth = Paragraph.maxIntrinsicWidthを得る
- {engine}
Paragraph::maxIntrinsicWidth
が呼ばれる-
txt::ParagraphTxt::getMaxIntrisicWidth
が呼ばれる- max_intrisic_widthはテキストの改行を考えたときの最大幅かな
-
- {engine}
- newWidthを最小最大幅でクランプ
- newWidthに更新があればもう一度Paragraph.layout()
- TextPainter.layout()に幅の制約を与えて呼ぶ
- RenderParagraph._layoutChildren(constraints, dry: true)呼ぶ
- RenderParagraph.computeDryLayout(constraints)呼ぶ
- RenderParagraph.performLayout()呼ぶ
- RenderParagraph._layoutChildren(constraints)呼ぶ
- RenderParagraph._layoutText()に幅の制約を与えて呼ぶ
- _textPainter.sizeをsizeとする
- オーバーフローしてたらその処理
- RenderObject.markNeedsSemanticsUpdate()呼ぶ
- RenderObject.markNeedsPaint()呼ぶ
- shrinkWrapXはfalseなので縦横double.infinityを要求する→親の最大大きさになる
- RenderAligningShiftedBox.alignChild()呼ぶ
- RenderAligningShiftedBox._resolve()呼ぶ
- alignmentとtextDirectionを解決する(Alignmentだとそのまま)
- RenderPositionedBoxと子の左上の距離(offset)を計算してchild.parentData.offsetにセット
- RenderAligningShiftedBox._resolve()呼ぶ
- child.layout({画面サイズ}.loosen, parentUsesSize: true)呼ぶ -> RenderObject.layout
- RenderObject.markNeedsSemanticsUpdate()呼ぶ
- RenderObject.markNeedsPaint()呼ぶ
- PipelineOwner.requestVisualUpdate()呼ぶ
- SchedulerBinding.ensureVisualUpdate()呼ぶ
- PipelineOwner.requestVisualUpdate()呼ぶ
- RenderPositionedBox.layout({画面サイズ}.tight) ->
-
PaintingContext.repaintCompositedChild(renderView)
-
OffsetLayer? childLayer = renderView._layerHandle.layer
== rootLayer -
childLayer.removeAllChildren()
呼ぶ -
childContext = PaintingContext(childLayer, renderView.paintBounds)
- paintBoundsはoffsetとsizeを合わせた矩形情報
-
renderView._paintWithContext(childContext, Offset.zero)
呼ぶ-
renderView.paint(childContext, Offset.zero)
呼ぶ-
childContext.paintChild(renderView.child, Offset.zero)
呼ぶ-
RenderPositionedBox._paintWithContext(childContext, Offset.zero)
呼ぶ-
RenderPositionedBox(=ShiftedBox).paint(childContext, Offset.zero)
呼ぶ-
childContext.paintChild(RenderPositionedBox.child, RenderPositionedBox.child.parentData.offset + Offset.zero)
呼ぶ-
RenderParagraph._paintWithContext(childContext, offset)
呼ぶ-
RenderParagraph.paint(childContext, offset)
呼ぶ-
RenderParagraph._layoutTextWithConstraints()
呼ぶ-
RenderParagraph._layoutText()
に幅の制約を与えて呼ぶ- サイズ更新
-
-
_textPainter.paint(childContext.canvas, offset)
でcanvasに描画 - canvasを最初に使うときに
_currentLayer=PictureLayer()
を生成し、childLayerの子として追加
-
-
-
-
-
-
-
-
-
childContext.stopRecordingIfNeeded()
呼ぶ-
_currentLayer.picture
に描画結果を格納
-
-
ここまででLayerツリーは以下のようになっている
- TransformLayer
- dpiで変形する情報を持つ
- PictureLayer
- rootの大きさで文字がかかれたpictureを持つ
- RenderView.isRepaintBoundary == true
- RenderPositionedBox.isRepaintBoundary == false
- RenderParagraph.isRepaintBoundary == false
-
RenderView.layer.buildScene(builder)
- {engine}/SceneBuilderがスタックを使ってlayerツリーをengineに移す
-
updateSubtreeNeedsAddToScene()
呼ぶ- 自身とサブツリーの`_needsAddToSceneを更新する
- 自身ないしは子の_needsAddToSceneがtrueならtrue
-
TransformLayer
とPictureLayer
は共にtrue
-
TransformLayer.addToScene(builder)
呼ぶ-
builder.pushTransform()
でtransform情報をengineに積む -
ContainerLayer.addChildrenToScene()
呼ぶ- 子ごとに
child.addToScene(builder)
呼ぶ -
PictureLayer.addToScene()
-
builder.addPicture(picture)
で描画をengineに追加
-
- 子ごとに
-
builder.pop()
呼ぶ
-
- return
builder.build()
- Sceneを作成
- Sceneはlayerツリーの参照を持ってるだけだと思う
ここまででSceneが持っているツリー
- ContainerLayer (RootLayerとして追加された)
- TransformLayer
- PictureLayer
- TransformLayer
-
_window.render(scene)
=FlutterView.render()
- {engine}内の
Render()
が呼ばれる-
UIDartState.platform_configuration.client.Render(scene)
が呼ばれる -
RuntimeController::Render()
が呼ばれる-
Engine::Render(layer_tree)
呼ぶ-
Animator::Render(layer_tree)
呼ぶ-
Animator::producer_continuation_
(キュー)にlayer_treeを追加- これでpipelineに追加されている?
-
Shell::OnAnimatorDraw(pipeline)
呼ぶ-
RasterTaskRunner
内でRasterizer::Draw(pipeline)
呼ぶ-
pipeline.consume(consumer)
経由でRasterizer::DoDraw(layer_tree)
呼ぶ-
Rasterizer::DrawToSurface(layer_tree)
呼ぶ-
Rasterizer::DrawToSurfaceUnsafe(layer_tree)
呼ぶ-
frame = Rasterizer::surface_.AcquireFrame(layer_tree.frame_size())
でフレームを作成 -
root_surface_canvas=frame->SkiaCanvas()
でcanvasを作成 -
compositor_frame
=CompositorContext.AcquireFrame()
でScopedFrame
を作成-
ScopedFrame::Raster(layer_tree)
呼ぶ-
LayerTree.Paint(ScopedFrame)
呼ぶ-
SkNWayCanvas internal_nodes_canvas
を作成してframe.canvasを追加する- これが実際に書き込むcanvas
-
Layer::PaintContext
をScopedFrame
から作成 -
root_layer_(=ContainerLayer).Paint(context)
呼ぶ-
ContainerLayer::Paint(context)
-
ContainerLayer::PaintChildren(context)
呼ぶ-
TransformLayer::Paint(context)
呼ぶ-
SkAutoCanvasRestore save(context.internal_nodes_canvas, true)
を呼んでスコープを抜けたら変換行列を戻すようにする -
context.internal_nodes_canvas.concat(transform_)
でcanvasに変換行列を適用-
TransformLayer::PaintChildren(context)
呼ぶ-
PictureLayer::Paint(context)
呼ぶ-
PictureLayer.picture.playback(canvas)
呼ぶ- canvasにpictureを描画する
-
-
-
-
-
-
-
-
-
-
-
frame.submit()
で画面に表示
-
-
-
-
-
-
-
-
-
- {engine}内の
-
Animator.requestFrame(bool regenerate_layer_tree)
は基本引数がtrue
- PlatformViewの関連のとき(
PlatformView::Delegate::OnPlatformViewMarkTextureFrameAvailable()
)だけfalse
で呼んでる
- PlatformViewの関連のとき(
- LayerTreeは
あまり再利用しない- _needsPaintでマークされた場合子は再生成
- isRepaintBoundary == trueかつ_needsPaint == falseなら、layerHandleの参照から再利用する
- RenderTransformとかもpaint()内で再利用してるわね
- _needsPaintでマークされた場合子は再生成
- SkPictureはRasterCacheにてPicture単位でキャッシュされる
- 再利用できると判断されればキャッシュがDrawされる
- RepaintBoundaryの説明にもそれっぽいことが書いてある
RepaintBoundary has the further side-effect of possibly hinting to the engine that it should further optimize animation performance if the render subtree behind the RepaintBoundary is sufficiently complex and is static while the surrounding tree changes frequently.
- CacheはRasterCacheKey::Mapというstd::unordered_mapに保存される
- キーはRasterCacheKeyで、id・type・matrixを持つ
- 値はRasterCache::Entryで、このフレームで使われたか・アクセス回数・RasterCacheResultを持つ
- RasterCacheResultはSkImageとSkRectを持つ
- RasterCache::Prepare()を呼んだときにCacheを新規作成
- PictureLayerだとPreroll内で呼んでいる
- RenderObject._relayoutBoundary
- https://juejin.cn/post/6981368741653119013
- ツリー中で再レイアウトする部分を決定
- _relayoutBoundary != this の場合親を探索
- _relayoutBoundary == this の場合自身で再レイアウトの伝搬を停止
- 親はrelayoutされない
- sizedByParent == trueの場合
- layout()内でperformResize()が呼ばれ、ここでサイズを決定する
- performLayout()内ではサイズを変更してはいけない
- sizedByParent == falseの場合
- performResize()は呼ばれない
- performLayout()内でサイズを適宜変更する
- performResize()はRenderBoxではcomputeDryLayout()で更新される
HitTest詳細
- hitTestではRenderObjectの左上からのローカルな座標が渡される
- 子のhitTestを呼ぶときにOffset分を引いて流している
- pointerの位置から見た場合、Offsetの適用はその分を引くこと
- HitTestResultのStackには-offsetを積む
- RenderTransformなどはそのままtransformをStackに積む
- Stackに積まれた変換行列はそこまでの累積をHitTestEntryの追加時に渡す
- 根を右に置いて左から掛け合わせることで、順に変換したのと同等の変換行列になっている
- EntryのtransformはGestureBinding.dispatchEventで使われる
- abstract class PointerEvent with Diagnosticable
- mixin _PointerEventDescription on PointerEvent
- abstract class _AbstractPointerEvent implements PointerEvent
- abstract class _TransformedPointerEvent extends _AbstractPointerEvent with Diagnosticable, _PointerEventDescription
- mixin _CopyPointerXxxEvent on PointerEvent
- class PointerXxxEvent extends PointerEvent with _PointerEventDescription, _CopyPainterXxxEvent
- class _TransformedPointerXxxEvent extends _TransformedPointerEvent with _CopyPointerXxxEvent implements PointerXxxEvent
Layerについて
- RasterではPaint前にPrerollを呼ぶ
- サイズ確定のため?
- PictureLayer -> 持っているpictureからrectを計算
- MutatorsStackはPlatformViewの変形用
- TransformLayer -> context.cull_rectに逆行列を埋めて子のrectを計算
- 子のrectを変換して自分のrectとする
- read_backはよくわからないけどフラグでオン・オフできるからなくてもよいのかな
- GLFWKeyCallback()
- TextInputPlugin::KeyboardHook()
- KeyEventHandler::KeyboardHook()
- BasicMessageChannel::Send()
- BinaryMessenger::Send()
- BinaryMessengerはPluginRegistrarのBinaryMessengerImpl
- FlutterDesktopMessengerSend()
- FlutterDesktopMessengerSendWithReply()
- FlutterEngineSendPlatformMessage()
- EmbedderEngine::SendPlatformMessage()
- PlatformView::DispatchPlatformMessage()
- Shell::OnPlatformViewDispatchPlatformMessage()
- (in UI TaskRunner) Engine::DispatchPlatformMessage()
- RuntimeController::DispatchPlatformMessage()
- PlatformConfiguration::DispatchPlatformMessage()
- (hooks.dart) _dispatchPlatformMessage()
- PlatformDispatcher._dispatchPlatformMessage()
- ChannelBuffers.push()
- _Channel.push()
- "flutter/keyevent"のcallbackが設定されていれば呼び出し
- 後述
- 設定されていなければqueueに保存
- これは設定されたときにdrain()で全て呼び出される
- "flutter/keyevent"のcallbackが設定されていれば呼び出し
- _Channel.push()
- ChannelBuffers.push()
- PlatformDispatcher._dispatchPlatformMessage()
- (hooks.dart) _dispatchPlatformMessage()
- PlatformConfiguration::DispatchPlatformMessage()
- RuntimeController::DispatchPlatformMessage()
- (in UI TaskRunner) Engine::DispatchPlatformMessage()
- Shell::OnPlatformViewDispatchPlatformMessage()
- PlatformView::DispatchPlatformMessage()
- EmbedderEngine::SendPlatformMessage()
- FlutterEngineSendPlatformMessage()
- FlutterDesktopMessengerSendWithReply()
- FlutterDesktopMessengerSend()
- BinaryMessengerはPluginRegistrarのBinaryMessengerImpl
- BinaryMessenger::Send()
- BasicMessageChannel::Send()
- ServicesBinding.initInstances()
- ServicesBinding._initKeyboard()
- window.onKeyData = _keyEventManager.handleKeyData
- PlatformDispatcher.setOnKeyData
- ChannelBuffers.setListener()
- _Channel.setListener()
- "flutter/keyData"にcallbackを登録
- _Channel.setListener()
- ChannelBuffers.setListener()
- PlatformDispatcher.setOnKeyData
- SystemChannels.keyEvent.setMessageHandlerにKeyEventManager.handleRawKeyMessageを指定
- _DefaultBinaryMessenger.setMessageHandler()
- ui.channelBuffers.setListener()
- "flutter/keyEvent"にcallbackを追加
- ui.channelBuffers.setListener()
- _DefaultBinaryMessenger.setMessageHandler()
- window.onKeyData = _keyEventManager.handleKeyData
- ServicesBinding._initKeyboard()
- つまりcallbackはKeyEventManager.handleRawKeyMessage()
needsAddToSceneの調査
- trueになる時
- Layerの生成時
- markNeedsAddToScene()が呼ばれた時
- updateSubtreeNeedsAddToScene()でalwaysNeedsAddToSceneがtrue
- updateSubtreeNeddsAddToScene()で子のneedsAddToSceneがtrue
- falseになる時
- ContainerLayer.buildScene()が呼ばれた後
- Layer._addToSceneWithRetainedRendering()が呼ばれた後
- ContainerLayer.addChildrenToScene()から呼ばれる
- これは各addToScene()から呼ばれる
- どちらもaddToScene()が呼ばれた後である
- ContainerLayer.addChildrenToScene()から呼ばれる
- markNeedsAddToScene()の呼ばれるタイミング
- engineLayerが設定された時
- layerのプロパティが変更された時
- needsAddToSceneの使われるタイミング
- Layer._addToSceneWithRetainedRendering()のみ
- falseかつengineLayerがある場合addRetainedを呼び処理を省略
- ContainerLayer.addChildrenToScene()からのみ呼ばれる
- Layer._addToSceneWithRetainedRendering()のみ
SceneBuilderのoldLayerの役割
- pushXxxを呼ぶとき一緒にoldLayerを渡すことができる
- oldLayerはインスタンスが使い回されるわけではない
- 毎回新たにLayerが生成される
- 新しいLayerはoldLayerからoriginal_layer_idを継承する
- 同じidを持つかどうかはLayer.IsReplacing()で確認される
- ContainerLayer::DiffChildren()で利用
- ContainerLayer::Diff()で利用
- ここで使い回せるかどうかを判定している?
- ContainerLayer::Diff()で利用
- ContainerLayer::DiffChildren()で利用
- addRetained()を使うとインスタンスを使い回すことができる
- needsAddToSceneがfalseの場合