| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "platform/graphics/compositing/PropertyTreeManager.h" |
| 6 |
| 7 #include "cc/layers/layer.h" |
| 8 #include "cc/trees/clip_node.h" |
| 9 #include "cc/trees/effect_node.h" |
| 10 #include "cc/trees/property_tree.h" |
| 11 #include "cc/trees/scroll_node.h" |
| 12 #include "cc/trees/transform_node.h" |
| 13 #include "platform/graphics/paint/ClipPaintPropertyNode.h" |
| 14 #include "platform/graphics/paint/EffectPaintPropertyNode.h" |
| 15 #include "platform/graphics/paint/ScrollPaintPropertyNode.h" |
| 16 #include "platform/graphics/paint/TransformPaintPropertyNode.h" |
| 17 |
| 18 namespace blink { |
| 19 |
| 20 namespace { |
| 21 |
| 22 static constexpr int kInvalidNodeId = -1; |
| 23 // cc's property trees use 0 for the root node (always non-null). |
| 24 static constexpr int kRealRootNodeId = 0; |
| 25 // cc allocates special nodes for root effects such as the device scale. |
| 26 static constexpr int kSecondaryRootNodeId = 1; |
| 27 |
| 28 } // namespace |
| 29 |
| 30 PropertyTreeManager::PropertyTreeManager(cc::PropertyTrees& propertyTrees, |
| 31 cc::Layer* rootLayer) |
| 32 : m_propertyTrees(propertyTrees), m_rootLayer(rootLayer) { |
| 33 setupRootTransformNode(); |
| 34 setupRootClipNode(); |
| 35 setupRootEffectNode(); |
| 36 setupRootScrollNode(); |
| 37 } |
| 38 |
| 39 cc::TransformTree& PropertyTreeManager::transformTree() { |
| 40 return m_propertyTrees.transform_tree; |
| 41 } |
| 42 |
| 43 cc::ClipTree& PropertyTreeManager::clipTree() { |
| 44 return m_propertyTrees.clip_tree; |
| 45 } |
| 46 |
| 47 cc::EffectTree& PropertyTreeManager::effectTree() { |
| 48 return m_propertyTrees.effect_tree; |
| 49 } |
| 50 |
| 51 cc::ScrollTree& PropertyTreeManager::scrollTree() { |
| 52 return m_propertyTrees.scroll_tree; |
| 53 } |
| 54 |
| 55 const EffectPaintPropertyNode* PropertyTreeManager::currentEffectNode() const { |
| 56 return m_effectStack.back().effect; |
| 57 } |
| 58 |
| 59 void PropertyTreeManager::setupRootTransformNode() { |
| 60 // cc is hardcoded to use transform node index 1 for device scale and |
| 61 // transform. |
| 62 cc::TransformTree& transformTree = m_propertyTrees.transform_tree; |
| 63 transformTree.clear(); |
| 64 cc::TransformNode& transformNode = *transformTree.Node( |
| 65 transformTree.Insert(cc::TransformNode(), kRealRootNodeId)); |
| 66 DCHECK_EQ(transformNode.id, kSecondaryRootNodeId); |
| 67 transformNode.source_node_id = transformNode.parent_id; |
| 68 transformTree.SetTargetId(transformNode.id, kRealRootNodeId); |
| 69 transformTree.SetContentTargetId(transformNode.id, kRealRootNodeId); |
| 70 |
| 71 // TODO(jaydasika): We shouldn't set ToScreen and FromScreen of root |
| 72 // transform node here. They should be set while updating transform tree in |
| 73 // cc. |
| 74 float deviceScaleFactor = m_rootLayer->GetLayerTree()->device_scale_factor(); |
| 75 gfx::Transform toScreen; |
| 76 toScreen.Scale(deviceScaleFactor, deviceScaleFactor); |
| 77 transformTree.SetToScreen(kRealRootNodeId, toScreen); |
| 78 gfx::Transform fromScreen; |
| 79 bool invertible = toScreen.GetInverse(&fromScreen); |
| 80 DCHECK(invertible); |
| 81 transformTree.SetFromScreen(kRealRootNodeId, fromScreen); |
| 82 transformTree.set_needs_update(true); |
| 83 |
| 84 m_transformNodeMap.set(TransformPaintPropertyNode::root(), transformNode.id); |
| 85 m_rootLayer->SetTransformTreeIndex(transformNode.id); |
| 86 } |
| 87 |
| 88 void PropertyTreeManager::setupRootClipNode() { |
| 89 // cc is hardcoded to use clip node index 1 for viewport clip. |
| 90 cc::ClipTree& clipTree = m_propertyTrees.clip_tree; |
| 91 clipTree.clear(); |
| 92 m_propertyTrees.layer_id_to_clip_node_index.clear(); |
| 93 cc::ClipNode& clipNode = |
| 94 *clipTree.Node(clipTree.Insert(cc::ClipNode(), kRealRootNodeId)); |
| 95 DCHECK_EQ(clipNode.id, kSecondaryRootNodeId); |
| 96 |
| 97 clipNode.resets_clip = true; |
| 98 clipNode.owning_layer_id = m_rootLayer->id(); |
| 99 clipNode.clip_type = cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP; |
| 100 clipNode.clip = gfx::RectF( |
| 101 gfx::SizeF(m_rootLayer->GetLayerTree()->device_viewport_size())); |
| 102 clipNode.transform_id = kRealRootNodeId; |
| 103 clipNode.target_transform_id = kRealRootNodeId; |
| 104 clipNode.target_effect_id = kSecondaryRootNodeId; |
| 105 m_propertyTrees.layer_id_to_clip_node_index[clipNode.owning_layer_id] = |
| 106 clipNode.id; |
| 107 |
| 108 m_clipNodeMap.set(ClipPaintPropertyNode::root(), clipNode.id); |
| 109 m_rootLayer->SetClipTreeIndex(clipNode.id); |
| 110 } |
| 111 |
| 112 void PropertyTreeManager::setupRootEffectNode() { |
| 113 // cc is hardcoded to use effect node index 1 for root render surface. |
| 114 cc::EffectTree& effectTree = m_propertyTrees.effect_tree; |
| 115 effectTree.clear(); |
| 116 m_propertyTrees.layer_id_to_effect_node_index.clear(); |
| 117 cc::EffectNode& effectNode = |
| 118 *effectTree.Node(effectTree.Insert(cc::EffectNode(), kInvalidNodeId)); |
| 119 DCHECK_EQ(effectNode.id, kSecondaryRootNodeId); |
| 120 effectNode.owning_layer_id = m_rootLayer->id(); |
| 121 effectNode.transform_id = kRealRootNodeId; |
| 122 effectNode.clip_id = kSecondaryRootNodeId; |
| 123 effectNode.has_render_surface = true; |
| 124 m_propertyTrees.layer_id_to_effect_node_index[effectNode.owning_layer_id] = |
| 125 effectNode.id; |
| 126 |
| 127 m_effectStack.push_back( |
| 128 BlinkEffectAndCcIdPair{EffectPaintPropertyNode::root(), effectNode.id}); |
| 129 m_rootLayer->SetEffectTreeIndex(effectNode.id); |
| 130 } |
| 131 |
| 132 void PropertyTreeManager::setupRootScrollNode() { |
| 133 cc::ScrollTree& scrollTree = m_propertyTrees.scroll_tree; |
| 134 scrollTree.clear(); |
| 135 m_propertyTrees.layer_id_to_scroll_node_index.clear(); |
| 136 cc::ScrollNode& scrollNode = |
| 137 *scrollTree.Node(scrollTree.Insert(cc::ScrollNode(), kRealRootNodeId)); |
| 138 DCHECK_EQ(scrollNode.id, kSecondaryRootNodeId); |
| 139 scrollNode.owning_layer_id = m_rootLayer->id(); |
| 140 scrollNode.transform_id = kSecondaryRootNodeId; |
| 141 m_propertyTrees.layer_id_to_scroll_node_index[scrollNode.owning_layer_id] = |
| 142 scrollNode.id; |
| 143 |
| 144 m_scrollNodeMap.set(ScrollPaintPropertyNode::root(), scrollNode.id); |
| 145 m_rootLayer->SetScrollTreeIndex(scrollNode.id); |
| 146 } |
| 147 |
| 148 int PropertyTreeManager::ensureCompositorTransformNode( |
| 149 const TransformPaintPropertyNode* transformNode) { |
| 150 DCHECK(transformNode); |
| 151 // TODO(crbug.com/645615): Remove the failsafe here. |
| 152 if (!transformNode) |
| 153 return kSecondaryRootNodeId; |
| 154 |
| 155 auto it = m_transformNodeMap.find(transformNode); |
| 156 if (it != m_transformNodeMap.end()) |
| 157 return it->value; |
| 158 |
| 159 scoped_refptr<cc::Layer> dummyLayer = cc::Layer::Create(); |
| 160 int parentId = ensureCompositorTransformNode(transformNode->parent()); |
| 161 int id = transformTree().Insert(cc::TransformNode(), parentId); |
| 162 |
| 163 cc::TransformNode& compositorNode = *transformTree().Node(id); |
| 164 transformTree().SetTargetId(id, kRealRootNodeId); |
| 165 transformTree().SetContentTargetId(id, kRealRootNodeId); |
| 166 compositorNode.source_node_id = parentId; |
| 167 |
| 168 FloatPoint3D origin = transformNode->origin(); |
| 169 compositorNode.pre_local.matrix().setTranslate(-origin.x(), -origin.y(), |
| 170 -origin.z()); |
| 171 compositorNode.local.matrix() = |
| 172 TransformationMatrix::toSkMatrix44(transformNode->matrix()); |
| 173 compositorNode.post_local.matrix().setTranslate(origin.x(), origin.y(), |
| 174 origin.z()); |
| 175 compositorNode.needs_local_transform_update = true; |
| 176 compositorNode.flattens_inherited_transform = |
| 177 transformNode->flattensInheritedTransform(); |
| 178 compositorNode.sorting_context_id = transformNode->renderingContextId(); |
| 179 |
| 180 m_rootLayer->AddChild(dummyLayer); |
| 181 dummyLayer->SetTransformTreeIndex(id); |
| 182 dummyLayer->SetClipTreeIndex(kSecondaryRootNodeId); |
| 183 dummyLayer->SetEffectTreeIndex(kSecondaryRootNodeId); |
| 184 dummyLayer->SetScrollTreeIndex(kRealRootNodeId); |
| 185 dummyLayer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber); |
| 186 |
| 187 auto result = m_transformNodeMap.set(transformNode, id); |
| 188 DCHECK(result.isNewEntry); |
| 189 transformTree().set_needs_update(true); |
| 190 return id; |
| 191 } |
| 192 |
| 193 int PropertyTreeManager::ensureCompositorClipNode( |
| 194 const ClipPaintPropertyNode* clipNode) { |
| 195 DCHECK(clipNode); |
| 196 // TODO(crbug.com/645615): Remove the failsafe here. |
| 197 if (!clipNode) |
| 198 return kSecondaryRootNodeId; |
| 199 |
| 200 auto it = m_clipNodeMap.find(clipNode); |
| 201 if (it != m_clipNodeMap.end()) |
| 202 return it->value; |
| 203 |
| 204 scoped_refptr<cc::Layer> dummyLayer = cc::Layer::Create(); |
| 205 int parentId = ensureCompositorClipNode(clipNode->parent()); |
| 206 int id = clipTree().Insert(cc::ClipNode(), parentId); |
| 207 |
| 208 cc::ClipNode& compositorNode = *clipTree().Node(id); |
| 209 compositorNode.owning_layer_id = dummyLayer->id(); |
| 210 m_propertyTrees.layer_id_to_clip_node_index[compositorNode.owning_layer_id] = |
| 211 id; |
| 212 |
| 213 // TODO(jbroman): Don't discard rounded corners. |
| 214 compositorNode.clip = clipNode->clipRect().rect(); |
| 215 compositorNode.transform_id = |
| 216 ensureCompositorTransformNode(clipNode->localTransformSpace()); |
| 217 compositorNode.target_transform_id = kRealRootNodeId; |
| 218 compositorNode.target_effect_id = kSecondaryRootNodeId; |
| 219 compositorNode.clip_type = cc::ClipNode::ClipType::APPLIES_LOCAL_CLIP; |
| 220 compositorNode.layers_are_clipped = true; |
| 221 compositorNode.layers_are_clipped_when_surfaces_disabled = true; |
| 222 |
| 223 m_rootLayer->AddChild(dummyLayer); |
| 224 dummyLayer->SetTransformTreeIndex(compositorNode.transform_id); |
| 225 dummyLayer->SetClipTreeIndex(id); |
| 226 dummyLayer->SetEffectTreeIndex(kSecondaryRootNodeId); |
| 227 dummyLayer->SetScrollTreeIndex(kRealRootNodeId); |
| 228 dummyLayer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber); |
| 229 |
| 230 auto result = m_clipNodeMap.set(clipNode, id); |
| 231 DCHECK(result.isNewEntry); |
| 232 clipTree().set_needs_update(true); |
| 233 return id; |
| 234 } |
| 235 |
| 236 int PropertyTreeManager::ensureCompositorScrollNode( |
| 237 const ScrollPaintPropertyNode* scrollNode) { |
| 238 DCHECK(scrollNode); |
| 239 // TODO(crbug.com/645615): Remove the failsafe here. |
| 240 if (!scrollNode) |
| 241 return kSecondaryRootNodeId; |
| 242 |
| 243 auto it = m_scrollNodeMap.find(scrollNode); |
| 244 if (it != m_scrollNodeMap.end()) |
| 245 return it->value; |
| 246 |
| 247 int parentId = ensureCompositorScrollNode(scrollNode->parent()); |
| 248 int id = scrollTree().Insert(cc::ScrollNode(), parentId); |
| 249 |
| 250 cc::ScrollNode& compositorNode = *scrollTree().Node(id); |
| 251 compositorNode.owning_layer_id = parentId; |
| 252 m_propertyTrees |
| 253 .layer_id_to_scroll_node_index[compositorNode.owning_layer_id] = id; |
| 254 |
| 255 compositorNode.scrollable = true; |
| 256 |
| 257 compositorNode.scroll_clip_layer_bounds.SetSize(scrollNode->clip().width(), |
| 258 scrollNode->clip().height()); |
| 259 compositorNode.bounds.SetSize(scrollNode->bounds().width(), |
| 260 scrollNode->bounds().height()); |
| 261 compositorNode.user_scrollable_horizontal = |
| 262 scrollNode->userScrollableHorizontal(); |
| 263 compositorNode.user_scrollable_vertical = |
| 264 scrollNode->userScrollableVertical(); |
| 265 compositorNode.transform_id = |
| 266 ensureCompositorTransformNode(scrollNode->scrollOffsetTranslation()); |
| 267 compositorNode.main_thread_scrolling_reasons = |
| 268 scrollNode->mainThreadScrollingReasons(); |
| 269 |
| 270 auto result = m_scrollNodeMap.set(scrollNode, id); |
| 271 DCHECK(result.isNewEntry); |
| 272 scrollTree().set_needs_update(true); |
| 273 |
| 274 return id; |
| 275 } |
| 276 |
| 277 void PropertyTreeManager::updateScrollOffset(int layerId, int scrollId) { |
| 278 cc::ScrollNode& scrollNode = *scrollTree().Node(scrollId); |
| 279 cc::TransformNode& transformNode = |
| 280 *transformTree().Node(scrollNode.transform_id); |
| 281 |
| 282 transformNode.scrolls = true; |
| 283 |
| 284 // Blink creates a 2d transform node just for scroll offset whereas cc's |
| 285 // transform node has a special scroll offset field. To handle this we |
| 286 // adjust cc's transform node to remove the 2d scroll translation and |
| 287 // let the cc scroll tree update the cc scroll offset. |
| 288 DCHECK(transformNode.local.IsIdentityOr2DTranslation()); |
| 289 auto offset = transformNode.local.To2dTranslation(); |
| 290 transformNode.local.MakeIdentity(); |
| 291 scrollTree().SetScrollOffset(layerId, |
| 292 gfx::ScrollOffset(-offset.x(), -offset.y())); |
| 293 scrollTree().set_needs_update(true); |
| 294 } |
| 295 |
| 296 namespace { |
| 297 |
| 298 unsigned depth(const EffectPaintPropertyNode* node) { |
| 299 unsigned result = 0; |
| 300 for (; node; node = node->parent()) |
| 301 result++; |
| 302 return result; |
| 303 } |
| 304 |
| 305 // TODO(chrishtr): templatize this to avoid duplication of |
| 306 // GeometryMapper::leastCommonAncestor. |
| 307 const EffectPaintPropertyNode* lowestCommonAncestor( |
| 308 const EffectPaintPropertyNode* nodeA, |
| 309 const EffectPaintPropertyNode* nodeB) { |
| 310 // Optimized common case. |
| 311 if (nodeA == nodeB) |
| 312 return nodeA; |
| 313 |
| 314 unsigned depthA = depth(nodeA), depthB = depth(nodeB); |
| 315 while (depthA > depthB) { |
| 316 nodeA = nodeA->parent(); |
| 317 depthA--; |
| 318 } |
| 319 while (depthB > depthA) { |
| 320 nodeB = nodeB->parent(); |
| 321 depthB--; |
| 322 } |
| 323 DCHECK_EQ(depthA, depthB); |
| 324 while (nodeA != nodeB) { |
| 325 nodeA = nodeA->parent(); |
| 326 nodeB = nodeB->parent(); |
| 327 } |
| 328 return nodeA; |
| 329 } |
| 330 |
| 331 } // namespace |
| 332 |
| 333 int PropertyTreeManager::switchToEffectNode( |
| 334 const EffectPaintPropertyNode& nextEffect) { |
| 335 const EffectPaintPropertyNode* ancestor = |
| 336 lowestCommonAncestor(currentEffectNode(), &nextEffect); |
| 337 DCHECK(ancestor) << "Malformed effect tree. All nodes must be descendant of " |
| 338 "EffectPaintPropertyNode::root()."; |
| 339 while (currentEffectNode() != ancestor) |
| 340 m_effectStack.pop_back(); |
| 341 |
| 342 // Now the current effect is the lowest common ancestor of previous effect |
| 343 // and the next effect. That implies it is an existing node that already has |
| 344 // at least one paint chunk or child effect, and we are going to either attach |
| 345 // another paint chunk or child effect to it. We can no longer omit render |
| 346 // surface for it even for opacity-only nodes. |
| 347 // See comments in PropertyTreeManager::buildEffectNodesRecursively(). |
| 348 // TODO(crbug.com/504464): Remove premature optimization here. |
| 349 if (currentEffectNode() && currentEffectNode()->opacity() != 1.f) { |
| 350 effectTree() |
| 351 .Node(getCurrentCompositorEffectNodeIndex()) |
| 352 ->has_render_surface = true; |
| 353 } |
| 354 |
| 355 buildEffectNodesRecursively(&nextEffect); |
| 356 |
| 357 return getCurrentCompositorEffectNodeIndex(); |
| 358 } |
| 359 |
| 360 void PropertyTreeManager::buildEffectNodesRecursively( |
| 361 const EffectPaintPropertyNode* nextEffect) { |
| 362 if (nextEffect == currentEffectNode()) |
| 363 return; |
| 364 DCHECK(nextEffect); |
| 365 |
| 366 buildEffectNodesRecursively(nextEffect->parent()); |
| 367 DCHECK_EQ(nextEffect->parent(), currentEffectNode()); |
| 368 |
| 369 #if DCHECK_IS_ON() |
| 370 DCHECK(!m_effectNodesConverted.contains(nextEffect)) |
| 371 << "Malformed paint artifact. Paint chunks under the same effect should " |
| 372 "be contiguous."; |
| 373 m_effectNodesConverted.add(nextEffect); |
| 374 #endif |
| 375 |
| 376 // An effect node can't omit render surface if it has child with exotic |
| 377 // blending mode. See comments below for more detail. |
| 378 // TODO(crbug.com/504464): Remove premature optimization here. |
| 379 if (nextEffect->blendMode() != SkBlendMode::kSrcOver) { |
| 380 effectTree() |
| 381 .Node(getCurrentCompositorEffectNodeIndex()) |
| 382 ->has_render_surface = true; |
| 383 } |
| 384 |
| 385 // We currently create dummy layers to host effect nodes and corresponding |
| 386 // render surfaces. This should be removed once cc implements better support |
| 387 // for freestanding property trees. |
| 388 scoped_refptr<cc::Layer> dummyLayer = nextEffect->ensureDummyLayer(); |
| 389 m_rootLayer->AddChild(dummyLayer); |
| 390 |
| 391 int outputClipId = ensureCompositorClipNode(nextEffect->outputClip()); |
| 392 |
| 393 cc::EffectNode& effectNode = *effectTree().Node(effectTree().Insert( |
| 394 cc::EffectNode(), getCurrentCompositorEffectNodeIndex())); |
| 395 effectNode.owning_layer_id = dummyLayer->id(); |
| 396 effectNode.clip_id = outputClipId; |
| 397 // Every effect is supposed to have render surface enabled for grouping, |
| 398 // but we can get away without one if the effect is opacity-only and has only |
| 399 // one compositing child with kSrcOver blend mode. This is both for |
| 400 // optimization and not introducing sub-pixel differences in layout tests. |
| 401 // See PropertyTreeManager::switchToEffectNode() and above where we |
| 402 // retrospectively enable render surface when more than one compositing child |
| 403 // or a child with exotic blend mode is detected. |
| 404 // TODO(crbug.com/504464): There is ongoing work in cc to delay render surface |
| 405 // decision until later phase of the pipeline. Remove premature optimization |
| 406 // here once the work is ready. |
| 407 if (!nextEffect->filter().isEmpty() || |
| 408 nextEffect->blendMode() != SkBlendMode::kSrcOver) |
| 409 effectNode.has_render_surface = true; |
| 410 effectNode.opacity = nextEffect->opacity(); |
| 411 effectNode.filters = nextEffect->filter().asCcFilterOperations(); |
| 412 effectNode.blend_mode = nextEffect->blendMode(); |
| 413 m_propertyTrees.layer_id_to_effect_node_index[effectNode.owning_layer_id] = |
| 414 effectNode.id; |
| 415 m_effectStack.push_back(BlinkEffectAndCcIdPair{nextEffect, effectNode.id}); |
| 416 |
| 417 dummyLayer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber); |
| 418 dummyLayer->SetTransformTreeIndex(kSecondaryRootNodeId); |
| 419 dummyLayer->SetClipTreeIndex(outputClipId); |
| 420 dummyLayer->SetEffectTreeIndex(effectNode.id); |
| 421 dummyLayer->SetScrollTreeIndex(kRealRootNodeId); |
| 422 } |
| 423 |
| 424 } // namespace blink |
| OLD | NEW |