zoukankan      html  css  js  c++  java
  • Property Trees & DispalyItem

    版权声明:本文为CSDN博主「tornmy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/tornmy/article/details/82593718

    tornmy 2018-09-10 21:53:11 407 收藏 2
    分类专栏: chromium
    版权
    //src/third_party/blink/renderer/core/paint/README.md

    Property Tree:

    DisplayItem:

    什么是property tree:
    Paint properties define characteristics of how a paint chunk should be drawn,
    such as the transform it should be drawn with. To enable efficient updates,
    a chunk's paint properties are described hierarchically. For instance, each
    chunk is associated with a transform node, whose matrix should be multiplied by
    its ancestor transform nodes in order to compute the final transformation matrix
    to the screen.


    Transform Tree

    Each paint chunk is associated with a [transform node](TransformPaintPropertyNode.h),
    which defines the coordinate space in which the content should be painted.

    Each transform node has:

    1* a 4x4 [`TransformationMatrix`](../../transforms/TransformationMatrix.h)
    2* a 3-dimensional transform origin, which defines the origin relative to which
    the transformation matrix should be applied (e.g. a rotation applied with some
    transform origin will rotate the plane about that point)
    3* a pointer to the parent node, which defines the coordinate space relative to
    which the above should be interpreted
    4* a boolean indicating whether the transform should be projected into the plane
    of its parent (i.e., whether the total transform inherited from its parent
    should be flattened before this node's transform is applied and propagated to
    children)
    5* an integer rendering context ID; content whose transform nodes share a
    rendering context ID should sort together

    The parent node pointers link the transform nodes in a hierarchy (the *transform
    tree*), which defines how the transform for any painted content can be
    determined.
    其中1、2、4、5已统一用State结构体封装。父节点指针继承自PaintPropertyNode,通过parent节点连接成Tree。

    Clip Tree

    ### Clips

    Each paint chunk is associated with a [clip node](ClipPaintPropertyNode.h),
    which defines the raster region that will be applied on the canvas when
    the chunk is rastered.

    Each clip node has:

    * A float rect with (optionally) rounded corner radius.
    * An associated transform node, which the clip rect is based on.

    The raster region defined by a node is the rounded rect transformed to the
    root space, intersects with the raster region defined by its parent clip node
    (if not root).
    Effect Tree

    ### Effects

    Each paint chunk is associated with an [effect node](EffectPaintPropertyNode.h),
    which defines the effect (opacity, transfer mode, filter, mask, etc.) that
    should be applied to the content before or as it is composited into the content
    below.

    Each effect node has:

    * a floating-point opacity (from 0 to 1, inclusive)
    * a pointer to the parent node, which will be applied to the result of this
    effect before or as it is composited into its parent in the effect tree

    The paret node pointers link the effect nodes in a hierarchy (the *effect
    tree*), which defines the dependencies between rasterization of different
    contents.

    One can imagine each effect node as corresponding roughly to a bitmap that is
    drawn before being composited into another bitmap, though for implementation
    reasons this may not be how it is actually implemented.
    Scroll Tree

    ### Scrolling

    Each paint chunk is associated with a [scroll node](ScrollPaintPropertyNode.h)
    which defines information about how a subtree scrolls so threads other than the
    main thread can perform scrolling. Scroll information includes:

    * Which directions, if any, are scrollable by the user.
    * A reference to a [transform node](TransformPaintPropertyNode.h) which contains
    a 2d scroll offset.
    * The extent that can be scrolled. For example, an overflow clip of size 7x9
    with scrolling contents of size 7x13 can scroll 4px vertically and none
    horizontally.

    To ensure geometry operations are simple and only deal with transforms, the
    scroll offset is stored as a 2d transform in the transform tree.
    blink为什么需要输出property tree:抽象出property tree可以

    how a subtree scrolls so threads other than the
    main thread can perform scrolling
    也许在一定程度上可以提高renderer进程的效率

    blink property trees在Layout Tree进行prepaint时构造,(paint时则会产生Displayitem list和创建Graphics Layer Tree)。

    具体调用栈为

    void PrePaintTreeWalk::Walk(const LayoutObject& object) {
    ...

    WalkInternal(object, context());

    for (const LayoutObject* child = object.SlowFirstChild(); child;
    child = child->NextSibling()) {
    if (child->IsLayoutMultiColumnSpannerPlaceholder()) {
    child->GetMutableForPainting().ClearPaintFlags();
    continue;
    }
    Walk(*child);
    }
    ...
    }
    void PrePaintTreeWalk::WalkInternal(const LayoutObject& object,
    PrePaintTreeWalkContext& context) {
    PaintInvalidatorContext& paint_invalidator_context =
    context.paint_invalidator_context;
    ...
    base::Optional<PaintPropertyTreeBuilder> property_tree_builder;
    bool property_changed = false;
    if (context.tree_builder_context) {
    property_tree_builder.emplace(object, *context.tree_builder_context);
    // create Transform, Clip and Effect Trees
    property_changed = property_tree_builder->UpdateForSelf();
    ...
    }

    ...
    InvalidatePaintForHitTesting(object, context);

    if (context.tree_builder_context) {
    // create Scroll Tree
    property_changed |= property_tree_builder->UpdateForChildren();
    InvalidatePaintLayerOptimizationsIfNeeded(object, context);
    ...
    }
    // set Property Trees's state to Graphics Layer
    CompositingLayerPropertyUpdater::Update(object);
    ...
    }
    }
    通过先序遍历Layout Layer Tree创建四种tree,具体通过创建FragmentPaintPropertyTreeBuilder,用其创建每一个节点。FragmentPaintPropertyTreeBuilder保存了ObjectPaintProperties* properties_中,它指向LayoutObject的fragment_。

    FragmentPaintPropertyTreeBuilder(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& full_context,
    PaintPropertyTreeBuilderFragmentContext& context,
    FragmentData& fragment_data)
    : object_(object),
    full_context_(full_context),
    context_(context),
    fragment_data_(fragment_data),
    properties_(fragment_data.PaintProperties()) {}


    fragment_data = &object_.GetMutableForPainting().FirstFragment();
    Each `PaintLayer`'s `LayoutObject` has one or more `FragmentData` objects (see
    below for more on fragments). Every `FragmentData` has an
    `ObjectPaintProperties` object if any property nodes are induced by itDuring paint, each display item will be associated with a property
    tree state.
    其中,核心函数为

    void FragmentPaintPropertyTreeBuilder::UpdateForSelf() {
    ...
    if (properties_) {
    UpdateTransform();
    UpdateClipPathClip(false);
    UpdateEffect();
    UpdateLinkHighlightEffect();
    UpdateClipPathClip(true); // Special pass for SPv1 composited clip-path.
    UpdateCssClip();
    UpdateFilter();
    UpdateOverflowControlsClip();
    }
    UpdateLocalBorderBoxContext();
    }
    通过下面的宏(不用虚函数方式是为了提高效率吗)可以统一对所有Property Trees进行创建

    #define ADD_NODE(type, function, variable)
    const type##PaintPropertyNode* function() const { return variable.get(); }
    UpdateResult Update##function(const type##PaintPropertyNode& parent,
    type##PaintPropertyNode::State&& state) {
    auto result = Update(variable, parent, std::move(state));
    DCHECK(!is_immutable_ || result.Unchanged())
    << "Value changed while immutable. New state: "
    << *variable;
    return result;
    }
    对于Transform Tree 调用

    static scoped_refptr<TransformPaintPropertyNode> Create(
    const TransformPaintPropertyNode& parent,
    State&& state) {
    return base::AdoptRef(new TransformPaintPropertyNode(
    &parent, std::move(state), false /* is_parent_alias */));
    }

    // Indicates whether this node is an alias for its parent. Parent aliases are
    // nodes that do not affect rendering and are ignored for the purposes of
    // display item list generation.
    对Clip Tree 调用

    static scoped_refptr<ClipPaintPropertyNode> Create(
    const ClipPaintPropertyNode& parent,
    State&& state) {
    return base::AdoptRef(new ClipPaintPropertyNode(
    &parent, std::move(state), false /* is_parent_alias */));
    }
    对Effect Tree 调用

    static scoped_refptr<EffectPaintPropertyNode> Create(
    const EffectPaintPropertyNode& parent,
    State&& state) {
    return base::AdoptRef(
    new EffectPaintPropertyNode(&parent, std::move(state)));
    }
    对Scroll Tree 调用

    static scoped_refptr<ScrollPaintPropertyNode> Create(
    const ScrollPaintPropertyNode& parent,
    State&& state) {
    return base::AdoptRef(
    new ScrollPaintPropertyNode(&parent, std::move(state)));
    }
    到这其实我们基本得到了四棵Property Trees,但是对于树我们是非常关心它的根节点的,通过它才能访问和更新整个这个树。那么根节点在哪里设置的呢?

    回到PrePaintTreeWalk::Walk

    void PrePaintTreeWalk::Walk(LocalFrameView& frame_view) {
    ...
    // ancestor_overflow_paint_layer does not cross frame boundaries.
    context().ancestor_overflow_paint_layer = nullptr;
    if (context().tree_builder_context) {
    PaintPropertyTreeBuilder::SetupContextForFrame(
    frame_view, *context().tree_builder_context);
    }
    ...
    Walk(*view);
    ...
    }
    通过PaintPropertyTreeBuilder::SetupContextForFrame调用

    PaintPropertyTreeBuilderFragmentContext::
    PaintPropertyTreeBuilderFragmentContext()
    : current_effect(&EffectPaintPropertyNode::Root()) {
    current.clip = absolute_position.clip = fixed_position.clip =
    &ClipPaintPropertyNode::Root();
    current.transform = absolute_position.transform = fixed_position.transform =
    &TransformPaintPropertyNode::Root();
    current.scroll = absolute_position.scroll = fixed_position.scroll =
    &ScrollPaintPropertyNode::Root();
    }
    也就是在这里为4个Property Tree设置了root,以后都可以通过XXXPaintPropertyNode::Root()的方式访问对应的root,

    其创建的方式也比较fancy,root定义了一个静态变量,用lambda创建,以Transform Tree的root为例

    const TransformPaintPropertyNode& TransformPaintPropertyNode::Root() {
    DEFINE_STATIC_REF(
    TransformPaintPropertyNode, root,
    base::AdoptRef(new TransformPaintPropertyNode(
    nullptr,
    State{TransformationMatrix(), FloatPoint3D(), false,
    BackfaceVisibility::kVisible, 0, CompositingReason::kNone,
    CompositorElementId(), &ScrollPaintPropertyNode::Root()},
    true /* is_parent_alias */)));
    return *root;
    }


    #define DEFINE_STATIC_REF(type, name, arguments)
    static type* name = [](scoped_refptr<type> o) {
    if (o)
    o->AddRef();
    return o.get();
    }(arguments);
    而CC使用的Property Tree的结构和blink使用的结构不完全相同(不清楚为什么不把blink和cc的Displayitem格式统一,额外搞一步转换) ,通过PropertyTreeManager转换

    void PaintArtifactCompositor::Update(
    scoped_refptr<const PaintArtifact> paint_artifact,
    CompositorElementIdSet& composited_element_ids,
    TransformPaintPropertyNode* viewport_scale_node) {
    ...
    for (auto& pending_layer : pending_layers) {
    ...
    // convert blink property tree node into cc property tree node
    int transform_id =
    property_tree_manager.EnsureCompositorTransformNode(transform);
    int clip_id = property_tree_manager.EnsureCompositorClipNode(clip);
    int effect_id = property_tree_manager.SwitchToEffectNodeWithSynthesizedClip(
    *property_state.Effect(), *clip);
    // The compositor scroll node is not directly stored in the property tree
    // state but can be created via the scroll offset translation node.
    const auto& scroll_translation =
    ScrollTranslationForPendingLayer(*paint_artifact, pending_layer);
    int scroll_id =
    property_tree_manager.EnsureCompositorScrollNode(&scroll_translation);
    ...
    layer->set_property_tree_sequence_number(g_s_property_tree_sequence_number);
    layer->SetTransformTreeIndex(transform_id);
    layer->SetScrollTreeIndex(scroll_id);
    layer->SetClipTreeIndex(clip_id);
    layer->SetEffectTreeIndex(effect_id);
    ...
    }
    property_tree_manager.Finalize();
    ...
    }
    而cc的property tree存储在LayerTreeHost的成员PropertyTrees中,而LayerTreeHost就是用来管理CC Layer Tree的。

    然后就进一步收集DisplayItemList,它是DisplayItem的集合。

    什么是DisplayItem:
    ## Display items

    A display item is the smallest unit of a display list in Blink. Each display
    item is identified by an ID consisting of:

    * an opaque pointer to the *display item client* that produced it
    * a type (from the `DisplayItem::Type` enum)
    class PLATFORM_EXPORT DisplayItem {
    DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();

    public:
    ...
    // Ids are for matching new DisplayItems with existing DisplayItems.
    struct Id {
    ...
    const DisplayItemClient& client;
    const Type type;
    const unsigned fragment;
    };
    Id GetId() const { return Id(*client_, GetType(), fragment_); }
    ...

    const DisplayItemClient* client_;
    FloatRect visual_rect_;
    float outset_for_raster_effects_;

    static_assert(kTypeLast < (1 << 8), "DisplayItem::Type should fit in 8 bits");
    unsigned type_ : 8;
    unsigned derived_size_ : 8; // size of the actual derived class
    unsigned fragment_ : 14;
    unsigned is_cacheable_ : 1;
    unsigned is_tombstone_ : 1;
    };
    DisplayItemList间接继承自ContiguousContainerBase,具有成员Vector<void*> elements_;

    A Quick Overview of Chrome's Rendering Path

    举个栗子,如下的网页

    <html>
    Hello World
    <img src="chrome_big.jpg" />
    </html>
    将产生如下的DisplayItemList,包含3个DispalyItems:

    从client_包含了对LayoutObject的关联,可以看出其对应的内容,第一个对应与Layout的根节点,另一个对应于InlineTextBox,还有一个对应于LayoutImage。

    从这里看,Displayitem的绘制指令,其实只包含LayoutObject的位置和大小信息,也就是还需要结合property trees。

    Displayitem的创建过程如下

    其中不同的LayoutObject会有不同入口的函数,比如Graphicslayer对应于ViewPainter::PaintBoxDecorationBackground,inlineText对应于InlineTextBoxPainter::Paint,而LayoutImage对应于ImagePainter::PaintReplaced,等等。相同的是它们都会在栈上创建base::DrawingRecorder,在离开对应函数时,调用DrawingRecorder的析构函数,从而创建DisplayItem并Append到DisplayItemList上,而new_display_item_list_为PaintController的成员,是控制整个paint流程的。如下:

    template <typename DisplayItemClass, typename... Args>
    void CreateAndAppend(Args&&... args) {
    ...

    if (DisplayItemConstructionIsDisabled())
    return;

    EnsureNewDisplayItemListInitialCapacity();
    DisplayItemClass& display_item =
    new_display_item_list_.AllocateAndConstruct<DisplayItemClass>(
    std::forward<Args>(args)...);
    display_item.SetFragment(current_fragment_);
    // will cache Displayitem and modify PaintChunk if needed
    ProcessNewItem(display_item);
    }
    其中ProcessNewItem也非常重要,它会把PropertyTreeState相同的DisplayItem组合成以个PaintChunk,保存在PaintChunker中,而new_paint_chunks_也是PaintController的成员,如下:

    void PaintController::ProcessNewItem(DisplayItem& display_item) {
    if (IsSkippingCache() && usage_ == kMultiplePaints)
    display_item.Client().Invalidate(PaintInvalidationReason::kUncacheable);

    new_paint_chunks_.IncrementDisplayItemIndex(display_item);
    auto& last_chunk = new_paint_chunks_.LastChunk();

    last_chunk.outset_for_raster_effects =
    std::max(last_chunk.outset_for_raster_effects,
    display_item.OutsetForRasterEffects());
    ...
    }
    }
    具体通过如下合并相同DisplayItem

    bool PaintChunker::IncrementDisplayItemIndex(const DisplayItem& item) {
    bool item_forces_new_chunk = item.IsForeignLayer() || item.IsScrollHitTest();
    if (item_forces_new_chunk)
    force_new_chunk_ = true;

    size_t new_chunk_begin_index;
    if (chunks_.IsEmpty()) {
    new_chunk_begin_index = 0;
    } else {
    auto& last_chunk = LastChunk();
    if (!force_new_chunk_ && current_properties_ == last_chunk.properties) {
    // Continue the current chunk.
    last_chunk.end_index++;
    // We don't create a new chunk when UpdateCurrentPaintChunkProperties()
    // just changed |next_chunk_id_| but not |current_properties_|. Clear
    // |next_chunk_id_| which has been ignored.
    next_chunk_id_ = base::nullopt;
    return false;
    }
    new_chunk_begin_index = last_chunk.end_index;
    }

    chunks_.emplace_back(new_chunk_begin_index, new_chunk_begin_index + 1,
    next_chunk_id_ ? *next_chunk_id_ : item.GetId(),
    current_properties_);
    next_chunk_id_ = base::nullopt;

    // When forcing a new chunk, we still need to force new chunk for the next
    // display item. Otherwise reset force_new_chunk_ to false.
    if (!item_forces_new_chunk)
    force_new_chunk_ = false;

    return true;
    }
    可以看出,PaintChunk(仅仅是Displayitem的Id(*client_, GetType(), fragment_),而不是Displalyitem本身; )在PaintChunker中线性存贮,而这一步合并不是全局的,而是每次新产生的DisplayItem和之前的PaintChunk的PropertyTreeState形同则合并到统一PaintChunk,即增加该PaintChunk的end_index,否则新加一个PaintChunk。(这一步只是粗略的合并,之后还有进一步合并,还是说此处有进一步优化的可能呢?)

    而最终产生的DisplayItemList和PaintChunk会被打包成PaintArtifact,方便调用,当然它也是PaintControl的成员。

    void PaintController::CommitNewDisplayItems() {
    ...
    // The new list will not be appended to again so we can release unused memory.
    new_display_item_list_.ShrinkToFit();

    current_paint_artifact_ =
    PaintArtifact::Create(std::move(new_display_item_list_),
    new_paint_chunks_.ReleasePaintChunks());
    ...
    }
    Chromium之后会使用slimming paint v2,所以我们关注RuntimeEnabledFeatures::SlimmingPaintV2Enabled()的流程,打开此特性(可以使用的特性的名字在//src/third_party/blink/renderer/platform/runtime_enabled_features.json5的data中查询)。并在开启chrome时添加如下参数:

    chrome --enable-blink-features=SlimmingPaintV2
    而CC使用的displayitem的结构和blink使用的displayitem结构也不完全相同,所以会把PaintChunk转换成CC需要的格式,

    scoped_refptr<cc::DisplayItemList> PaintChunksToCcLayer::Convert(
    const PaintChunkSubset& paint_chunks,
    ...,
    const DisplayItemList& display_items,
    ...) {
    auto cc_list = base::MakeRefCounted<cc::DisplayItemList>(hint);
    ConvertInto(paint_chunks, layer_state, layer_offset, FloatSize(),
    display_items, *cc_list);
    ...
    cc_list->Finalize();
    return cc_list;
    }
    参数中paint_chunks和displayitems正是取自于前面的PaintArtifact,而根据以上内容转换成cc所需要的cc::DisplayItemList,比较核心的成员如下

    class CC_PAINT_EXPORT DisplayItemList
    : public base::RefCountedThreadSafe<DisplayItemList> {
    public:
    ...
    private:
    // RTree stores indices into the paint op buffer.
    // TODO(vmpstr): Update the rtree to store offsets instead.
    RTree<size_t> rtree_;
    DiscardableImageMap image_map_;
    PaintOpBuffer paint_op_buffer_;

    // The visual rects associated with each of the display items in the
    // display item list. These rects are intentionally kept separate because they
    // are used to decide which ops to walk for raster.
    std::vector<gfx::Rect> visual_rects_;
    // Byte offsets associated with each of the ops.
    std::vector<size_t> offsets_;
    ...
    };
    其中paint_op_buffer存储了具体的绘制指令,而红黑树rtree_存储了每个paintchunk的索引号,和对应在paint_op_buffer_中的offset,这样可以常数时间访问所需的displayitem。(visual_rects_还不太明白是干哈的)

    最终转换好的DisplayItemList存储在ContentLayerClientImpl中,其存储于PaintArtifactCompositor,其存储于LocalFrameView。
    ————————————————

  • 相关阅读:
    windows,linux,esxi系统判断当前主机是物理机还是虚拟机?查询主机序列号命令 风行天下
    zabbix监控网络的出入口流量 风行天下
    python 编写远程连接服务器脚本 风行天下
    zabbix 监控windows端cpu使用率百分比 风行天下
    linux计划任务crontab的使用 风行天下
    cd
    rm
    cp
    Windows XP和Windows 7双系统安装和启动菜单修复
    MapInfo、ArcGIS 狙击战 1
  • 原文地址:https://www.cnblogs.com/bigben0123/p/14992048.html
Copyright © 2011-2022 走看看