flutterchina / flutter_in_action_2nd

《Flutter实战 第二版》 书稿
https://book.flutterchina.club/
2.25k stars 349 forks source link

书中内容不知是否有误:书中提到"...Layer 会对应一个 Skia 引擎中的 Layer...",但似乎没找到skia layer(附源码分析) #109

Open fzyzcjy opened 2 years ago

fzyzcjy commented 2 years ago

https://book.flutterchina.club/chapter14/paint.html

有变换功能的容器类 Layer 会对应一个 Skia 引擎中的 Layer,为了和Flutter framework中 Layer 区分,flutter 中将 Skia 的Layer 称为 engine layer Skia 可以支持多层渲染,但并不是层越多越好,engineLayer 是会占用一定的资源

以OffsetLayer为例

https://github.com/flutter/engine/blob/2962099077b36704071802ea5595f4a016a1c214/lib/ui/compositing/scene_builder.h#L39 https://github.com/flutter/engine/blob/3f2750c1f8965f8f6378f986237bebf46b06689b/lib/ui/compositing/scene_builder.cc#L97

void SceneBuilder::pushOffset(Dart_Handle layer_handle,
                              double dx,
                              double dy,
                              fml::RefPtr<EngineLayer> oldLayer) {
  SkMatrix sk_matrix = SkMatrix::Translate(dx, dy);
  auto layer = std::make_shared<flutter::TransformLayer>(sk_matrix);
  PushLayer(layer);
  EngineLayer::MakeRetained(layer_handle, layer);

  if (oldLayer && oldLayer->Layer()) {
    layer->AssignOldLayer(oldLayer->Layer().get());
  }
}

因此,创建了 TransformLayer。其源码为:https://cs.github.com/flutter/engine/blob/e6233bd30b2cf196a41eced388980991e3fe573b/flow/layers/transform_layer.cc

void TransformLayer::Paint(PaintContext& context) const {
  TRACE_EVENT0("flutter", "TransformLayer::Paint");
  FML_DCHECK(needs_painting(context));

  SkAutoCanvasRestore save(context.internal_nodes_canvas, true);
  context.internal_nodes_canvas->concat(transform_);

  PaintChildren(context);
}

此处并没有找到"skia引擎中的layer"。

挖掘TransformLayer的父类,ContainerLayer: https://cs.github.com/flutter/engine/blob/e6233bd30b2cf196a41eced388980991e3fe573b/flow/layers/container_layer.cc

直接翻阅源码,似乎没有找到;在该文件源码中搜索"sk"也没有类似"skia引擎中的layer"的东西。


因此,请问一下,"...Layer 会对应一个 Skia 引擎中的 Layer..."中,"skia引擎的layer"指的是?

我猜有可能指的是 https://api.skia.org/classSkCanvas.html#a06bd76ce35082366bb6b8e6dfcb6f435 SkCanvas.saveLayer 之类的layer相关行为。然而,这个saveLayer和Flutter Canvas.saveLayer似乎非常接近。那么,平时CustomPainter调用Canvas.saveLayer时,就创建了一个skia引擎的layer吗?

谢谢

wendux commented 2 years ago

Canvas对象中也有名为 ...layer 相关的 API,如 Canvas.saveLayer,它和本节介绍的Layer 含义不同。Canvas对象中的 layer 主要是提供一种在绘制过程中缓存中间绘制结果的手段,为了在绘制复杂对象时方便多个绘制元素之间分离绘制而设计的,更多关于Canvas layer相关API读者可以查阅相关文档,我们可以简单认为不管 Canvas 对创建多少个 layer,这些 layer 都是在同一个 PictureLayer 上(当然具体Canvas API底层实现方式还是 Flutter团队说了算,但作为应用开发者,理解到这里就够了)

fzyzcjy commented 2 years ago

谢谢回复。不过还是不太理解本issue提出的问题:我没有找到skia引擎中的Layer。比如dart层面的OffsetLayer、ContainerLayer这些,只有engine层C++ TransformLayer,但似乎skia中没有相关的东西。

Canvas对象中也有名为 ...layer 相关的 API,如 Canvas.saveLayer,它和本节介绍的Layer 含义不同。Canvas对象中的 layer 主要是提供一种在绘制过程中缓存中间绘制结果的手段,为了在绘制复杂对象时方便多个绘制元素之间分离绘制而设计的

感谢。仔细查看发现确实是无关的。

当然具体Canvas API底层实现方式还是 Flutter团队说了算,但作为应用开发者,理解到这里就够了

这两天好奇Flutter底层实现,所以研究到C++层了,遇到这个问题。

fzyzcjy commented 2 years ago

进一步地,似乎dart层的OffsetLayer(即c++ engine层的TransformLayer),对应的是skia层SkCanvas.saveSkCanvas.restore,理由如下。

观察TransformLayer::paint

void TransformLayer::Paint(PaintContext& context) const {
  TRACE_EVENT0("flutter", "TransformLayer::Paint");
  FML_DCHECK(needs_painting(context));

  SkAutoCanvasRestore save(context.internal_nodes_canvas, true);
  context.internal_nodes_canvas->concat(transform_);

  PaintChildren(context);
}

可以看到SkAutoCanvasRestore save(context.internal_nodes_canvas, true);。文档:https://api.skia.org/classSkAutoCanvasRestore.html

doSave call SkCanvas::save() Stack helper class calls SkCanvas::restoreToCount when SkAutoCanvasRestore goes out of scope.

由此可知,(1)该对象构造时,调用了SkCanvas::save(2)该对象析构时,调用SkCanvas::restoreToCount(类似restore)。 然后,paint就直接调用PaintChildren(context);了。

所以,它的核心逻辑可能是:

sk_canvas->save();
PaintChildren();
sk_canvas->restore();

换言之,不存在"skia引擎中的layer",而是直接用skia引擎的save/restore机制实现。

fzyzcjy commented 2 years ago

@wendux "希望您在阅读的过程中能积极参与到本书的纠错以及未完成内容的创作上来,也算是有所付出。" (https://book.flutterchina.club/)

我可以写一下这一块内容,提交PR