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 <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 553 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 564 ccInvalidationRect, | 564 ccInvalidationRect, |
| 565 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index] | 565 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index] |
| 566 : nullptr); | 566 : nullptr); |
| 567 } | 567 } |
| 568 } | 568 } |
| 569 | 569 |
| 570 newContentLayerClients.push_back(std::move(contentLayerClient)); | 570 newContentLayerClients.push_back(std::move(contentLayerClient)); |
| 571 return ccPictureLayer; | 571 return ccPictureLayer; |
| 572 } | 572 } |
| 573 | 573 |
| 574 bool PaintArtifactCompositor::canMergeInto( | 574 PaintArtifactCompositor::PendingLayer::PendingLayer( |
| 575 const PaintArtifact& paintArtifact, | 575 const PaintChunk& firstPaintChunk, |
| 576 const PaintChunk& newChunk, | 576 bool chunkIsForeign, |
| 577 const PendingLayer& candidatePendingLayer) { | 577 GeometryMapper& geometryMapper) |
| 578 const PaintChunk& pendingLayerFirstChunk = | 578 : bounds(firstPaintChunk.bounds), |
| 579 *candidatePendingLayer.paintChunks[0]; | 579 knownToBeOpaque(firstPaintChunk.knownToBeOpaque), |
| 580 if (paintArtifact.getDisplayItemList()[newChunk.beginIndex].isForeignLayer()) | 580 backfaceHidden(firstPaintChunk.properties.backfaceHidden), |
| 581 propertyTreeState(firstPaintChunk.properties.propertyTreeState), | |
| 582 isForeign(chunkIsForeign) { | |
| 583 paintChunks.push_back(&firstPaintChunk); | |
| 584 } | |
| 585 | |
| 586 void PaintArtifactCompositor::PendingLayer::merge( | |
| 587 const PendingLayer& guest, | |
| 588 GeometryMapper& geometryMapper) { | |
| 589 DCHECK(!isForeign && !guest.isForeign); | |
| 590 DCHECK_EQ(backfaceHidden, guest.backfaceHidden); | |
| 591 | |
| 592 paintChunks.appendVector(guest.paintChunks); | |
| 593 FloatRect oldBounds = bounds; | |
| 594 bounds.unite(geometryMapper | |
| 595 .localToAncestorVisualRect( | |
| 596 guest.bounds, guest.propertyTreeState, propertyTreeState) | |
| 597 .rect()); | |
| 598 if (bounds != oldBounds) | |
| 599 knownToBeOpaque = false; | |
| 600 } | |
| 601 | |
| 602 static bool canUpcastTo(const PropertyTreeState& guest, | |
| 603 const PropertyTreeState& home); | |
| 604 bool PaintArtifactCompositor::PendingLayer::canMerge( | |
| 605 const PendingLayer& guest) const { | |
| 606 if (isForeign || guest.isForeign) | |
| 581 return false; | 607 return false; |
| 582 | 608 if (backfaceHidden != guest.backfaceHidden) |
| 583 if (paintArtifact.getDisplayItemList()[pendingLayerFirstChunk.beginIndex] | |
| 584 .isForeignLayer()) | |
| 585 return false; | 609 return false; |
| 586 | 610 if (propertyTreeState.effect() != guest.propertyTreeState.effect()) |
| 587 if (newChunk.properties.backfaceHidden != | |
| 588 pendingLayerFirstChunk.properties.backfaceHidden) | |
| 589 return false; | 611 return false; |
| 590 | 612 return canUpcastTo(guest.propertyTreeState, propertyTreeState); |
| 591 DCHECK_GE(candidatePendingLayer.paintChunks.size(), 1u); | 613 } |
| 592 PropertyTreeStateIterator iterator(newChunk.properties.propertyTreeState); | 614 |
| 593 for (const PropertyTreeState* currentState = | 615 void PaintArtifactCompositor::PendingLayer::upcast( |
| 594 &newChunk.properties.propertyTreeState; | 616 const PropertyTreeState& newState, |
| 595 currentState; currentState = iterator.next()) { | 617 GeometryMapper& geometryMapper) { |
| 596 if (*currentState == candidatePendingLayer.propertyTreeState) | 618 bounds = geometryMapper |
| 597 return true; | 619 .localToAncestorVisualRect(bounds, propertyTreeState, newState) |
| 598 if (currentState->hasDirectCompositingReasons()) | 620 .rect(); |
| 621 propertyTreeState = newState; | |
| 622 // TODO(trchen): Upgrade GeometryMapper. | |
| 623 // A local visual rect mapped to an ancestor space may become a polygon | |
| 624 // (e.g. consider transformed clip), also effects may affect the opaque | |
| 625 // region. To determine whether the layer is still opaque, we need to | |
| 626 // query conservative opaque rect after mapping to an ancestor space, | |
| 627 // which is not supported by GeometryMapper yet. | |
| 628 knownToBeOpaque = false; | |
| 629 } | |
| 630 | |
| 631 static bool isNonCompositingAncestorOf( | |
| 632 const TransformPaintPropertyNode* ancestor, | |
| 633 const TransformPaintPropertyNode* node) { | |
| 634 for (; node != ancestor; node = node->parent()) { | |
| 635 if (!node || node->hasDirectCompositingReasons()) | |
| 599 return false; | 636 return false; |
| 600 } | 637 } |
| 601 return false; | 638 return true; |
| 602 } | 639 } |
| 603 | 640 |
| 604 bool PaintArtifactCompositor::mightOverlap( | 641 static bool canUpcastTo(const PropertyTreeState& guest, |
|
chrishtr
2017/03/09 01:27:00
Document this method.
trchen
2017/03/15 22:46:52
Done.
| |
| 605 const PaintChunk& paintChunk, | 642 const PropertyTreeState& home) { |
| 606 const PendingLayer& candidatePendingLayer, | 643 DCHECK_EQ(home.effect(), guest.effect()); |
| 607 GeometryMapper& geometryMapper) { | 644 |
| 645 for (const ClipPaintPropertyNode* currentClip = guest.clip(); | |
| 646 currentClip != home.clip(); currentClip = currentClip->parent()) { | |
| 647 if (!currentClip || currentClip->hasDirectCompositingReasons()) | |
| 648 return false; | |
| 649 if (!isNonCompositingAncestorOf(home.transform(), | |
| 650 currentClip->localTransformSpace())) | |
| 651 return false; | |
| 652 } | |
| 653 | |
| 654 return isNonCompositingAncestorOf(home.transform(), guest.transform()); | |
| 655 } | |
| 656 | |
| 657 // Returns nullptr if 'ancestor' is not a strict ancestor of 'node'. | |
| 658 // Otherwise, return the child of 'ancestor' that is an ancestor of 'node' or | |
| 659 // 'node' itself. | |
| 660 static const EffectPaintPropertyNode* strictAncestorOf( | |
|
chrishtr
2017/03/09 01:27:00
strictChildOfAlongPath ?
trchen
2017/03/15 22:46:52
Done.
| |
| 661 const EffectPaintPropertyNode* ancestor, | |
| 662 const EffectPaintPropertyNode* node) { | |
| 663 for (; node; node = node->parent()) { | |
| 664 if (node->parent() == ancestor) | |
| 665 return node; | |
| 666 } | |
| 667 return nullptr; | |
| 668 } | |
| 669 | |
| 670 static bool mightOverlap(const PropertyTreeState& stateA, | |
| 671 const FloatRect& boundsA, | |
| 672 const PropertyTreeState& stateB, | |
| 673 const FloatRect& boundsB, | |
| 674 GeometryMapper& geometryMapper) { | |
| 608 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(), | 675 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(), |
| 609 ClipPaintPropertyNode::root(), | 676 ClipPaintPropertyNode::root(), |
| 610 EffectPaintPropertyNode::root()); | 677 EffectPaintPropertyNode::root()); |
| 611 | 678 |
| 612 FloatRect paintChunkScreenVisualRect = | 679 FloatRect screenVisualRectA = |
| 613 geometryMapper | 680 geometryMapper |
| 614 .localToAncestorVisualRect(paintChunk.bounds, | 681 .localToAncestorVisualRect(boundsA, stateA, rootPropertyTreeState) |
| 615 paintChunk.properties.propertyTreeState, | |
| 616 rootPropertyTreeState) | |
| 617 .rect(); | 682 .rect(); |
| 618 | 683 |
| 619 FloatRect pendingLayerScreenVisualRect = | 684 FloatRect screenVisualRectB = |
| 620 geometryMapper | 685 geometryMapper |
| 621 .localToAncestorVisualRect(candidatePendingLayer.bounds, | 686 .localToAncestorVisualRect(boundsB, stateB, rootPropertyTreeState) |
| 622 candidatePendingLayer.propertyTreeState, | |
| 623 rootPropertyTreeState) | |
| 624 .rect(); | 687 .rect(); |
| 625 | 688 |
| 626 return paintChunkScreenVisualRect.intersects(pendingLayerScreenVisualRect); | 689 return screenVisualRectA.intersects(screenVisualRectB); |
| 627 } | 690 } |
| 628 | 691 |
| 629 PaintArtifactCompositor::PendingLayer::PendingLayer( | 692 void PaintArtifactCompositor::layerizeGroup( |
| 630 const PaintChunk& firstPaintChunk) | 693 const PaintArtifact& paintArtifact, |
| 631 : bounds(firstPaintChunk.bounds), | 694 Vector<PendingLayer>& pendingLayers, |
| 632 knownToBeOpaque(firstPaintChunk.knownToBeOpaque), | 695 GeometryMapper& geometryMapper, |
| 633 backfaceHidden(firstPaintChunk.properties.backfaceHidden), | 696 const EffectPaintPropertyNode& currentGroup, |
| 634 propertyTreeState(firstPaintChunk.properties.propertyTreeState) { | 697 Vector<PaintChunk>::const_iterator& chunkIt) { |
| 635 paintChunks.push_back(&firstPaintChunk); | 698 size_t firstLayerInCurrentGroup = pendingLayers.size(); |
| 636 } | 699 // The worst case time complexity of the algorithm is O(pqd), where |
| 637 | 700 // p = the number of paint chunks. |
| 638 void PaintArtifactCompositor::PendingLayer::add( | 701 // q = average number of trials to find a squash layer or rejected |
| 639 const PaintChunk& paintChunk, | 702 // for overlapping. |
| 640 GeometryMapper* geometryMapper) { | 703 // d = (sum of) the depth of property trees. |
| 641 DCHECK(paintChunk.properties.backfaceHidden == backfaceHidden); | 704 // The analysis as follows: |
| 642 paintChunks.push_back(&paintChunk); | 705 // Every paint chunk will be visited by the main loop below for exactly once, |
| 643 FloatRect mappedBounds = paintChunk.bounds; | 706 // except for chunks that enter or exit groups (case B & C below). |
| 644 if (geometryMapper) { | 707 // For normal chunk visit (case A), the only cost is determining squash, |
| 645 mappedBounds = geometryMapper->localToAncestorRect( | 708 // which costs O(qd), where d came from "canUpcastTo" and geometry mapping. |
| 646 mappedBounds, paintChunk.properties.propertyTreeState.transform(), | 709 // Subtotal: O(pqd) |
| 647 propertyTreeState.transform()); | 710 // For group entering and exiting, it could cost O(d) for each group, for |
| 648 } | 711 // searching the shallowest subgroup (strictAncestorOf), thus O(d^2) in total. |
| 649 bounds.unite(mappedBounds); | 712 // Also when exiting group, the group may be decomposited and squashed to a |
| 650 if (bounds.size() != paintChunks[0]->bounds.size()) { | 713 // previous layer. Again finding the host costs O(qd). Merging would cost O(p) |
| 651 if (bounds.size() != paintChunk.bounds.size()) | 714 // due to copying the chunk list. Subtotal: O((qd + p)d) = O(qd^2 + pd) |
| 652 knownToBeOpaque = false; | 715 // Assuming p > d, the total complexity would be O(pqd + qd^2 + pd) = O(pqd) |
| 653 else | 716 while (chunkIt != paintArtifact.paintChunks().end()) { |
| 654 knownToBeOpaque = paintChunk.knownToBeOpaque; | 717 // Look at the effect node of the next chunk. There are 3 possible cases: |
| 718 // A. The next chunk belongs to the current group but no subgroup. | |
| 719 // B. The next chunk does not belong to the current group. | |
| 720 // C. The next chunk belongs to some subgroup of the current group. | |
| 721 const EffectPaintPropertyNode* chunkEffect = | |
| 722 chunkIt->properties.propertyTreeState.effect(); | |
| 723 if (chunkEffect == ¤tGroup) { | |
| 724 // Case A: The next chunk belongs to the current group but no subgroup. | |
| 725 bool isForeign = paintArtifact.getDisplayItemList()[chunkIt->beginIndex] | |
| 726 .isForeignLayer(); | |
| 727 pendingLayers.push_back( | |
| 728 PendingLayer(*chunkIt++, isForeign, geometryMapper)); | |
| 729 if (isForeign) | |
| 730 continue; | |
| 731 } else { | |
| 732 const EffectPaintPropertyNode* subgroup = | |
| 733 strictAncestorOf(¤tGroup, chunkEffect); | |
| 734 // Case B: This means we need to close the current group without | |
| 735 // processing the next chunk. | |
| 736 if (!subgroup) | |
| 737 break; | |
| 738 // Case C: The following chunks belong to a subgroup. Process them by | |
| 739 // a recursion call. | |
| 740 size_t firstLayerInSubgroup = pendingLayers.size(); | |
| 741 layerizeGroup(paintArtifact, pendingLayers, geometryMapper, *subgroup, | |
| 742 chunkIt); | |
| 743 // Now the chunk iterator stepped over the subgroup we just saw. | |
|
chrishtr
2017/03/09 01:27:00
What about a 3d composited child?
trchen
2017/03/15 22:46:52
Hmmm that's a good point. 3D transformed children
| |
| 744 // If the subgroup generated 2 or more layers then the subgroup must be | |
| 745 // composited to satisfy grouping requirement. | |
| 746 // i.e. Grouping effects generally must be applied atomically, | |
| 747 // for example, Opacity(A+B) != Opacity(A) + Opacity(B), thus an effect | |
| 748 // either applied 100% within a layer, or not at all applied within layer | |
| 749 // (i.e. applied by compositor render surface instead). | |
| 750 if (pendingLayers.size() != firstLayerInSubgroup + 1) | |
|
chrishtr
2017/03/09 01:27:00
Please factor these conditions into a helper metho
trchen
2017/03/15 22:46:52
Done.
| |
| 751 continue; | |
| 752 // Attempt to "decomposite" subgroup. Only decomposite one level deep | |
| 753 // because the recusion should have decomposited the sub-subgroups. | |
| 754 // If we see a deep subgroup here, that implies some intermediate | |
| 755 // subgroup must be composited. | |
| 756 PendingLayer& subgroupLayer = pendingLayers[firstLayerInSubgroup]; | |
| 757 if (subgroupLayer.propertyTreeState.effect() != subgroup) | |
| 758 continue; | |
| 759 if (subgroupLayer.isForeign) | |
| 760 continue; | |
| 761 // TODO(trchen): Exotic blending layer may be decomposited only if it | |
| 762 // could be merged into the first layer of the current group. | |
| 763 if (subgroup->blendMode() != SkBlendMode::kSrcOver) | |
| 764 continue; | |
| 765 if (subgroup->hasDirectCompositingReasons() || | |
| 766 !canUpcastTo(subgroupLayer.propertyTreeState, | |
| 767 PropertyTreeState(subgroup->localTransformSpace(), | |
| 768 subgroup->outputClip(), subgroup))) | |
| 769 continue; | |
| 770 subgroupLayer.upcast( | |
| 771 PropertyTreeState(subgroup->localTransformSpace(), | |
| 772 subgroup->outputClip(), ¤tGroup), | |
| 773 geometryMapper); | |
| 774 } | |
| 775 // At this point pendingLayers.back() is the either a layer from a | |
| 776 // "decomposited" subgroup or a layer created from a chunk we just | |
| 777 // processed. Now determine whether it could be merged into a previous | |
| 778 // layer. | |
| 779 const PendingLayer& newLayer = pendingLayers.back(); | |
| 780 DCHECK(!newLayer.isForeign); | |
| 781 DCHECK_EQ(¤tGroup, newLayer.propertyTreeState.effect()); | |
| 782 // This iterates pendingLayers[firstLayerInCurrentGroup:-1] in reverse. | |
| 783 for (size_t candidateIndex = pendingLayers.size() - 1; | |
| 784 candidateIndex-- > firstLayerInCurrentGroup;) { | |
| 785 PendingLayer& candidateLayer = pendingLayers[candidateIndex]; | |
| 786 if (candidateLayer.canMerge(newLayer)) { | |
| 787 candidateLayer.merge(newLayer, geometryMapper); | |
| 788 pendingLayers.pop_back(); | |
| 789 break; | |
| 790 } | |
| 791 if (mightOverlap(newLayer.propertyTreeState, newLayer.bounds, | |
| 792 candidateLayer.propertyTreeState, candidateLayer.bounds, | |
| 793 geometryMapper)) | |
| 794 break; | |
| 795 } | |
| 655 } | 796 } |
| 656 } | 797 } |
| 657 | 798 |
| 658 void PaintArtifactCompositor::collectPendingLayers( | 799 void PaintArtifactCompositor::collectPendingLayers( |
| 659 const PaintArtifact& paintArtifact, | 800 const PaintArtifact& paintArtifact, |
| 660 Vector<PendingLayer>& pendingLayers, | 801 Vector<PendingLayer>& pendingLayers, |
| 661 GeometryMapper& geometryMapper) { | 802 GeometryMapper& geometryMapper) { |
| 662 // n = # of paint chunks. Memoizing canMergeInto() can get it to O(n^2), and | 803 Vector<PaintChunk>::const_iterator cursor = |
| 663 // other heuristics can make worst-case behavior better. | 804 paintArtifact.paintChunks().begin(); |
| 664 for (const PaintChunk& paintChunk : paintArtifact.paintChunks()) { | 805 layerizeGroup(paintArtifact, pendingLayers, geometryMapper, |
| 665 bool createNew = true; | 806 *EffectPaintPropertyNode::root(), cursor); |
| 666 for (Vector<PendingLayer>::reverse_iterator candidatePendingLayer = | 807 DCHECK_EQ(paintArtifact.paintChunks().end(), cursor); |
| 667 pendingLayers.rbegin(); | |
| 668 candidatePendingLayer != pendingLayers.rend(); | |
| 669 ++candidatePendingLayer) { | |
| 670 if (canMergeInto(paintArtifact, paintChunk, *candidatePendingLayer)) { | |
| 671 candidatePendingLayer->add(paintChunk, &geometryMapper); | |
| 672 createNew = false; | |
| 673 break; | |
| 674 } | |
| 675 if (mightOverlap(paintChunk, *candidatePendingLayer, geometryMapper)) { | |
| 676 break; | |
| 677 } | |
| 678 } | |
| 679 if (createNew) | |
| 680 pendingLayers.push_back(PendingLayer(paintChunk)); | |
| 681 } | |
| 682 } | 808 } |
| 683 | 809 |
| 684 void PaintArtifactCompositor::update( | 810 void PaintArtifactCompositor::update( |
| 685 const PaintArtifact& paintArtifact, | 811 const PaintArtifact& paintArtifact, |
| 686 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations, | 812 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations, |
| 687 bool storeDebugInfo, | 813 bool storeDebugInfo, |
| 688 GeometryMapper& geometryMapper) { | 814 GeometryMapper& geometryMapper) { |
| 689 #ifndef NDEBUG | 815 #ifndef NDEBUG |
| 690 storeDebugInfo = true; | 816 storeDebugInfo = true; |
| 691 #endif | 817 #endif |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 759 #ifndef NDEBUG | 885 #ifndef NDEBUG |
| 760 void PaintArtifactCompositor::showDebugData() { | 886 void PaintArtifactCompositor::showDebugData() { |
| 761 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo) | 887 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo) |
| 762 ->toPrettyJSONString() | 888 ->toPrettyJSONString() |
| 763 .utf8() | 889 .utf8() |
| 764 .data(); | 890 .data(); |
| 765 } | 891 } |
| 766 #endif | 892 #endif |
| 767 | 893 |
| 768 } // namespace blink | 894 } // namespace blink |
| OLD | NEW |