| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "platform/graphics/compositing/PaintArtifactCompositor.h" | 5 #include "platform/graphics/compositing/PaintArtifactCompositor.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include <utility> | 9 #include <utility> |
| 10 #include "cc/layers/content_layer_client.h" | 10 #include "cc/layers/content_layer_client.h" |
| (...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 335 ccInvalidationRect, | 335 ccInvalidationRect, |
| 336 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index] | 336 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index] |
| 337 : nullptr); | 337 : nullptr); |
| 338 } | 338 } |
| 339 } | 339 } |
| 340 | 340 |
| 341 newContentLayerClients.push_back(std::move(contentLayerClient)); | 341 newContentLayerClients.push_back(std::move(contentLayerClient)); |
| 342 return ccPictureLayer; | 342 return ccPictureLayer; |
| 343 } | 343 } |
| 344 | 344 |
| 345 bool PaintArtifactCompositor::canMergeInto( | 345 PaintArtifactCompositor::PendingLayer::PendingLayer( |
| 346 const PaintArtifact& paintArtifact, | 346 const PaintChunk& firstPaintChunk, |
| 347 const PaintChunk& newChunk, | 347 bool chunkIsForeign) |
| 348 const PendingLayer& candidatePendingLayer) { | 348 : bounds(firstPaintChunk.bounds), |
| 349 const PaintChunk& pendingLayerFirstChunk = | 349 knownToBeOpaque(firstPaintChunk.knownToBeOpaque), |
| 350 *candidatePendingLayer.paintChunks[0]; | 350 backfaceHidden(firstPaintChunk.properties.backfaceHidden), |
| 351 if (paintArtifact.getDisplayItemList()[newChunk.beginIndex].isForeignLayer()) | 351 propertyTreeState(firstPaintChunk.properties.propertyTreeState), |
| 352 return false; | 352 isForeign(chunkIsForeign) { |
| 353 | 353 paintChunks.push_back(&firstPaintChunk); |
| 354 if (paintArtifact.getDisplayItemList()[pendingLayerFirstChunk.beginIndex] | 354 } |
| 355 .isForeignLayer()) | 355 |
| 356 return false; | 356 void PaintArtifactCompositor::PendingLayer::merge( |
| 357 | 357 const PendingLayer& guest, |
| 358 if (newChunk.properties.backfaceHidden != | 358 GeometryMapper& geometryMapper) { |
| 359 pendingLayerFirstChunk.properties.backfaceHidden) | 359 DCHECK(!isForeign && !guest.isForeign); |
| 360 return false; | 360 DCHECK_EQ(backfaceHidden, guest.backfaceHidden); |
| 361 | 361 |
| 362 DCHECK_GE(candidatePendingLayer.paintChunks.size(), 1u); | 362 paintChunks.appendVector(guest.paintChunks); |
| 363 PropertyTreeStateIterator iterator(newChunk.properties.propertyTreeState); | 363 FloatRect guestBoundsInHome = guest.bounds; |
| 364 for (const PropertyTreeState* currentState = | 364 geometryMapper.localToAncestorVisualRect( |
| 365 &newChunk.properties.propertyTreeState; | 365 guest.propertyTreeState, propertyTreeState, guestBoundsInHome); |
| 366 currentState; currentState = iterator.next()) { | 366 FloatRect oldBounds = bounds; |
| 367 if (*currentState == candidatePendingLayer.propertyTreeState) | 367 bounds.unite(guestBoundsInHome); |
| 368 return true; | 368 if (bounds != oldBounds) |
| 369 if (currentState->hasDirectCompositingReasons()) | 369 knownToBeOpaque = false; |
| 370 // TODO(crbug.com/701991): Upgrade GeometryMapper. |
| 371 // If we knew the new bounds is enclosed by the mapped opaque region of |
| 372 // the guest layer, we can deduce the merged layer being opaque too. |
| 373 } |
| 374 |
| 375 static bool canUpcastTo(const PropertyTreeState& guest, |
| 376 const PropertyTreeState& home); |
| 377 bool PaintArtifactCompositor::PendingLayer::canMerge( |
| 378 const PendingLayer& guest) const { |
| 379 if (isForeign || guest.isForeign) |
| 380 return false; |
| 381 if (backfaceHidden != guest.backfaceHidden) |
| 382 return false; |
| 383 if (propertyTreeState.effect() != guest.propertyTreeState.effect()) |
| 384 return false; |
| 385 return canUpcastTo(guest.propertyTreeState, propertyTreeState); |
| 386 } |
| 387 |
| 388 void PaintArtifactCompositor::PendingLayer::upcast( |
| 389 const PropertyTreeState& newState, |
| 390 GeometryMapper& geometryMapper) { |
| 391 DCHECK(!isForeign); |
| 392 geometryMapper.localToAncestorVisualRect(propertyTreeState, newState, bounds); |
| 393 |
| 394 propertyTreeState = newState; |
| 395 // TODO(crbug.com/701991): Upgrade GeometryMapper. |
| 396 // A local visual rect mapped to an ancestor space may become a polygon |
| 397 // (e.g. consider transformed clip), also effects may affect the opaque |
| 398 // region. To determine whether the layer is still opaque, we need to |
| 399 // query conservative opaque rect after mapping to an ancestor space, |
| 400 // which is not supported by GeometryMapper yet. |
| 401 knownToBeOpaque = false; |
| 402 } |
| 403 |
| 404 static bool isNonCompositingAncestorOf( |
| 405 const TransformPaintPropertyNode* ancestor, |
| 406 const TransformPaintPropertyNode* node) { |
| 407 for (; node != ancestor; node = node->parent()) { |
| 408 if (!node || node->hasDirectCompositingReasons()) |
| 370 return false; | 409 return false; |
| 371 } | 410 } |
| 372 return false; | 411 return true; |
| 373 } | 412 } |
| 374 | 413 |
| 375 bool PaintArtifactCompositor::mightOverlap( | 414 // Determines whether drawings based on the 'guest' state can be painted into |
| 376 const PaintChunk& paintChunk, | 415 // a layer with the 'home' state. A number of criteria need to be met: |
| 377 const PendingLayer& candidatePendingLayer, | 416 // 1. The guest effect must be a descendant of the home effect. However this |
| 378 GeometryMapper& geometryMapper) { | 417 // check is enforced by the layerization recursion. Here we assume the guest |
| 418 // has already been upcasted to the same effect. |
| 419 // 2. The guest clip must be a descendant of the home clip. |
| 420 // 3. The local space of each clip and effect node on the ancestor chain must |
| 421 // be within compositing boundary of the home transform space. |
| 422 // 4. The guest transform space must be within compositing boundary of the home |
| 423 // transform space. |
| 424 static bool canUpcastTo(const PropertyTreeState& guest, |
| 425 const PropertyTreeState& home) { |
| 426 DCHECK_EQ(home.effect(), guest.effect()); |
| 427 |
| 428 for (const ClipPaintPropertyNode* currentClip = guest.clip(); |
| 429 currentClip != home.clip(); currentClip = currentClip->parent()) { |
| 430 if (!currentClip || currentClip->hasDirectCompositingReasons()) |
| 431 return false; |
| 432 if (!isNonCompositingAncestorOf(home.transform(), |
| 433 currentClip->localTransformSpace())) |
| 434 return false; |
| 435 } |
| 436 |
| 437 return isNonCompositingAncestorOf(home.transform(), guest.transform()); |
| 438 } |
| 439 |
| 440 // Returns nullptr if 'ancestor' is not a strict ancestor of 'node'. |
| 441 // Otherwise, return the child of 'ancestor' that is an ancestor of 'node' or |
| 442 // 'node' itself. |
| 443 static const EffectPaintPropertyNode* strictChildOfAlongPath( |
| 444 const EffectPaintPropertyNode* ancestor, |
| 445 const EffectPaintPropertyNode* node) { |
| 446 for (; node; node = node->parent()) { |
| 447 if (node->parent() == ancestor) |
| 448 return node; |
| 449 } |
| 450 return nullptr; |
| 451 } |
| 452 |
| 453 bool PaintArtifactCompositor::mightOverlap(const PendingLayer& layerA, |
| 454 const PendingLayer& layerB, |
| 455 GeometryMapper& geometryMapper) { |
| 379 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(), | 456 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(), |
| 380 ClipPaintPropertyNode::root(), | 457 ClipPaintPropertyNode::root(), |
| 381 EffectPaintPropertyNode::root()); | 458 EffectPaintPropertyNode::root()); |
| 382 | 459 |
| 383 FloatRect paintChunkScreenVisualRect = paintChunk.bounds; | 460 FloatRect boundsA = layerA.bounds; |
| 384 geometryMapper.localToAncestorVisualRect( | 461 geometryMapper.localToAncestorVisualRect(layerA.propertyTreeState, |
| 385 paintChunk.properties.propertyTreeState, rootPropertyTreeState, | 462 rootPropertyTreeState, boundsA); |
| 386 paintChunkScreenVisualRect); | 463 FloatRect boundsB = layerB.bounds; |
| 387 | 464 geometryMapper.localToAncestorVisualRect(layerB.propertyTreeState, |
| 388 FloatRect pendingLayerScreenVisualRect = candidatePendingLayer.bounds; | 465 rootPropertyTreeState, boundsB); |
| 389 geometryMapper.localToAncestorVisualRect( | 466 |
| 390 candidatePendingLayer.propertyTreeState, rootPropertyTreeState, | 467 return boundsA.intersects(boundsB); |
| 391 pendingLayerScreenVisualRect); | 468 } |
| 392 | 469 |
| 393 return paintChunkScreenVisualRect.intersects(pendingLayerScreenVisualRect); | 470 bool PaintArtifactCompositor::canDecompositeEffect( |
| 394 } | 471 const EffectPaintPropertyNode* effect, |
| 395 | 472 const PendingLayer& layer) { |
| 396 PaintArtifactCompositor::PendingLayer::PendingLayer( | 473 // If the effect associated with the layer is deeper than than the effect |
| 397 const PaintChunk& firstPaintChunk) | 474 // we are attempting to decomposite, than implies some previous decision |
| 398 : bounds(firstPaintChunk.bounds), | 475 // did not allow to decomposite intermediate effects. |
| 399 knownToBeOpaque(firstPaintChunk.knownToBeOpaque), | 476 if (layer.propertyTreeState.effect() != effect) |
| 400 backfaceHidden(firstPaintChunk.properties.backfaceHidden), | 477 return false; |
| 401 propertyTreeState(firstPaintChunk.properties.propertyTreeState) { | 478 if (layer.isForeign) |
| 402 paintChunks.push_back(&firstPaintChunk); | 479 return false; |
| 403 } | 480 // TODO(trchen): Exotic blending layer may be decomposited only if it could |
| 404 | 481 // be merged into the first layer of the current group. |
| 405 void PaintArtifactCompositor::PendingLayer::add( | 482 if (effect->blendMode() != SkBlendMode::kSrcOver) |
| 406 const PaintChunk& paintChunk, | 483 return false; |
| 407 GeometryMapper* geometryMapper) { | 484 if (effect->hasDirectCompositingReasons()) |
| 408 DCHECK(paintChunk.properties.backfaceHidden == backfaceHidden); | 485 return false; |
| 409 paintChunks.push_back(&paintChunk); | 486 if (!canUpcastTo(layer.propertyTreeState, |
| 410 FloatRect mappedBounds = paintChunk.bounds; | 487 PropertyTreeState(effect->localTransformSpace(), |
| 411 if (geometryMapper) { | 488 effect->outputClip(), effect))) |
| 412 geometryMapper->localToAncestorRect( | 489 return false; |
| 413 paintChunk.properties.propertyTreeState.transform(), | 490 return true; |
| 414 propertyTreeState.transform(), mappedBounds); | 491 } |
| 415 } | 492 |
| 416 bounds.unite(mappedBounds); | 493 void PaintArtifactCompositor::layerizeGroup( |
| 417 if (bounds.size() != paintChunks[0]->bounds.size()) { | 494 const PaintArtifact& paintArtifact, |
| 418 if (bounds.size() != paintChunk.bounds.size()) | 495 Vector<PendingLayer>& pendingLayers, |
| 419 knownToBeOpaque = false; | 496 GeometryMapper& geometryMapper, |
| 420 else | 497 const EffectPaintPropertyNode& currentGroup, |
| 421 knownToBeOpaque = paintChunk.knownToBeOpaque; | 498 Vector<PaintChunk>::const_iterator& chunkIt) { |
| 499 size_t firstLayerInCurrentGroup = pendingLayers.size(); |
| 500 // The worst case time complexity of the algorithm is O(pqd), where |
| 501 // p = the number of paint chunks. |
| 502 // q = average number of trials to find a squash layer or rejected |
| 503 // for overlapping. |
| 504 // d = (sum of) the depth of property trees. |
| 505 // The analysis as follows: |
| 506 // Every paint chunk will be visited by the main loop below for exactly once, |
| 507 // except for chunks that enter or exit groups (case B & C below). |
| 508 // For normal chunk visit (case A), the only cost is determining squash, |
| 509 // which costs O(qd), where d came from "canUpcastTo" and geometry mapping. |
| 510 // Subtotal: O(pqd) |
| 511 // For group entering and exiting, it could cost O(d) for each group, for |
| 512 // searching the shallowest subgroup (strictChildOfAlongPath), thus O(d^2) |
| 513 // in total. |
| 514 // Also when exiting group, the group may be decomposited and squashed to a |
| 515 // previous layer. Again finding the host costs O(qd). Merging would cost O(p) |
| 516 // due to copying the chunk list. Subtotal: O((qd + p)d) = O(qd^2 + pd) |
| 517 // Assuming p > d, the total complexity would be O(pqd + qd^2 + pd) = O(pqd) |
| 518 while (chunkIt != paintArtifact.paintChunks().end()) { |
| 519 // Look at the effect node of the next chunk. There are 3 possible cases: |
| 520 // A. The next chunk belongs to the current group but no subgroup. |
| 521 // B. The next chunk does not belong to the current group. |
| 522 // C. The next chunk belongs to some subgroup of the current group. |
| 523 const EffectPaintPropertyNode* chunkEffect = |
| 524 chunkIt->properties.propertyTreeState.effect(); |
| 525 if (chunkEffect == ¤tGroup) { |
| 526 // Case A: The next chunk belongs to the current group but no subgroup. |
| 527 bool isForeign = paintArtifact.getDisplayItemList()[chunkIt->beginIndex] |
| 528 .isForeignLayer(); |
| 529 pendingLayers.push_back(PendingLayer(*chunkIt++, isForeign)); |
| 530 if (isForeign) |
| 531 continue; |
| 532 } else { |
| 533 const EffectPaintPropertyNode* subgroup = |
| 534 strictChildOfAlongPath(¤tGroup, chunkEffect); |
| 535 // Case B: This means we need to close the current group without |
| 536 // processing the next chunk. |
| 537 if (!subgroup) |
| 538 break; |
| 539 // Case C: The following chunks belong to a subgroup. Process them by |
| 540 // a recursion call. |
| 541 size_t firstLayerInSubgroup = pendingLayers.size(); |
| 542 layerizeGroup(paintArtifact, pendingLayers, geometryMapper, *subgroup, |
| 543 chunkIt); |
| 544 // Now the chunk iterator stepped over the subgroup we just saw. |
| 545 // If the subgroup generated 2 or more layers then the subgroup must be |
| 546 // composited to satisfy grouping requirement. |
| 547 // i.e. Grouping effects generally must be applied atomically, |
| 548 // for example, Opacity(A+B) != Opacity(A) + Opacity(B), thus an effect |
| 549 // either applied 100% within a layer, or not at all applied within layer |
| 550 // (i.e. applied by compositor render surface instead). |
| 551 if (pendingLayers.size() != firstLayerInSubgroup + 1) |
| 552 continue; |
| 553 // Now attempt to "decomposite" subgroup. |
| 554 PendingLayer& subgroupLayer = pendingLayers[firstLayerInSubgroup]; |
| 555 if (!canDecompositeEffect(subgroup, subgroupLayer)) |
| 556 continue; |
| 557 subgroupLayer.upcast( |
| 558 PropertyTreeState(subgroup->localTransformSpace(), |
| 559 subgroup->outputClip(), ¤tGroup), |
| 560 geometryMapper); |
| 561 } |
| 562 // At this point pendingLayers.back() is the either a layer from a |
| 563 // "decomposited" subgroup or a layer created from a chunk we just |
| 564 // processed. Now determine whether it could be merged into a previous |
| 565 // layer. |
| 566 const PendingLayer& newLayer = pendingLayers.back(); |
| 567 DCHECK(!newLayer.isForeign); |
| 568 DCHECK_EQ(¤tGroup, newLayer.propertyTreeState.effect()); |
| 569 // This iterates pendingLayers[firstLayerInCurrentGroup:-1] in reverse. |
| 570 for (size_t candidateIndex = pendingLayers.size() - 1; |
| 571 candidateIndex-- > firstLayerInCurrentGroup;) { |
| 572 PendingLayer& candidateLayer = pendingLayers[candidateIndex]; |
| 573 if (candidateLayer.canMerge(newLayer)) { |
| 574 candidateLayer.merge(newLayer, geometryMapper); |
| 575 pendingLayers.pop_back(); |
| 576 break; |
| 577 } |
| 578 if (mightOverlap(newLayer, candidateLayer, geometryMapper)) |
| 579 break; |
| 580 } |
| 422 } | 581 } |
| 423 } | 582 } |
| 424 | 583 |
| 425 void PaintArtifactCompositor::collectPendingLayers( | 584 void PaintArtifactCompositor::collectPendingLayers( |
| 426 const PaintArtifact& paintArtifact, | 585 const PaintArtifact& paintArtifact, |
| 427 Vector<PendingLayer>& pendingLayers, | 586 Vector<PendingLayer>& pendingLayers, |
| 428 GeometryMapper& geometryMapper) { | 587 GeometryMapper& geometryMapper) { |
| 429 // n = # of paint chunks. Memoizing canMergeInto() can get it to O(n^2), and | 588 Vector<PaintChunk>::const_iterator cursor = |
| 430 // other heuristics can make worst-case behavior better. | 589 paintArtifact.paintChunks().begin(); |
| 431 for (const PaintChunk& paintChunk : paintArtifact.paintChunks()) { | 590 layerizeGroup(paintArtifact, pendingLayers, geometryMapper, |
| 432 bool createNew = true; | 591 *EffectPaintPropertyNode::root(), cursor); |
| 433 for (Vector<PendingLayer>::reverse_iterator candidatePendingLayer = | 592 DCHECK_EQ(paintArtifact.paintChunks().end(), cursor); |
| 434 pendingLayers.rbegin(); | |
| 435 candidatePendingLayer != pendingLayers.rend(); | |
| 436 ++candidatePendingLayer) { | |
| 437 if (canMergeInto(paintArtifact, paintChunk, *candidatePendingLayer)) { | |
| 438 candidatePendingLayer->add(paintChunk, &geometryMapper); | |
| 439 createNew = false; | |
| 440 break; | |
| 441 } | |
| 442 if (mightOverlap(paintChunk, *candidatePendingLayer, geometryMapper)) { | |
| 443 break; | |
| 444 } | |
| 445 } | |
| 446 if (createNew) | |
| 447 pendingLayers.push_back(PendingLayer(paintChunk)); | |
| 448 } | |
| 449 } | 593 } |
| 450 | 594 |
| 451 void PaintArtifactCompositor::update( | 595 void PaintArtifactCompositor::update( |
| 452 const PaintArtifact& paintArtifact, | 596 const PaintArtifact& paintArtifact, |
| 453 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations, | 597 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations, |
| 454 bool storeDebugInfo, | 598 bool storeDebugInfo, |
| 455 GeometryMapper& geometryMapper) { | 599 GeometryMapper& geometryMapper) { |
| 456 #ifndef NDEBUG | 600 #ifndef NDEBUG |
| 457 storeDebugInfo = true; | 601 storeDebugInfo = true; |
| 458 #endif | 602 #endif |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 526 #ifndef NDEBUG | 670 #ifndef NDEBUG |
| 527 void PaintArtifactCompositor::showDebugData() { | 671 void PaintArtifactCompositor::showDebugData() { |
| 528 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo) | 672 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo) |
| 529 ->toPrettyJSONString() | 673 ->toPrettyJSONString() |
| 530 .utf8() | 674 .utf8() |
| 531 .data(); | 675 .data(); |
| 532 } | 676 } |
| 533 #endif | 677 #endif |
| 534 | 678 |
| 535 } // namespace blink | 679 } // namespace blink |
| OLD | NEW |