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

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: fix one crash bug & update tests & added unit tests 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 546 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 == &currentGroup) {
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(&currentGroup, 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(), &currentGroup),
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(&currentGroup, 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() == &currentGroup &&
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698