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 546 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 557 ccInvalidationRect, | 557 ccInvalidationRect, |
| 558 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index] | 558 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index] |
| 559 : nullptr); | 559 : nullptr); |
| 560 } | 560 } |
| 561 } | 561 } |
| 562 | 562 |
| 563 newContentLayerClients.push_back(std::move(contentLayerClient)); | 563 newContentLayerClients.push_back(std::move(contentLayerClient)); |
| 564 return ccPictureLayer; | 564 return ccPictureLayer; |
| 565 } | 565 } |
| 566 | 566 |
| 567 bool PaintArtifactCompositor::canMergeInto( | 567 PaintArtifactCompositor::PendingLayer::PendingLayer( |
| 568 const PaintArtifact& paintArtifact, | 568 const PaintChunk& firstPaintChunk, |
| 569 const PaintChunk& newChunk, | 569 bool chunkIsForeign, |
| 570 const PendingLayer& candidatePendingLayer) { | 570 GeometryMapper& geometryMapper) |
| 571 const PaintChunk& pendingLayerFirstChunk = | 571 : bounds(firstPaintChunk.bounds), |
| 572 *candidatePendingLayer.paintChunks[0]; | 572 knownToBeOpaque(firstPaintChunk.knownToBeOpaque), |
| 573 if (paintArtifact.getDisplayItemList()[newChunk.beginIndex].isForeignLayer()) | 573 backfaceHidden(firstPaintChunk.properties.backfaceHidden), |
| 574 return false; | 574 propertyTreeState(firstPaintChunk.properties.propertyTreeState), |
| 575 | 575 isForeign(chunkIsForeign) { |
| 576 if (paintArtifact.getDisplayItemList()[pendingLayerFirstChunk.beginIndex] | 576 paintChunks.push_back(&firstPaintChunk); |
| 577 .isForeignLayer()) | 577 } |
| 578 return false; | 578 |
| 579 | 579 void PaintArtifactCompositor::PendingLayer::merge( |
| 580 if (newChunk.properties.backfaceHidden != | 580 const PendingLayer& guest, |
| 581 pendingLayerFirstChunk.properties.backfaceHidden) | 581 GeometryMapper& geometryMapper) { |
| 582 return false; | 582 DCHECK(!isForeign && !guest.isForeign); |
| 583 | 583 DCHECK_EQ(backfaceHidden, guest.backfaceHidden); |
| 584 DCHECK_GE(candidatePendingLayer.paintChunks.size(), 1u); | 584 |
| 585 PropertyTreeStateIterator iterator(newChunk.properties.propertyTreeState); | 585 paintChunks.appendVector(guest.paintChunks); |
| 586 for (const PropertyTreeState* currentState = | 586 FloatRect oldBounds = bounds; |
| 587 &newChunk.properties.propertyTreeState; | 587 bounds.unite(geometryMapper |
| 588 currentState; currentState = iterator.next()) { | 588 .localToAncestorVisualRect( |
|
chrishtr
2017/02/28 22:50:35
This is a change (used to be localToAncestorRect).
trchen
2017/03/01 01:26:11
Good point. I shall add a test.
trchen
2017/03/07 01:41:24
Done.
| |
| 589 if (*currentState == candidatePendingLayer.propertyTreeState) | 589 guest.bounds, guest.propertyTreeState, propertyTreeState) |
| 590 return true; | 590 .rect()); |
| 591 if (currentState->hasDirectCompositingReasons()) | 591 if (bounds != oldBounds) |
| 592 knownToBeOpaque = false; | |
| 593 } | |
| 594 | |
| 595 void PaintArtifactCompositor::PendingLayer::upcast( | |
| 596 const PropertyTreeState& newState, | |
| 597 GeometryMapper& geometryMapper) { | |
| 598 bounds = geometryMapper | |
| 599 .localToAncestorVisualRect(bounds, propertyTreeState, newState) | |
| 600 .rect(); | |
| 601 propertyTreeState = newState; | |
| 602 // TODO(trchen): Upgrade GeometryMapper. | |
| 603 // knownToBeOpaque = knownToBeOpaque && enclosingRect(mappedRegion) == | |
|
chrishtr
2017/02/28 22:50:33
Are you trying to detect exact integer bounds here
trchen
2017/03/01 01:26:11
Sorry my comment was confusing. By enclosedRect I
trchen
2017/03/07 01:41:24
Comments clarified.
| |
| 604 // enclosedRect(mappedRegion); | |
| 605 // localToAncestorVisualRect returns the enclosingRect of the mapped region. | |
| 606 // A variant that returns the enclosedRect is needed. | |
| 607 knownToBeOpaque = false; | |
| 608 } | |
| 609 | |
| 610 static bool isNonCompositingAncestorOf( | |
| 611 const TransformPaintPropertyNode* ancestor, | |
| 612 const TransformPaintPropertyNode* node) { | |
| 613 for (; node != ancestor; node = node->parent()) { | |
| 614 if (!node || node->hasDirectCompositingReasons()) | |
| 592 return false; | 615 return false; |
| 593 } | 616 } |
| 594 return false; | 617 return true; |
| 595 } | 618 } |
| 596 | 619 |
| 597 bool PaintArtifactCompositor::mightOverlap( | 620 static bool canUpcastTo(const PropertyTreeState& guest, |
| 598 const PaintChunk& paintChunk, | 621 const PropertyTreeState& home) { |
| 599 const PendingLayer& candidatePendingLayer, | 622 DCHECK_EQ(home.effect(), guest.effect()); |
| 600 GeometryMapper& geometryMapper) { | 623 |
| 624 for (const ClipPaintPropertyNode* currentClip = guest.clip(); | |
| 625 currentClip != home.clip(); currentClip = currentClip->parent()) { | |
| 626 if (!currentClip || currentClip->hasDirectCompositingReasons()) | |
| 627 return false; | |
| 628 if (!isNonCompositingAncestorOf(home.transform(), | |
|
chrishtr
2017/02/28 22:50:35
Won't the check on line 633 handle all of these?
trchen
2017/03/01 01:26:11
Unfortunately not. Sometimes clips applied to a ch
| |
| 629 currentClip->localTransformSpace())) | |
| 630 return false; | |
| 631 } | |
| 632 | |
| 633 return isNonCompositingAncestorOf(home.transform(), guest.transform()); | |
| 634 } | |
| 635 | |
| 636 // Returns nullptr if 'ancestor' is not a strict ancestor of 'node'. | |
| 637 // Otherwise, return the child of 'ancestor' that is an ancestor of 'node' or | |
| 638 // 'node' itself. | |
| 639 static const EffectPaintPropertyNode* strictAncestorOf( | |
| 640 const EffectPaintPropertyNode* ancestor, | |
| 641 const EffectPaintPropertyNode* node) { | |
| 642 for (; node; node = node->parent()) { | |
| 643 if (node->parent() == ancestor) | |
| 644 return node; | |
| 645 } | |
| 646 return nullptr; | |
| 647 } | |
| 648 | |
| 649 static bool mightOverlap(const PropertyTreeState& stateA, | |
| 650 const FloatRect& boundsA, | |
| 651 const PropertyTreeState& stateB, | |
| 652 const FloatRect& boundsB, | |
| 653 GeometryMapper& geometryMapper) { | |
| 601 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(), | 654 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(), |
| 602 ClipPaintPropertyNode::root(), | 655 ClipPaintPropertyNode::root(), |
| 603 EffectPaintPropertyNode::root()); | 656 EffectPaintPropertyNode::root()); |
| 604 | 657 |
| 605 FloatRect paintChunkScreenVisualRect = | 658 FloatRect screenVisualRectA = |
| 606 geometryMapper | 659 geometryMapper |
| 607 .localToAncestorVisualRect(paintChunk.bounds, | 660 .localToAncestorVisualRect(boundsA, stateA, rootPropertyTreeState) |
| 608 paintChunk.properties.propertyTreeState, | |
| 609 rootPropertyTreeState) | |
| 610 .rect(); | 661 .rect(); |
| 611 | 662 |
| 612 FloatRect pendingLayerScreenVisualRect = | 663 FloatRect screenVisualRectB = |
| 613 geometryMapper | 664 geometryMapper |
| 614 .localToAncestorVisualRect(candidatePendingLayer.bounds, | 665 .localToAncestorVisualRect(boundsB, stateB, rootPropertyTreeState) |
| 615 candidatePendingLayer.propertyTreeState, | |
| 616 rootPropertyTreeState) | |
| 617 .rect(); | 666 .rect(); |
| 618 | 667 |
| 619 return paintChunkScreenVisualRect.intersects(pendingLayerScreenVisualRect); | 668 return screenVisualRectA.intersects(screenVisualRectB); |
| 620 } | 669 } |
| 621 | 670 |
| 622 PaintArtifactCompositor::PendingLayer::PendingLayer( | 671 void PaintArtifactCompositor::layerizeGroup( |
| 623 const PaintChunk& firstPaintChunk) | 672 const PaintArtifact& paintArtifact, |
| 624 : bounds(firstPaintChunk.bounds), | 673 Vector<PendingLayer>& pendingLayers, |
| 625 knownToBeOpaque(firstPaintChunk.knownToBeOpaque), | 674 GeometryMapper& geometryMapper, |
| 626 backfaceHidden(firstPaintChunk.properties.backfaceHidden), | 675 const EffectPaintPropertyNode& currentGroup, |
| 627 propertyTreeState(firstPaintChunk.properties.propertyTreeState) { | 676 Vector<PaintChunk>::const_iterator& chunkIt) { |
| 628 paintChunks.push_back(&firstPaintChunk); | 677 size_t firstLayerInCurrentGroup = pendingLayers.size(); |
| 629 } | 678 // The worst case time complexity of the algorithm is O(pqd), where |
| 630 | 679 // p = the number of paint chunks. |
| 631 void PaintArtifactCompositor::PendingLayer::add( | 680 // q = average number of trials to find a squash layer or rejected |
| 632 const PaintChunk& paintChunk, | 681 // for overlapping. |
| 633 GeometryMapper* geometryMapper) { | 682 // d = (sum of) the depth of property trees. |
| 634 DCHECK(paintChunk.properties.backfaceHidden == backfaceHidden); | 683 // The analysis as follows: |
| 635 paintChunks.push_back(&paintChunk); | 684 // Every paint chunk will be visited by the main loop below for exactly once, |
| 636 FloatRect mappedBounds = paintChunk.bounds; | 685 // except for chunks that enter or exit groups (case B & C below). |
| 637 if (geometryMapper) { | 686 // For normal chunk visit (case A), the only cost is determining squash, |
| 638 mappedBounds = geometryMapper->localToAncestorRect( | 687 // which costs O(qd), where d came from "canUpcastTo" and geometry mapping. |
| 639 mappedBounds, paintChunk.properties.propertyTreeState.transform(), | 688 // Subtotal: O(pqd) |
| 640 propertyTreeState.transform()); | 689 // For group entering and exiting, it could cost O(d) for each group, for |
| 641 } | 690 // searching the shallowest subgroup (strictAncestorOf), thus O(d^2) in total. |
| 642 bounds.unite(mappedBounds); | 691 // Also when exiting group, the group may be decomposited and squashed to a |
| 643 if (bounds.size() != paintChunks[0]->bounds.size()) { | 692 // previous layer. Again finding the host costs O(qd). Merging would cost O(p) |
| 644 if (bounds.size() != paintChunk.bounds.size()) | 693 // due to copying the chunk list. Subtotal: O((qd + p)d) = O(qd^2 + pd) |
| 645 knownToBeOpaque = false; | 694 // Assuming p > d, the total complexity would be O(pqd + qd^2 + pd) = O(pqd) |
| 646 else | 695 while (chunkIt != paintArtifact.paintChunks().end()) { |
| 647 knownToBeOpaque = paintChunk.knownToBeOpaque; | 696 // Look at the effect node of the next chunk. There are 3 possible cases: |
| 697 // A. The next chunk belongs to the current group but no subgroup. | |
| 698 // B. The next chunk does not belong to the current group. | |
| 699 // C. The next chunk belongs to some subgroup of the current group. | |
| 700 const EffectPaintPropertyNode* chunkEffect = | |
| 701 chunkIt->properties.propertyTreeState.effect(); | |
| 702 if (chunkEffect == ¤tGroup) { | |
| 703 // Case A: The next chunk belongs to the current group but no subgroup. | |
| 704 bool isForeign = paintArtifact.getDisplayItemList()[chunkIt->beginIndex] | |
| 705 .isForeignLayer(); | |
| 706 pendingLayers.push_back( | |
| 707 PendingLayer(*chunkIt++, isForeign, geometryMapper)); | |
| 708 if (isForeign) | |
| 709 continue; | |
| 710 } else { | |
| 711 const EffectPaintPropertyNode* subgroup = | |
| 712 strictAncestorOf(¤tGroup, chunkEffect); | |
| 713 // Case B: This means we need to close the current group without | |
| 714 // processing the next chunk. | |
| 715 if (!subgroup) | |
| 716 break; | |
| 717 // Case C: The following chunks belong to a subgroup. Process them by | |
| 718 // a recursion call. | |
| 719 size_t firstLayerInSubgroup = pendingLayers.size(); | |
| 720 layerizeGroup(paintArtifact, pendingLayers, geometryMapper, *subgroup, | |
| 721 chunkIt); | |
| 722 // Now the chunk iterator stepped over the subgroup we just saw. | |
| 723 // If the subgroup generated 2 or more layers then the grouping must be | |
| 724 // composited as well. | |
|
chrishtr
2017/02/28 22:50:33
Explain why.
trchen
2017/03/01 01:26:11
Acknowledged.
trchen
2017/03/07 01:41:24
Done.
| |
| 725 if (pendingLayers.size() != firstLayerInSubgroup + 1) | |
| 726 continue; | |
| 727 // Attempt to "decomposite" subgroup. Only decomposite one level deep | |
| 728 // because the recusion should have decomposited the sub-subgroups. | |
| 729 // If we see a deep subgroup here, that implies some intermediate | |
| 730 // subgroup must be composited. | |
| 731 PendingLayer& subgroupLayer = pendingLayers[firstLayerInSubgroup]; | |
| 732 if (subgroupLayer.propertyTreeState.effect() != subgroup) | |
| 733 continue; | |
| 734 if (subgroupLayer.isForeign) | |
| 735 continue; | |
| 736 // TODO(trchen): Exotic blending layer may be decomposited only if it | |
| 737 // could be merged into the first layer of the current group. | |
| 738 if (subgroup->blendMode() != SkBlendMode::kSrcOver) | |
| 739 continue; | |
| 740 if (subgroup->hasDirectCompositingReasons() || | |
| 741 !canUpcastTo(subgroupLayer.propertyTreeState, | |
| 742 PropertyTreeState(subgroup->localTransformSpace(), | |
| 743 subgroup->outputClip(), subgroup))) | |
| 744 continue; | |
| 745 subgroupLayer.upcast( | |
| 746 PropertyTreeState(subgroup->localTransformSpace(), | |
| 747 subgroup->outputClip(), ¤tGroup), | |
| 748 geometryMapper); | |
| 749 } | |
| 750 // At this point pendingLayers.back() is the either a layer from a | |
| 751 // "decomposited" subgroup or a layer created from a chunk we just | |
| 752 // processed. Now determine whether it could be merged into a previous | |
| 753 // layer. | |
| 754 const PendingLayer& newLayer = pendingLayers.back(); | |
| 755 DCHECK(!newLayer.isForeign); | |
| 756 DCHECK_EQ(¤tGroup, newLayer.propertyTreeState.effect()); | |
| 757 // This iterates pendingLayers[firstLayerInCurrentGroup:-1] in reverse. | |
| 758 for (size_t candidateIndex = pendingLayers.size() - 1; | |
| 759 candidateIndex-- > firstLayerInCurrentGroup;) { | |
| 760 PendingLayer& candidateLayer = pendingLayers[candidateIndex]; | |
| 761 if (!candidateLayer.isForeign && | |
|
chrishtr
2017/02/28 22:50:35
I think it would be clearer to move these conditio
trchen
2017/03/01 01:26:11
How about making it PendingLayer::canMerge(const P
trchen
2017/03/07 01:41:24
Done.
| |
| 762 newLayer.backfaceHidden == candidateLayer.backfaceHidden && | |
| 763 candidateLayer.propertyTreeState.effect() == ¤tGroup && | |
| 764 canUpcastTo(newLayer.propertyTreeState, | |
| 765 candidateLayer.propertyTreeState)) { | |
| 766 candidateLayer.merge(newLayer, geometryMapper); | |
| 767 pendingLayers.pop_back(); | |
| 768 break; | |
| 769 } | |
| 770 if (mightOverlap(newLayer.propertyTreeState, newLayer.bounds, | |
| 771 candidateLayer.propertyTreeState, candidateLayer.bounds, | |
| 772 geometryMapper)) | |
| 773 break; | |
| 774 } | |
| 648 } | 775 } |
| 649 } | 776 } |
| 650 | 777 |
| 651 void PaintArtifactCompositor::collectPendingLayers( | 778 void PaintArtifactCompositor::collectPendingLayers( |
| 652 const PaintArtifact& paintArtifact, | 779 const PaintArtifact& paintArtifact, |
| 653 Vector<PendingLayer>& pendingLayers, | 780 Vector<PendingLayer>& pendingLayers, |
| 654 GeometryMapper& geometryMapper) { | 781 GeometryMapper& geometryMapper) { |
| 655 // n = # of paint chunks. Memoizing canMergeInto() can get it to O(n^2), and | 782 Vector<PaintChunk>::const_iterator cursor = |
| 656 // other heuristics can make worst-case behavior better. | 783 paintArtifact.paintChunks().begin(); |
| 657 for (const PaintChunk& paintChunk : paintArtifact.paintChunks()) { | 784 layerizeGroup(paintArtifact, pendingLayers, geometryMapper, |
| 658 bool createNew = true; | 785 *EffectPaintPropertyNode::root(), cursor); |
| 659 for (Vector<PendingLayer>::reverse_iterator candidatePendingLayer = | 786 DCHECK_EQ(paintArtifact.paintChunks().end(), cursor); |
| 660 pendingLayers.rbegin(); | |
| 661 candidatePendingLayer != pendingLayers.rend(); | |
| 662 ++candidatePendingLayer) { | |
| 663 if (canMergeInto(paintArtifact, paintChunk, *candidatePendingLayer)) { | |
| 664 candidatePendingLayer->add(paintChunk, &geometryMapper); | |
| 665 createNew = false; | |
| 666 break; | |
| 667 } | |
| 668 if (mightOverlap(paintChunk, *candidatePendingLayer, geometryMapper)) { | |
| 669 break; | |
| 670 } | |
| 671 } | |
| 672 if (createNew) | |
| 673 pendingLayers.push_back(PendingLayer(paintChunk)); | |
| 674 } | |
| 675 } | 787 } |
| 676 | 788 |
| 677 void PaintArtifactCompositor::update( | 789 void PaintArtifactCompositor::update( |
| 678 const PaintArtifact& paintArtifact, | 790 const PaintArtifact& paintArtifact, |
| 679 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations, | 791 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations, |
| 680 bool storeDebugInfo, | 792 bool storeDebugInfo, |
| 681 GeometryMapper& geometryMapper) { | 793 GeometryMapper& geometryMapper) { |
| 682 #ifndef NDEBUG | 794 #ifndef NDEBUG |
| 683 storeDebugInfo = true; | 795 storeDebugInfo = true; |
| 684 #endif | 796 #endif |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 750 #ifndef NDEBUG | 862 #ifndef NDEBUG |
| 751 void PaintArtifactCompositor::showDebugData() { | 863 void PaintArtifactCompositor::showDebugData() { |
| 752 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo) | 864 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo) |
| 753 ->toPrettyJSONString() | 865 ->toPrettyJSONString() |
| 754 .utf8() | 866 .utf8() |
| 755 .data(); | 867 .data(); |
| 756 } | 868 } |
| 757 #endif | 869 #endif |
| 758 | 870 |
| 759 } // namespace blink | 871 } // namespace blink |
| OLD | NEW |