blob: a9927e99f8e35ebc9095a118ee46e1ab56bbf9cc [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/graphics/compositing/PaintArtifactCompositor.h"
#include "cc/layers/content_layer_client.h"
#include "cc/layers/layer.h"
#include "cc/layers/picture_layer.h"
#include "cc/playback/display_item_list.h"
#include "cc/playback/display_item_list_settings.h"
#include "cc/playback/drawing_display_item.h"
#include "cc/playback/transform_display_item.h"
#include "cc/trees/clip_node.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/property_tree.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/transform_node.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/graphics/paint/ClipPaintPropertyNode.h"
#include "platform/graphics/paint/DisplayItem.h"
#include "platform/graphics/paint/DrawingDisplayItem.h"
#include "platform/graphics/paint/ForeignLayerDisplayItem.h"
#include "platform/graphics/paint/PaintArtifact.h"
#include "platform/graphics/paint/RasterInvalidationTracking.h"
#include "platform/graphics/paint/ScrollPaintPropertyNode.h"
#include "platform/graphics/paint/TransformPaintPropertyNode.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCompositorSupport.h"
#include "public/platform/WebLayer.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/skia_util.h"
#include "wtf/Allocator.h"
#include "wtf/Noncopyable.h"
#include "wtf/PtrUtil.h"
#include <algorithm>
#include <memory>
#include <utility>
namespace blink {
template class RasterInvalidationTrackingMap<const cc::Layer>;
static RasterInvalidationTrackingMap<const cc::Layer>&
ccLayersRasterInvalidationTrackingMap() {
DEFINE_STATIC_LOCAL(RasterInvalidationTrackingMap<const cc::Layer>, map, ());
return map;
}
template <typename T>
static std::unique_ptr<JSONArray> sizeAsJSONArray(const T& size) {
std::unique_ptr<JSONArray> array = JSONArray::create();
array->pushDouble(size.width());
array->pushDouble(size.height());
return array;
}
class PaintArtifactCompositor::ContentLayerClientImpl
: public cc::ContentLayerClient {
WTF_MAKE_NONCOPYABLE(ContentLayerClientImpl);
USING_FAST_MALLOC(ContentLayerClientImpl);
public:
ContentLayerClientImpl(DisplayItem::Id paintChunkId)
: m_id(paintChunkId),
m_debugName(paintChunkId.client.debugName()),
m_ccPictureLayer(cc::PictureLayer::Create(this)) {}
void SetDisplayList(scoped_refptr<cc::DisplayItemList> ccDisplayItemList) {
m_ccDisplayItemList = std::move(ccDisplayItemList);
}
void SetPaintableRegion(gfx::Rect region) { m_paintableRegion = region; }
// cc::ContentLayerClient
gfx::Rect PaintableRegion() override { return m_paintableRegion; }
scoped_refptr<cc::DisplayItemList> PaintContentsToDisplayList(
PaintingControlSetting) override {
return m_ccDisplayItemList;
}
bool FillsBoundsCompletely() const override { return false; }
size_t GetApproximateUnsharedMemoryUsage() const override {
// TODO(jbroman): Actually calculate memory usage.
return 0;
}
void resetTrackedRasterInvalidations() {
RasterInvalidationTracking* tracking =
ccLayersRasterInvalidationTrackingMap().find(m_ccPictureLayer.get());
if (!tracking)
return;
if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled())
tracking->trackedRasterInvalidations.clear();
else
ccLayersRasterInvalidationTrackingMap().remove(m_ccPictureLayer.get());
}
bool hasTrackedRasterInvalidations() const {
RasterInvalidationTracking* tracking =
ccLayersRasterInvalidationTrackingMap().find(m_ccPictureLayer.get());
if (tracking)
return !tracking->trackedRasterInvalidations.isEmpty();
return false;
}
void setNeedsDisplayRect(const gfx::Rect& rect,
RasterInvalidationInfo* rasterInvalidationInfo) {
m_ccPictureLayer->SetNeedsDisplayRect(rect);
if (!rasterInvalidationInfo || rect.IsEmpty())
return;
RasterInvalidationTracking& tracking =
ccLayersRasterInvalidationTrackingMap().add(m_ccPictureLayer.get());
tracking.trackedRasterInvalidations.append(*rasterInvalidationInfo);
if (RuntimeEnabledFeatures::paintUnderInvalidationCheckingEnabled()) {
// TODO(crbug.com/496260): Some antialiasing effects overflow the paint
// invalidation rect.
IntRect r = rasterInvalidationInfo->rect;
r.inflate(1);
tracking.rasterInvalidationRegionSinceLastPaint.unite(r);
}
}
std::unique_ptr<JSONObject> layerAsJSON() {
std::unique_ptr<JSONObject> json = JSONObject::create();
json->setString("name", m_debugName);
IntSize bounds(m_ccPictureLayer->bounds().width(),
m_ccPictureLayer->bounds().height());
if (!bounds.isEmpty())
json->setArray("bounds", sizeAsJSONArray(bounds));
json->setBoolean("contentsOpaque", m_ccPictureLayer->contents_opaque());
json->setBoolean("drawsContent", m_ccPictureLayer->DrawsContent());
ccLayersRasterInvalidationTrackingMap().asJSON(m_ccPictureLayer.get(),
json.get());
return json;
}
scoped_refptr<cc::PictureLayer> ccPictureLayer() { return m_ccPictureLayer; }
bool matches(const PaintChunk& paintChunk) {
return paintChunk.id && m_id == *paintChunk.id;
}
private:
PaintChunk::Id m_id;
String m_debugName;
scoped_refptr<cc::PictureLayer> m_ccPictureLayer;
scoped_refptr<cc::DisplayItemList> m_ccDisplayItemList;
gfx::Rect m_paintableRegion;
};
PaintArtifactCompositor::PaintArtifactCompositor() {
if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled())
return;
m_rootLayer = cc::Layer::Create();
m_webLayer = wrapUnique(
Platform::current()->compositorSupport()->createLayerFromCCLayer(
m_rootLayer.get()));
m_isTrackingRasterInvalidations = false;
}
PaintArtifactCompositor::~PaintArtifactCompositor() {}
void PaintArtifactCompositor::setTracksRasterInvalidations(
bool tracksPaintInvalidations) {
resetTrackedRasterInvalidations();
m_isTrackingRasterInvalidations = tracksPaintInvalidations;
}
void PaintArtifactCompositor::resetTrackedRasterInvalidations() {
for (auto& client : m_contentLayerClients)
client->resetTrackedRasterInvalidations();
}
bool PaintArtifactCompositor::hasTrackedRasterInvalidations() const {
for (auto& client : m_contentLayerClients) {
if (client->hasTrackedRasterInvalidations())
return true;
}
return false;
}
std::unique_ptr<JSONObject> PaintArtifactCompositor::layersAsJSON(
LayerTreeFlags flags) const {
std::unique_ptr<JSONArray> layersJSON = JSONArray::create();
for (const auto& client : m_contentLayerClients) {
layersJSON->pushObject(client->layerAsJSON());
}
std::unique_ptr<JSONObject> json = JSONObject::create();
json->setArray("layers", std::move(layersJSON));
return json;
}
namespace {
static gfx::Rect largeRect(-200000, -200000, 400000, 400000);
static void appendDisplayItemToCcDisplayItemList(const DisplayItem& displayItem,
cc::DisplayItemList* list) {
if (DisplayItem::isDrawingType(displayItem.getType())) {
const SkPicture* picture =
static_cast<const DrawingDisplayItem&>(displayItem).picture();
if (!picture)
return;
// In theory we would pass the bounds of the picture, previously done as:
// gfx::Rect bounds = gfx::SkIRectToRect(picture->cullRect().roundOut());
// or use the visual rect directly. However, clip content layers attempt
// to raster in a different space than that of the visual rects. We'll be
// reworking visual rects further for SPv2, so for now we just pass a
// visual rect large enough to make sure items raster.
list->CreateAndAppendDrawingItem<cc::DrawingDisplayItem>(
largeRect, sk_ref_sp(picture));
}
}
static scoped_refptr<cc::DisplayItemList> recordPaintChunk(
const PaintArtifact& artifact,
const PaintChunk& chunk,
const gfx::Rect& combinedBounds) {
cc::DisplayItemListSettings settings;
scoped_refptr<cc::DisplayItemList> list =
cc::DisplayItemList::Create(settings);
gfx::Transform translation;
translation.Translate(-combinedBounds.x(), -combinedBounds.y());
// Passing combinedBounds as the visual rect for the begin/end transform item
// would normally be the sensible thing to do, but see comment above re:
// visual rects for drawing items and further rework in flight.
list->CreateAndAppendPairedBeginItem<cc::TransformDisplayItem>(translation);
const DisplayItemList& displayItems = artifact.getDisplayItemList();
for (const auto& displayItem : displayItems.itemsInPaintChunk(chunk))
appendDisplayItemToCcDisplayItemList(displayItem, list.get());
list->CreateAndAppendPairedEndItem<cc::EndTransformDisplayItem>();
list->Finalize();
return list;
}
scoped_refptr<cc::Layer> foreignLayerForPaintChunk(
const PaintArtifact& paintArtifact,
const PaintChunk& paintChunk,
gfx::Vector2dF& layerOffset) {
if (paintChunk.size() != 1)
return nullptr;
const auto& displayItem =
paintArtifact.getDisplayItemList()[paintChunk.beginIndex];
if (!displayItem.isForeignLayer())
return nullptr;
const auto& foreignLayerDisplayItem =
static_cast<const ForeignLayerDisplayItem&>(displayItem);
layerOffset = gfx::Vector2dF(foreignLayerDisplayItem.location().x(),
foreignLayerDisplayItem.location().y());
scoped_refptr<cc::Layer> layer = foreignLayerDisplayItem.layer();
layer->SetBounds(foreignLayerDisplayItem.bounds());
layer->SetIsDrawable(true);
return layer;
}
// cc's property trees use 0 for the root node (always non-null).
constexpr int kRealRootNodeId = 0;
// cc allocates special nodes for root effects such as the device scale.
constexpr int kSecondaryRootNodeId = 1;
constexpr int kPropertyTreeSequenceNumber = 1;
// Creates a minimal set of property trees for the compositor.
void setMinimalPropertyTrees(cc::PropertyTrees* propertyTrees, int ownerId) {
// cc is hardcoded to use transform node index 1 for device scale and
// transform.
cc::TransformTree& transformTree = propertyTrees->transform_tree;
transformTree.clear();
cc::TransformNode& transformNode = *transformTree.Node(
transformTree.Insert(cc::TransformNode(), kRealRootNodeId));
DCHECK_EQ(transformNode.id, kSecondaryRootNodeId);
transformNode.source_node_id = transformNode.parent_id;
transformTree.SetTargetId(transformNode.id, kRealRootNodeId);
transformTree.SetContentTargetId(transformNode.id, kRealRootNodeId);
// cc is hardcoded to use clip node index 1 for viewport clip.
cc::ClipTree& clipTree = propertyTrees->clip_tree;
clipTree.clear();
cc::ClipNode& clipNode =
*clipTree.Node(clipTree.Insert(cc::ClipNode(), kRealRootNodeId));
DCHECK_EQ(clipNode.id, kSecondaryRootNodeId);
clipNode.owner_id = ownerId;
// cc is hardcoded to use effect node index 1 for root render surface.
cc::EffectTree& effectTree = propertyTrees->effect_tree;
effectTree.clear();
cc::EffectNode& effectNode =
*effectTree.Node(effectTree.Insert(cc::EffectNode(), kRealRootNodeId));
DCHECK_EQ(effectNode.id, kSecondaryRootNodeId);
effectNode.owner_id = ownerId;
effectNode.clip_id = clipNode.id;
effectNode.has_render_surface = true;
cc::ScrollTree& scrollTree = propertyTrees->scroll_tree;
scrollTree.clear();
cc::ScrollNode& scrollNode =
*scrollTree.Node(scrollTree.Insert(cc::ScrollNode(), kRealRootNodeId));
DCHECK_EQ(scrollNode.id, kSecondaryRootNodeId);
scrollNode.owner_id = ownerId;
scrollNode.transform_id = kRealRootNodeId;
}
} // namespace
std::unique_ptr<PaintArtifactCompositor::ContentLayerClientImpl>
PaintArtifactCompositor::clientForPaintChunk(
const PaintChunk& paintChunk,
const PaintArtifact& paintArtifact) {
// TODO(chrishtr): for now, just using a linear walk. In the future we can
// optimize this by using the same techniques used in PaintController for
// display lists.
for (auto& client : m_contentLayerClients) {
if (client && client->matches(paintChunk))
return std::move(client);
}
return wrapUnique(new ContentLayerClientImpl(
paintChunk.id
? *paintChunk.id
: paintArtifact.getDisplayItemList()[paintChunk.beginIndex].getId()));
}
scoped_refptr<cc::Layer> PaintArtifactCompositor::layerForPaintChunk(
const PaintArtifact& paintArtifact,
const PaintChunk& paintChunk,
gfx::Vector2dF& layerOffset,
Vector<std::unique_ptr<ContentLayerClientImpl>>& newContentLayerClients,
RasterInvalidationTracking* tracking) {
DCHECK(paintChunk.size());
// If the paint chunk is a foreign layer, just return that layer.
if (scoped_refptr<cc::Layer> foreignLayer =
foreignLayerForPaintChunk(paintArtifact, paintChunk, layerOffset))
return foreignLayer;
// The common case: create or reuse a PictureLayer for painted content.
std::unique_ptr<ContentLayerClientImpl> contentLayerClient =
clientForPaintChunk(paintChunk, paintArtifact);
gfx::Rect combinedBounds = enclosingIntRect(paintChunk.bounds);
scoped_refptr<cc::DisplayItemList> displayList =
recordPaintChunk(paintArtifact, paintChunk, combinedBounds);
contentLayerClient->SetDisplayList(std::move(displayList));
contentLayerClient->SetPaintableRegion(gfx::Rect(combinedBounds.size()));
layerOffset = combinedBounds.OffsetFromOrigin();
scoped_refptr<cc::PictureLayer> ccPictureLayer =
contentLayerClient->ccPictureLayer();
ccPictureLayer->SetBounds(combinedBounds.size());
ccPictureLayer->SetIsDrawable(true);
if (paintChunk.knownToBeOpaque)
ccPictureLayer->SetContentsOpaque(true);
DCHECK(!tracking ||
tracking->trackedRasterInvalidations.size() ==
paintChunk.rasterInvalidationRects.size());
for (unsigned index = 0; index < paintChunk.rasterInvalidationRects.size();
++index) {
IntRect rect(enclosingIntRect(paintChunk.rasterInvalidationRects[index]));
gfx::Rect ccInvalidationRect(rect.x(), rect.y(), std::max(0, rect.width()),
std::max(0, rect.height()));
// Raster paintChunk.rasterInvalidationRects is in the space of the
// containing transform node, so need to subtract off the layer offset.
ccInvalidationRect.Offset(-combinedBounds.OffsetFromOrigin());
contentLayerClient->setNeedsDisplayRect(
ccInvalidationRect,
tracking ? &tracking->trackedRasterInvalidations[index] : nullptr);
}
newContentLayerClients.append(std::move(contentLayerClient));
return ccPictureLayer;
}
namespace {
class PropertyTreeManager {
WTF_MAKE_NONCOPYABLE(PropertyTreeManager);
public:
PropertyTreeManager(cc::PropertyTrees& propertyTrees, cc::Layer* rootLayer)
: m_propertyTrees(propertyTrees),
m_rootLayer(rootLayer)
#if DCHECK_IS_ON()
,
m_isFirstEffectEver(true)
#endif
{
m_effectStack.append(BlinkEffectAndCcIdPair{nullptr, kSecondaryRootNodeId});
}
// TODO(pdr): This will need to be unified with how viewport scale works.
void setDeviceScaleFactor(float);
int compositorIdForTransformNode(const TransformPaintPropertyNode*);
int compositorIdForClipNode(const ClipPaintPropertyNode*);
int switchToEffectNode(const EffectPaintPropertyNode& nextEffect);
int compositorIdForCurrentEffectNode() const {
return m_effectStack.last().id;
}
int compositorIdForScrollNode(const ScrollPaintPropertyNode*);
// Scroll offset has special treatment in the transform and scroll trees.
void updateScrollOffset(int layerId, int scrollId);
private:
void buildEffectNodesRecursively(const EffectPaintPropertyNode* nextEffect);
cc::TransformTree& transformTree() { return m_propertyTrees.transform_tree; }
cc::ClipTree& clipTree() { return m_propertyTrees.clip_tree; }
cc::EffectTree& effectTree() { return m_propertyTrees.effect_tree; }
cc::ScrollTree& scrollTree() { return m_propertyTrees.scroll_tree; }
const EffectPaintPropertyNode* currentEffectNode() const {
return m_effectStack.last().effect;
}
// Property trees which should be updated by the manager.
cc::PropertyTrees& m_propertyTrees;
// Layer to which transform "owner" layers should be added. These will not
// have any actual children, but at present must exist in the tree.
cc::Layer* m_rootLayer;
// Maps from Blink-side property tree nodes to cc property node indices.
HashMap<const TransformPaintPropertyNode*, int> m_transformNodeMap;
HashMap<const ClipPaintPropertyNode*, int> m_clipNodeMap;
HashMap<const ScrollPaintPropertyNode*, int> m_scrollNodeMap;
struct BlinkEffectAndCcIdPair {
const EffectPaintPropertyNode* effect;
int id;
};
Vector<BlinkEffectAndCcIdPair> m_effectStack;
#if DCHECK_IS_ON()
HashSet<const EffectPaintPropertyNode*> m_effectNodesConverted;
bool m_isFirstEffectEver;
#endif
};
void PropertyTreeManager::setDeviceScaleFactor(float deviceScaleFactor) {
auto& rootTransformNode = *transformTree().Node(kSecondaryRootNodeId);
rootTransformNode.local.Scale(deviceScaleFactor, deviceScaleFactor);
}
int PropertyTreeManager::compositorIdForTransformNode(
const TransformPaintPropertyNode* transformNode) {
if (!transformNode)
return kSecondaryRootNodeId;
auto it = m_transformNodeMap.find(transformNode);
if (it != m_transformNodeMap.end())
return it->value;
scoped_refptr<cc::Layer> dummyLayer = cc::Layer::Create();
int parentId = compositorIdForTransformNode(transformNode->parent());
int id = transformTree().Insert(cc::TransformNode(), parentId);
cc::TransformNode& compositorNode = *transformTree().Node(id);
transformTree().SetTargetId(id, kRealRootNodeId);
transformTree().SetContentTargetId(id, kRealRootNodeId);
compositorNode.source_node_id = parentId;
FloatPoint3D origin = transformNode->origin();
compositorNode.pre_local.matrix().setTranslate(-origin.x(), -origin.y(),
-origin.z());
compositorNode.local.matrix() =
TransformationMatrix::toSkMatrix44(transformNode->matrix());
compositorNode.post_local.matrix().setTranslate(origin.x(), origin.y(),
origin.z());
compositorNode.needs_local_transform_update = true;
compositorNode.flattens_inherited_transform =
transformNode->flattensInheritedTransform();
compositorNode.sorting_context_id = transformNode->renderingContextID();
m_rootLayer->AddChild(dummyLayer);
dummyLayer->SetTransformTreeIndex(id);
dummyLayer->SetClipTreeIndex(kSecondaryRootNodeId);
dummyLayer->SetEffectTreeIndex(kSecondaryRootNodeId);
dummyLayer->SetScrollTreeIndex(kRealRootNodeId);
dummyLayer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber);
auto result = m_transformNodeMap.set(transformNode, id);
DCHECK(result.isNewEntry);
transformTree().set_needs_update(true);
return id;
}
int PropertyTreeManager::compositorIdForClipNode(
const ClipPaintPropertyNode* clipNode) {
if (!clipNode)
return kSecondaryRootNodeId;
auto it = m_clipNodeMap.find(clipNode);
if (it != m_clipNodeMap.end())
return it->value;
scoped_refptr<cc::Layer> dummyLayer = cc::Layer::Create();
int parentId = compositorIdForClipNode(clipNode->parent());
int id = clipTree().Insert(cc::ClipNode(), parentId);
cc::ClipNode& compositorNode = *clipTree().Node(id);
compositorNode.owner_id = dummyLayer->id();
// TODO(jbroman): Don't discard rounded corners.
compositorNode.clip = clipNode->clipRect().rect();
compositorNode.transform_id =
compositorIdForTransformNode(clipNode->localTransformSpace());
compositorNode.target_transform_id = kRealRootNodeId;
compositorNode.applies_local_clip = true;
compositorNode.layers_are_clipped = true;
compositorNode.layers_are_clipped_when_surfaces_disabled = true;
m_rootLayer->AddChild(dummyLayer);
dummyLayer->SetTransformTreeIndex(compositorNode.transform_id);
dummyLayer->SetClipTreeIndex(id);
dummyLayer->SetEffectTreeIndex(kSecondaryRootNodeId);
dummyLayer->SetScrollTreeIndex(kRealRootNodeId);
dummyLayer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber);
auto result = m_clipNodeMap.set(clipNode, id);
DCHECK(result.isNewEntry);
clipTree().set_needs_update(true);
return id;
}
int PropertyTreeManager::compositorIdForScrollNode(
const ScrollPaintPropertyNode* scrollNode) {
if (!scrollNode)
return kSecondaryRootNodeId;
auto it = m_scrollNodeMap.find(scrollNode);
if (it != m_scrollNodeMap.end())
return it->value;
int parentId = compositorIdForScrollNode(scrollNode->parent());
int id = scrollTree().Insert(cc::ScrollNode(), parentId);
cc::ScrollNode& compositorNode = *scrollTree().Node(id);
compositorNode.owner_id = parentId;
compositorNode.scrollable = true;
compositorNode.scroll_clip_layer_bounds.SetSize(scrollNode->clip().width(),
scrollNode->clip().height());
compositorNode.bounds.SetSize(scrollNode->bounds().width(),
scrollNode->bounds().height());
compositorNode.user_scrollable_horizontal =
scrollNode->userScrollableHorizontal();
compositorNode.user_scrollable_vertical =
scrollNode->userScrollableVertical();
compositorNode.transform_id =
compositorIdForTransformNode(scrollNode->scrollOffsetTranslation());
compositorNode.main_thread_scrolling_reasons =
scrollNode->mainThreadScrollingReasons();
auto result = m_scrollNodeMap.set(scrollNode, id);
DCHECK(result.isNewEntry);
scrollTree().set_needs_update(true);
return id;
}
void PropertyTreeManager::updateScrollOffset(int layerId, int scrollId) {
cc::ScrollNode& scrollNode = *scrollTree().Node(scrollId);
cc::TransformNode& transformNode =
*transformTree().Node(scrollNode.transform_id);
transformNode.scrolls = true;
// Blink creates a 2d transform node just for scroll offset whereas cc's
// transform node has a special scroll offset field. To handle this we
// adjust cc's transform node to remove the 2d scroll translation and
// let the cc scroll tree update the cc scroll offset.
DCHECK(transformNode.local.IsIdentityOr2DTranslation());
auto offset = transformNode.local.To2dTranslation();
transformNode.local.MakeIdentity();
scrollTree().SetScrollOffset(layerId,
gfx::ScrollOffset(-offset.x(), -offset.y()));
scrollTree().set_needs_update(true);
}
unsigned depth(const EffectPaintPropertyNode* node) {
unsigned result = 0;
for (; node; node = node->parent())
result++;
return result;
}
const EffectPaintPropertyNode* lowestCommonAncestor(
const EffectPaintPropertyNode* nodeA,
const EffectPaintPropertyNode* nodeB) {
// Optimized common case.
if (nodeA == nodeB)
return nodeA;
unsigned depthA = depth(nodeA), depthB = depth(nodeB);
while (depthA > depthB) {
nodeA = nodeA->parent();
depthA--;
}
while (depthB > depthA) {
nodeB = nodeB->parent();
depthB--;
}
DCHECK_EQ(depthA, depthB);
while (nodeA != nodeB) {
nodeA = nodeA->parent();
nodeB = nodeB->parent();
}
return nodeA;
}
int PropertyTreeManager::switchToEffectNode(
const EffectPaintPropertyNode& nextEffect) {
const EffectPaintPropertyNode* ancestor =
lowestCommonAncestor(currentEffectNode(), &nextEffect);
while (currentEffectNode() != ancestor)
m_effectStack.removeLast();
#if DCHECK_IS_ON()
DCHECK(m_isFirstEffectEver || currentEffectNode())
<< "Malformed effect tree. Nodes in the same property tree should have "
"common root. "
<< &nextEffect;
m_isFirstEffectEver = false;
#endif
buildEffectNodesRecursively(&nextEffect);
return compositorIdForCurrentEffectNode();
}
void PropertyTreeManager::buildEffectNodesRecursively(
const EffectPaintPropertyNode* nextEffect) {
if (nextEffect == currentEffectNode())
return;
DCHECK(nextEffect);
buildEffectNodesRecursively(nextEffect->parent());
DCHECK_EQ(nextEffect->parent(), currentEffectNode());
#if DCHECK_IS_ON()
DCHECK(!m_effectNodesConverted.contains(nextEffect))
<< "Malformed paint artifact. Paint chunks under the same effect should "
"be contiguous.";
m_effectNodesConverted.add(nextEffect);
#endif
// We currently create dummy layers to host effect nodes and corresponding
// render surfaces. This should be removed once cc implements better support
// for freestanding property trees.
scoped_refptr<cc::Layer> dummyLayer = cc::Layer::Create();
m_rootLayer->AddChild(dummyLayer);
// Also cc assumes a clip node is always created by a layer that creates
// render surface.
cc::ClipNode& dummyClip =
*clipTree().Node(clipTree().Insert(cc::ClipNode(), kSecondaryRootNodeId));
dummyClip.owner_id = dummyLayer->id();
dummyClip.transform_id = kRealRootNodeId;
dummyClip.target_transform_id = kRealRootNodeId;
cc::EffectNode& effectNode = *effectTree().Node(effectTree().Insert(
cc::EffectNode(), compositorIdForCurrentEffectNode()));
effectNode.owner_id = dummyLayer->id();
effectNode.clip_id = dummyClip.id;
effectNode.opacity = nextEffect->opacity();
m_effectStack.append(BlinkEffectAndCcIdPair{nextEffect, effectNode.id});
dummyLayer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber);
dummyLayer->SetTransformTreeIndex(kSecondaryRootNodeId);
dummyLayer->SetClipTreeIndex(dummyClip.id);
dummyLayer->SetEffectTreeIndex(effectNode.id);
dummyLayer->SetScrollTreeIndex(kRealRootNodeId);
}
} // namespace
void PaintArtifactCompositor::update(
const PaintArtifact& paintArtifact,
RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations) {
DCHECK(m_rootLayer);
cc::LayerTree* layerTree = m_rootLayer->GetLayerTree();
// The tree will be null after detaching and this update can be ignored.
// See: WebViewImpl::detachPaintArtifactCompositor().
if (!layerTree)
return;
if (m_extraDataForTestingEnabled)
m_extraDataForTesting = wrapUnique(new ExtraDataForTesting);
setMinimalPropertyTrees(layerTree->property_trees(), m_rootLayer->id());
m_rootLayer->RemoveAllChildren();
m_rootLayer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber);
m_rootLayer->SetTransformTreeIndex(kSecondaryRootNodeId);
m_rootLayer->SetClipTreeIndex(kSecondaryRootNodeId);
m_rootLayer->SetEffectTreeIndex(kSecondaryRootNodeId);
m_rootLayer->SetScrollTreeIndex(kRealRootNodeId);
PropertyTreeManager propertyTreeManager(*layerTree->property_trees(),
m_rootLayer.get());
propertyTreeManager.setDeviceScaleFactor(layerTree->device_scale_factor());
Vector<std::unique_ptr<ContentLayerClientImpl>> newContentLayerClients;
newContentLayerClients.reserveCapacity(paintArtifact.paintChunks().size());
for (const PaintChunk& paintChunk : paintArtifact.paintChunks()) {
gfx::Vector2dF layerOffset;
scoped_refptr<cc::Layer> layer = layerForPaintChunk(
paintArtifact, paintChunk, layerOffset, newContentLayerClients,
rasterChunkInvalidations ? rasterChunkInvalidations->find(&paintChunk)
: nullptr);
int transformId = propertyTreeManager.compositorIdForTransformNode(
paintChunk.properties.transform.get());
int scrollId = propertyTreeManager.compositorIdForScrollNode(
paintChunk.properties.scroll.get());
int clipId = propertyTreeManager.compositorIdForClipNode(
paintChunk.properties.clip.get());
int effectId = propertyTreeManager.switchToEffectNode(
*paintChunk.properties.effect.get());
propertyTreeManager.updateScrollOffset(layer->id(), scrollId);
layer->set_offset_to_transform_parent(layerOffset);
m_rootLayer->AddChild(layer);
layer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber);
layer->SetTransformTreeIndex(transformId);
layer->SetClipTreeIndex(clipId);
layer->SetEffectTreeIndex(effectId);
layer->SetScrollTreeIndex(scrollId);
// TODO(jbroman): This probably shouldn't be necessary, but it is still
// queried by RenderSurfaceImpl.
layer->Set3dSortingContextId(layerTree->property_trees()
->transform_tree.Node(transformId)
->sorting_context_id);
layer->SetShouldCheckBackfaceVisibility(
paintChunk.properties.backfaceHidden);
if (m_extraDataForTestingEnabled)
m_extraDataForTesting->contentLayers.append(layer);
}
m_contentLayerClients.clear();
m_contentLayerClients.swap(newContentLayerClients);
// Mark the property trees as having been rebuilt.
layerTree->property_trees()->sequence_number = kPropertyTreeSequenceNumber;
layerTree->property_trees()->needs_rebuild = false;
}
} // namespace blink