Chromium Code Reviews| 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 "cc/layers/content_layer_client.h" | 7 #include "cc/layers/content_layer_client.h" |
| 8 #include "cc/layers/layer.h" | 8 #include "cc/layers/layer.h" |
| 9 #include "cc/layers/picture_layer.h" | 9 #include "cc/layers/picture_layer.h" |
| 10 #include "cc/playback/compositing_display_item.h" | 10 #include "cc/playback/compositing_display_item.h" |
| (...skipping 543 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 554 ccInvalidationRect, | 554 ccInvalidationRect, |
| 555 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index] | 555 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index] |
| 556 : nullptr); | 556 : nullptr); |
| 557 } | 557 } |
| 558 } | 558 } |
| 559 | 559 |
| 560 newContentLayerClients.push_back(std::move(contentLayerClient)); | 560 newContentLayerClients.push_back(std::move(contentLayerClient)); |
| 561 return ccPictureLayer; | 561 return ccPictureLayer; |
| 562 } | 562 } |
| 563 | 563 |
| 564 bool PaintArtifactCompositor::canMergeInto( | 564 PaintArtifactCompositor::PendingLayer::PendingLayer( |
| 565 const PaintArtifact& paintArtifact, | 565 const PaintChunk& firstPaintChunk, |
| 566 const PaintChunk& newChunk, | 566 bool chunkIsForeign, |
| 567 const PendingLayer& candidatePendingLayer) { | 567 GeometryMapper& geometryMapper) |
| 568 const PaintChunk& pendingLayerFirstChunk = | 568 : bounds(firstPaintChunk.bounds), |
| 569 *candidatePendingLayer.paintChunks[0]; | 569 knownToBeOpaque(firstPaintChunk.knownToBeOpaque), |
| 570 if (paintArtifact.getDisplayItemList()[newChunk.beginIndex].isForeignLayer()) | 570 backfaceHidden(firstPaintChunk.properties.backfaceHidden), |
| 571 return false; | 571 propertyTreeState(firstPaintChunk.properties.propertyTreeState), |
| 572 | 572 isForeign(chunkIsForeign) { |
| 573 if (paintArtifact.getDisplayItemList()[pendingLayerFirstChunk.beginIndex] | 573 paintChunks.push_back(&firstPaintChunk); |
| 574 .isForeignLayer()) | 574 } |
| 575 return false; | 575 |
| 576 | 576 void PaintArtifactCompositor::PendingLayer::merge( |
| 577 if (newChunk.properties.backfaceHidden != | 577 const PendingLayer& guest, |
| 578 pendingLayerFirstChunk.properties.backfaceHidden) | |
| 579 return false; | |
| 580 | |
| 581 DCHECK_GE(candidatePendingLayer.paintChunks.size(), 1u); | |
| 582 PropertyTreeStateIterator iterator(newChunk.properties.propertyTreeState); | |
| 583 for (const PropertyTreeState* currentState = | |
| 584 &newChunk.properties.propertyTreeState; | |
| 585 currentState; currentState = iterator.next()) { | |
| 586 if (*currentState == candidatePendingLayer.propertyTreeState) | |
| 587 return true; | |
| 588 if (currentState->hasDirectCompositingReasons()) | |
| 589 return false; | |
| 590 } | |
| 591 return false; | |
| 592 } | |
| 593 | |
| 594 bool PaintArtifactCompositor::mightOverlap( | |
| 595 const PaintChunk& paintChunk, | |
| 596 const PendingLayer& candidatePendingLayer, | |
| 597 GeometryMapper& geometryMapper) { | 578 GeometryMapper& geometryMapper) { |
| 579 DCHECK(!isForeign && !guest.isForeign); | |
| 580 DCHECK_EQ(backfaceHidden, guest.backfaceHidden); | |
| 581 | |
| 582 paintChunks.appendVector(guest.paintChunks); | |
| 583 FloatRect oldBounds = bounds; | |
| 584 bounds.unite(geometryMapper | |
| 585 .localToAncestorVisualRect( | |
| 586 guest.bounds, guest.propertyTreeState, propertyTreeState) | |
| 587 .rect()); | |
| 588 if (bounds != oldBounds) | |
| 589 knownToBeOpaque = false; | |
| 590 } | |
| 591 | |
| 592 void PaintArtifactCompositor::PendingLayer::upcast( | |
| 593 const PropertyTreeState& newState, | |
| 594 GeometryMapper& geometryMapper) { | |
| 595 bounds = geometryMapper | |
| 596 .localToAncestorVisualRect(bounds, propertyTreeState, newState) | |
| 597 .rect(); | |
| 598 propertyTreeState = newState; | |
| 599 // TODO(trchen): Upgrade GeometryMapper. | |
| 600 // knownToBeOpaque = knownToBeOpaque && enclosingRect(mappedRegion) == | |
| 601 // enclosedRect(mappedRegion); | |
| 602 // localToAncestorVisualRect returns the enclosingRect of the mapped region. | |
| 603 // A variant that returns the enclosedRect is needed. | |
| 604 knownToBeOpaque = false; | |
| 605 } | |
| 606 | |
| 607 #if 0 | |
| 608 static unsigned nodeDepth(const TransformPaintPropertyNode* node) { | |
| 609 unsigned depth = 0; | |
| 610 while (node) { | |
| 611 depth++; | |
| 612 node = node->parent(); | |
| 613 } | |
| 614 return depth; | |
| 615 } | |
| 616 | |
| 617 // Return true if there are no direct compositing reason on the path between two transform nodes. | |
| 618 static bool transformsAreCompatible(const TransformPaintPropertyNode* nodeA, con st TransformPaintPropertyNode* nodeB) { | |
| 619 // Measure both depths. | |
| 620 unsigned depthA = nodeDepth(nodeA); | |
| 621 unsigned depthB = nodeDepth(nodeB); | |
| 622 | |
| 623 // Make it so depthA >= depthB. | |
| 624 if (depthA < depthB) { | |
| 625 std::swap(nodeA, nodeB); | |
| 626 std::swap(depthA, depthB); | |
| 627 } | |
| 628 | |
| 629 // Make it so depthA == depthB. | |
| 630 while (depthA > depthB) { | |
| 631 if (nodeA->hasDirectCompositingReasons()) | |
| 632 return false; | |
| 633 nodeA = nodeA->parent(); | |
| 634 depthA--; | |
| 635 } | |
| 636 | |
| 637 // Walk up until we find the ancestor. | |
| 638 while (nodeA != nodeB) { | |
| 639 if (nodeA->hasDirectCompositingReasons() || nodeB->hasDirectCompositingReaso ns()) | |
| 640 return false; | |
| 641 nodeA = nodeA->parent(); | |
| 642 nodeB = nodeB->parent(); | |
| 643 } | |
| 644 return true; | |
| 645 } | |
| 646 #else | |
| 647 static bool transformsAreCompatible(const TransformPaintPropertyNode* nodeA, | |
| 648 const TransformPaintPropertyNode* nodeB) { | |
| 649 for (; nodeB != nodeA; nodeB = nodeB->parent()) { | |
| 650 if (!nodeB || nodeB->hasDirectCompositingReasons()) | |
| 651 return false; | |
| 652 } | |
| 653 return true; | |
| 654 } | |
| 655 #endif | |
| 656 | |
| 657 static bool canUpcastTo(const PropertyTreeState& guest, | |
| 658 const PropertyTreeState& home) { | |
| 659 DCHECK_EQ(home.effect(), guest.effect()); | |
| 660 | |
| 661 for (const ClipPaintPropertyNode* currentClip = guest.clip(); | |
| 662 currentClip != home.clip(); currentClip = currentClip->parent()) { | |
| 663 if (currentClip->hasDirectCompositingReasons()) | |
| 664 return false; | |
| 665 if (!transformsAreCompatible(home.transform(), | |
| 666 currentClip->localTransformSpace())) | |
| 667 return false; | |
| 668 } | |
| 669 | |
| 670 return transformsAreCompatible(home.transform(), guest.transform()); | |
| 671 } | |
| 672 | |
| 673 // Returns nullptr if 'ancestor' is not a strict ancestor of 'node'. | |
| 674 // Otherwise, return the child of 'ancestor' that is an ancestor of 'node' or | |
| 675 // 'node' itself. | |
| 676 static const EffectPaintPropertyNode* strictAncestorOf( | |
| 677 const EffectPaintPropertyNode* ancestor, | |
| 678 const EffectPaintPropertyNode* node) { | |
| 679 for (; node; node = node->parent()) { | |
| 680 if (node->parent() == ancestor) | |
| 681 return node; | |
| 682 } | |
| 683 return nullptr; | |
| 684 } | |
| 685 | |
| 686 static bool mightOverlap(const PropertyTreeState& stateA, | |
| 687 const FloatRect& boundsA, | |
| 688 const PropertyTreeState& stateB, | |
| 689 const FloatRect& boundsB, | |
| 690 GeometryMapper& geometryMapper) { | |
| 598 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(), | 691 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(), |
| 599 ClipPaintPropertyNode::root(), | 692 ClipPaintPropertyNode::root(), |
| 600 EffectPaintPropertyNode::root()); | 693 EffectPaintPropertyNode::root()); |
| 601 | 694 |
| 602 FloatRect paintChunkScreenVisualRect = | 695 FloatRect screenVisualRectA = |
| 603 geometryMapper | 696 geometryMapper |
| 604 .localToAncestorVisualRect(paintChunk.bounds, | 697 .localToAncestorVisualRect(boundsA, stateA, rootPropertyTreeState) |
| 605 paintChunk.properties.propertyTreeState, | |
| 606 rootPropertyTreeState) | |
| 607 .rect(); | 698 .rect(); |
| 608 | 699 |
| 609 FloatRect pendingLayerScreenVisualRect = | 700 FloatRect screenVisualRectB = |
| 610 geometryMapper | 701 geometryMapper |
| 611 .localToAncestorVisualRect(candidatePendingLayer.bounds, | 702 .localToAncestorVisualRect(boundsB, stateB, rootPropertyTreeState) |
| 612 candidatePendingLayer.propertyTreeState, | |
| 613 rootPropertyTreeState) | |
| 614 .rect(); | 703 .rect(); |
| 615 | 704 |
| 616 return paintChunkScreenVisualRect.intersects(pendingLayerScreenVisualRect); | 705 return screenVisualRectA.intersects(screenVisualRectB); |
| 617 } | 706 } |
| 618 | 707 |
| 619 PaintArtifactCompositor::PendingLayer::PendingLayer( | 708 void PaintArtifactCompositor::layerizeGroup( |
| 620 const PaintChunk& firstPaintChunk) | 709 const PaintArtifact& paintArtifact, |
| 621 : bounds(firstPaintChunk.bounds), | 710 Vector<PendingLayer>& pendingLayers, |
| 622 knownToBeOpaque(firstPaintChunk.knownToBeOpaque), | 711 GeometryMapper& geometryMapper, |
| 623 backfaceHidden(firstPaintChunk.properties.backfaceHidden), | 712 const EffectPaintPropertyNode& currentGroup, |
| 624 propertyTreeState(firstPaintChunk.properties.propertyTreeState) { | 713 Vector<PaintChunk>::const_iterator& chunkIt) { |
| 625 paintChunks.push_back(&firstPaintChunk); | 714 size_t firstLayerInCurrentGroup = pendingLayers.size(); |
| 626 } | 715 while (chunkIt != paintArtifact.paintChunks().end()) { |
| 627 | 716 // Look at the effect node of the next chunk. There are 3 possible cases: |
|
pdr.
2017/02/23 06:05:18
Do you have an intuition about the frequency of ea
trchen
2017/02/23 22:19:45
Case A and B always happen in pair, and should be
| |
| 628 void PaintArtifactCompositor::PendingLayer::add( | 717 // A. The next chunk does not belong to the current group. |
| 629 const PaintChunk& paintChunk, | 718 // B. The next chunk belongs to some subgroup of the current group. |
| 630 GeometryMapper* geometryMapper) { | 719 // C. The next chunk belongs to the current group but no subgroup. |
| 631 DCHECK(paintChunk.properties.backfaceHidden == backfaceHidden); | 720 const EffectPaintPropertyNode* chunkEffect = |
| 632 paintChunks.push_back(&paintChunk); | 721 chunkIt->properties.propertyTreeState.effect(); |
| 633 FloatRect mappedBounds = paintChunk.bounds; | 722 if (chunkEffect != ¤tGroup) { |
| 634 if (geometryMapper) { | 723 const EffectPaintPropertyNode* subgroup = |
| 635 mappedBounds = geometryMapper->localToAncestorRect( | 724 strictAncestorOf(¤tGroup, chunkEffect); |
| 636 mappedBounds, paintChunk.properties.propertyTreeState.transform(), | 725 // Case A: This means we need to close the current group without |
| 637 propertyTreeState.transform()); | 726 // processing the next chunk. |
| 638 } | 727 if (!subgroup) |
| 639 bounds.unite(mappedBounds); | 728 break; |
| 640 if (bounds.size() != paintChunks[0]->bounds.size()) { | 729 // Case B: The following chunks belong to a subgroup. Process them by |
| 641 if (bounds.size() != paintChunk.bounds.size()) | 730 // a recursion call. |
| 642 knownToBeOpaque = false; | 731 size_t firstLayerInSubgroup = pendingLayers.size(); |
| 643 else | 732 layerizeGroup(paintArtifact, pendingLayers, geometryMapper, *subgroup, |
| 644 knownToBeOpaque = paintChunk.knownToBeOpaque; | 733 chunkIt); |
| 734 // Now the chunk iterator stepped over the subgroup we just saw. | |
| 735 // If the subgroup generated 2 or more layers then the grouping must be | |
| 736 // composited as well. | |
| 737 if (pendingLayers.size() != firstLayerInSubgroup + 1) | |
| 738 continue; | |
| 739 // Attempt to "decomposite" subgroup. Only decomposite one level deep | |
| 740 // because the recusion should have decomposited the sub-subgroups. | |
| 741 // If we see a deep subgroup here, that implies some intermediate | |
| 742 // subgroup must be composited. | |
| 743 PendingLayer& subgroupLayer = pendingLayers[firstLayerInSubgroup]; | |
| 744 if (subgroupLayer.propertyTreeState.effect() != subgroup) | |
| 745 continue; | |
| 746 // TODO(trchen): Exotic blending layer may be decomposited only if it | |
| 747 // could be merged into the first layer of the current group. | |
| 748 if (subgroup->blendMode() != SkBlendMode::kSrcOver) | |
| 749 continue; | |
| 750 if (subgroupLayer.isForeign) | |
| 751 continue; | |
| 752 if (subgroup->hasDirectCompositingReasons() || | |
| 753 !canUpcastTo(subgroupLayer.propertyTreeState, | |
|
pdr.
2017/02/23 06:05:18
Can you add a note about the performance of this?
trchen
2017/02/23 22:19:45
Acknowledged.
| |
| 754 PropertyTreeState(subgroup->localTransformSpace(), | |
| 755 subgroup->outputClip(), subgroup))) | |
| 756 continue; | |
| 757 subgroupLayer.upcast( | |
| 758 PropertyTreeState(subgroup->localTransformSpace(), | |
| 759 subgroup->outputClip(), ¤tGroup), | |
| 760 geometryMapper); | |
| 761 } else { | |
| 762 // Case C: The next chunk belongs to the current group but no subgroup. | |
| 763 bool isForeign = paintArtifact.getDisplayItemList()[chunkIt->beginIndex] | |
| 764 .isForeignLayer(); | |
| 765 pendingLayers.push_back( | |
| 766 PendingLayer(*chunkIt++, isForeign, geometryMapper)); | |
| 767 if (isForeign) | |
| 768 continue; | |
| 769 } | |
| 770 // At this point pendingLayers.back() is the either a layer from a | |
| 771 // "decomposited" subgroup or a layer created from a chunk we just | |
| 772 // processed. Now determine whether it could be merged into a previous | |
| 773 // layer. | |
| 774 const PendingLayer& newLayer = pendingLayers.back(); | |
| 775 DCHECK(!newLayer.isForeign); | |
| 776 DCHECK_EQ(¤tGroup, newLayer.propertyTreeState.effect()); | |
| 777 // This iterates pendingLayers[firstLayerInCurrentGroup:-1] in reverse. | |
| 778 for (size_t candidateIndex = pendingLayers.size() - 1; | |
| 779 candidateIndex-- > firstLayerInCurrentGroup;) { | |
| 780 PendingLayer& candidateLayer = pendingLayers[candidateIndex]; | |
| 781 if (!candidateLayer.isForeign && | |
| 782 newLayer.backfaceHidden == candidateLayer.backfaceHidden && | |
| 783 candidateLayer.propertyTreeState.effect() == ¤tGroup && | |
| 784 canUpcastTo(newLayer.propertyTreeState, | |
| 785 candidateLayer.propertyTreeState)) { | |
| 786 candidateLayer.merge(newLayer, geometryMapper); | |
| 787 pendingLayers.pop_back(); | |
| 788 break; | |
| 789 } | |
| 790 if (mightOverlap(newLayer.propertyTreeState, newLayer.bounds, | |
| 791 candidateLayer.propertyTreeState, candidateLayer.bounds, | |
| 792 geometryMapper)) | |
| 793 break; | |
| 794 } | |
| 645 } | 795 } |
| 646 } | 796 } |
| 647 | 797 |
| 648 void PaintArtifactCompositor::collectPendingLayers( | 798 void PaintArtifactCompositor::collectPendingLayers( |
| 649 const PaintArtifact& paintArtifact, | 799 const PaintArtifact& paintArtifact, |
| 650 Vector<PendingLayer>& pendingLayers, | 800 Vector<PendingLayer>& pendingLayers, |
| 651 GeometryMapper& geometryMapper) { | 801 GeometryMapper& geometryMapper) { |
| 652 // n = # of paint chunks. Memoizing canMergeInto() can get it to O(n^2), and | 802 Vector<PaintChunk>::const_iterator cursor = |
| 653 // other heuristics can make worst-case behavior better. | 803 paintArtifact.paintChunks().begin(); |
| 654 for (const PaintChunk& paintChunk : paintArtifact.paintChunks()) { | 804 layerizeGroup(paintArtifact, pendingLayers, geometryMapper, |
| 655 bool createNew = true; | 805 *EffectPaintPropertyNode::root(), cursor); |
| 656 for (Vector<PendingLayer>::reverse_iterator candidatePendingLayer = | 806 DCHECK_EQ(paintArtifact.paintChunks().end(), cursor); |
| 657 pendingLayers.rbegin(); | |
| 658 candidatePendingLayer != pendingLayers.rend(); | |
| 659 ++candidatePendingLayer) { | |
| 660 if (canMergeInto(paintArtifact, paintChunk, *candidatePendingLayer)) { | |
| 661 candidatePendingLayer->add(paintChunk, &geometryMapper); | |
| 662 createNew = false; | |
| 663 break; | |
| 664 } | |
| 665 if (mightOverlap(paintChunk, *candidatePendingLayer, geometryMapper)) { | |
| 666 break; | |
| 667 } | |
| 668 } | |
| 669 if (createNew) | |
| 670 pendingLayers.push_back(PendingLayer(paintChunk)); | |
| 671 } | |
| 672 } | 807 } |
| 673 | 808 |
| 674 void PaintArtifactCompositor::update( | 809 void PaintArtifactCompositor::update( |
| 675 const PaintArtifact& paintArtifact, | 810 const PaintArtifact& paintArtifact, |
| 676 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations, | 811 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations, |
| 677 bool storeDebugInfo, | 812 bool storeDebugInfo, |
| 678 GeometryMapper& geometryMapper) { | 813 GeometryMapper& geometryMapper) { |
| 679 #ifndef NDEBUG | 814 #ifndef NDEBUG |
| 680 storeDebugInfo = true; | 815 storeDebugInfo = true; |
| 681 #endif | 816 #endif |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 747 #ifndef NDEBUG | 882 #ifndef NDEBUG |
| 748 void PaintArtifactCompositor::showDebugData() { | 883 void PaintArtifactCompositor::showDebugData() { |
| 749 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo) | 884 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo) |
| 750 ->toPrettyJSONString() | 885 ->toPrettyJSONString() |
| 751 .utf8() | 886 .utf8() |
| 752 .data(); | 887 .data(); |
| 753 } | 888 } |
| 754 #endif | 889 #endif |
| 755 | 890 |
| 756 } // namespace blink | 891 } // namespace blink |
| OLD | NEW |