Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(186)

Side by Side Diff: third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp

Issue 2714673002: [SPv2] Implement effect compositing for indirect reasons (Closed)
Patch Set: address review comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 == &currentGroup) {
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(&currentGroup, 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(), &currentGroup),
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(&currentGroup, 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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698