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

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: mark tests 589265 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 324 matching lines...) Expand 10 before | Expand all | Expand 10 after
335 ccInvalidationRect, 335 ccInvalidationRect,
336 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index] 336 rasterTracking ? &rasterTracking->trackedRasterInvalidations[index]
337 : nullptr); 337 : nullptr);
338 } 338 }
339 } 339 }
340 340
341 newContentLayerClients.push_back(std::move(contentLayerClient)); 341 newContentLayerClients.push_back(std::move(contentLayerClient));
342 return ccPictureLayer; 342 return ccPictureLayer;
343 } 343 }
344 344
345 bool PaintArtifactCompositor::canMergeInto( 345 PaintArtifactCompositor::PendingLayer::PendingLayer(
346 const PaintArtifact& paintArtifact, 346 const PaintChunk& firstPaintChunk,
347 const PaintChunk& newChunk, 347 bool chunkIsForeign)
348 const PendingLayer& candidatePendingLayer) { 348 : bounds(firstPaintChunk.bounds),
349 const PaintChunk& pendingLayerFirstChunk = 349 knownToBeOpaque(firstPaintChunk.knownToBeOpaque),
350 *candidatePendingLayer.paintChunks[0]; 350 backfaceHidden(firstPaintChunk.properties.backfaceHidden),
351 if (paintArtifact.getDisplayItemList()[newChunk.beginIndex].isForeignLayer()) 351 propertyTreeState(firstPaintChunk.properties.propertyTreeState),
352 return false; 352 isForeign(chunkIsForeign) {
353 353 paintChunks.push_back(&firstPaintChunk);
354 if (paintArtifact.getDisplayItemList()[pendingLayerFirstChunk.beginIndex] 354 }
355 .isForeignLayer()) 355
356 return false; 356 void PaintArtifactCompositor::PendingLayer::merge(
357 357 const PendingLayer& guest,
358 if (newChunk.properties.backfaceHidden != 358 GeometryMapper& geometryMapper) {
359 pendingLayerFirstChunk.properties.backfaceHidden) 359 DCHECK(!isForeign && !guest.isForeign);
360 return false; 360 DCHECK_EQ(backfaceHidden, guest.backfaceHidden);
361 361
362 DCHECK_GE(candidatePendingLayer.paintChunks.size(), 1u); 362 paintChunks.appendVector(guest.paintChunks);
363 PropertyTreeStateIterator iterator(newChunk.properties.propertyTreeState); 363 FloatRect guestBoundsInHome = guest.bounds;
364 for (const PropertyTreeState* currentState = 364 geometryMapper.localToAncestorVisualRect(
365 &newChunk.properties.propertyTreeState; 365 guest.propertyTreeState, propertyTreeState, guestBoundsInHome);
366 currentState; currentState = iterator.next()) { 366 FloatRect oldBounds = bounds;
367 if (*currentState == candidatePendingLayer.propertyTreeState) 367 bounds.unite(guestBoundsInHome);
368 return true; 368 if (bounds != oldBounds)
369 if (currentState->hasDirectCompositingReasons()) 369 knownToBeOpaque = false;
370 // TODO(crbug.com/701991): Upgrade GeometryMapper.
371 // If we knew the new bounds is enclosed by the mapped opaque region of
372 // the guest layer, we can deduce the merged layer being opaque too.
373 }
374
375 static bool canUpcastTo(const PropertyTreeState& guest,
376 const PropertyTreeState& home);
377 bool PaintArtifactCompositor::PendingLayer::canMerge(
378 const PendingLayer& guest) const {
379 if (isForeign || guest.isForeign)
380 return false;
381 if (backfaceHidden != guest.backfaceHidden)
382 return false;
383 if (propertyTreeState.effect() != guest.propertyTreeState.effect())
384 return false;
385 return canUpcastTo(guest.propertyTreeState, propertyTreeState);
386 }
387
388 void PaintArtifactCompositor::PendingLayer::upcast(
389 const PropertyTreeState& newState,
390 GeometryMapper& geometryMapper) {
391 DCHECK(!isForeign);
392 geometryMapper.localToAncestorVisualRect(propertyTreeState, newState, bounds);
393
394 propertyTreeState = newState;
395 // TODO(crbug.com/701991): Upgrade GeometryMapper.
396 // A local visual rect mapped to an ancestor space may become a polygon
397 // (e.g. consider transformed clip), also effects may affect the opaque
398 // region. To determine whether the layer is still opaque, we need to
399 // query conservative opaque rect after mapping to an ancestor space,
400 // which is not supported by GeometryMapper yet.
401 knownToBeOpaque = false;
402 }
403
404 static bool isNonCompositingAncestorOf(
405 const TransformPaintPropertyNode* ancestor,
406 const TransformPaintPropertyNode* node) {
407 for (; node != ancestor; node = node->parent()) {
408 if (!node || node->hasDirectCompositingReasons())
370 return false; 409 return false;
371 } 410 }
372 return false; 411 return true;
373 } 412 }
374 413
375 bool PaintArtifactCompositor::mightOverlap( 414 // Determines whether drawings based on the 'guest' state can be painted into
376 const PaintChunk& paintChunk, 415 // a layer with the 'home' state. A number of criteria need to be met:
377 const PendingLayer& candidatePendingLayer, 416 // 1. The guest effect must be a descendant of the home effect. However this
378 GeometryMapper& geometryMapper) { 417 // check is enforced by the layerization recursion. Here we assume the guest
418 // has already been upcasted to the same effect.
419 // 2. The guest clip must be a descendant of the home clip.
420 // 3. The local space of each clip and effect node on the ancestor chain must
421 // be within compositing boundary of the home transform space.
422 // 4. The guest transform space must be within compositing boundary of the home
423 // transform space.
424 static bool canUpcastTo(const PropertyTreeState& guest,
425 const PropertyTreeState& home) {
426 DCHECK_EQ(home.effect(), guest.effect());
427
428 for (const ClipPaintPropertyNode* currentClip = guest.clip();
429 currentClip != home.clip(); currentClip = currentClip->parent()) {
430 if (!currentClip || currentClip->hasDirectCompositingReasons())
431 return false;
432 if (!isNonCompositingAncestorOf(home.transform(),
433 currentClip->localTransformSpace()))
434 return false;
435 }
436
437 return isNonCompositingAncestorOf(home.transform(), guest.transform());
438 }
439
440 // Returns nullptr if 'ancestor' is not a strict ancestor of 'node'.
441 // Otherwise, return the child of 'ancestor' that is an ancestor of 'node' or
442 // 'node' itself.
443 static const EffectPaintPropertyNode* strictChildOfAlongPath(
444 const EffectPaintPropertyNode* ancestor,
445 const EffectPaintPropertyNode* node) {
446 for (; node; node = node->parent()) {
447 if (node->parent() == ancestor)
448 return node;
449 }
450 return nullptr;
451 }
452
453 bool PaintArtifactCompositor::mightOverlap(const PendingLayer& layerA,
454 const PendingLayer& layerB,
455 GeometryMapper& geometryMapper) {
379 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(), 456 PropertyTreeState rootPropertyTreeState(TransformPaintPropertyNode::root(),
380 ClipPaintPropertyNode::root(), 457 ClipPaintPropertyNode::root(),
381 EffectPaintPropertyNode::root()); 458 EffectPaintPropertyNode::root());
382 459
383 FloatRect paintChunkScreenVisualRect = paintChunk.bounds; 460 FloatRect boundsA = layerA.bounds;
384 geometryMapper.localToAncestorVisualRect( 461 geometryMapper.localToAncestorVisualRect(layerA.propertyTreeState,
385 paintChunk.properties.propertyTreeState, rootPropertyTreeState, 462 rootPropertyTreeState, boundsA);
386 paintChunkScreenVisualRect); 463 FloatRect boundsB = layerB.bounds;
387 464 geometryMapper.localToAncestorVisualRect(layerB.propertyTreeState,
388 FloatRect pendingLayerScreenVisualRect = candidatePendingLayer.bounds; 465 rootPropertyTreeState, boundsB);
389 geometryMapper.localToAncestorVisualRect( 466
390 candidatePendingLayer.propertyTreeState, rootPropertyTreeState, 467 return boundsA.intersects(boundsB);
391 pendingLayerScreenVisualRect); 468 }
392 469
393 return paintChunkScreenVisualRect.intersects(pendingLayerScreenVisualRect); 470 bool PaintArtifactCompositor::canDecompositeEffect(
394 } 471 const EffectPaintPropertyNode* effect,
395 472 const PendingLayer& layer) {
396 PaintArtifactCompositor::PendingLayer::PendingLayer( 473 // If the effect associated with the layer is deeper than than the effect
397 const PaintChunk& firstPaintChunk) 474 // we are attempting to decomposite, than implies some previous decision
398 : bounds(firstPaintChunk.bounds), 475 // did not allow to decomposite intermediate effects.
399 knownToBeOpaque(firstPaintChunk.knownToBeOpaque), 476 if (layer.propertyTreeState.effect() != effect)
400 backfaceHidden(firstPaintChunk.properties.backfaceHidden), 477 return false;
401 propertyTreeState(firstPaintChunk.properties.propertyTreeState) { 478 if (layer.isForeign)
402 paintChunks.push_back(&firstPaintChunk); 479 return false;
403 } 480 // TODO(trchen): Exotic blending layer may be decomposited only if it could
404 481 // be merged into the first layer of the current group.
405 void PaintArtifactCompositor::PendingLayer::add( 482 if (effect->blendMode() != SkBlendMode::kSrcOver)
406 const PaintChunk& paintChunk, 483 return false;
407 GeometryMapper* geometryMapper) { 484 if (effect->hasDirectCompositingReasons())
408 DCHECK(paintChunk.properties.backfaceHidden == backfaceHidden); 485 return false;
409 paintChunks.push_back(&paintChunk); 486 if (!canUpcastTo(layer.propertyTreeState,
410 FloatRect mappedBounds = paintChunk.bounds; 487 PropertyTreeState(effect->localTransformSpace(),
411 if (geometryMapper) { 488 effect->outputClip(), effect)))
412 geometryMapper->localToAncestorRect( 489 return false;
413 paintChunk.properties.propertyTreeState.transform(), 490 return true;
414 propertyTreeState.transform(), mappedBounds); 491 }
415 } 492
416 bounds.unite(mappedBounds); 493 void PaintArtifactCompositor::layerizeGroup(
417 if (bounds.size() != paintChunks[0]->bounds.size()) { 494 const PaintArtifact& paintArtifact,
418 if (bounds.size() != paintChunk.bounds.size()) 495 Vector<PendingLayer>& pendingLayers,
419 knownToBeOpaque = false; 496 GeometryMapper& geometryMapper,
420 else 497 const EffectPaintPropertyNode& currentGroup,
421 knownToBeOpaque = paintChunk.knownToBeOpaque; 498 Vector<PaintChunk>::const_iterator& chunkIt) {
499 size_t firstLayerInCurrentGroup = pendingLayers.size();
500 // The worst case time complexity of the algorithm is O(pqd), where
501 // p = the number of paint chunks.
502 // q = average number of trials to find a squash layer or rejected
503 // for overlapping.
504 // d = (sum of) the depth of property trees.
505 // The analysis as follows:
506 // Every paint chunk will be visited by the main loop below for exactly once,
507 // except for chunks that enter or exit groups (case B & C below).
508 // For normal chunk visit (case A), the only cost is determining squash,
509 // which costs O(qd), where d came from "canUpcastTo" and geometry mapping.
510 // Subtotal: O(pqd)
511 // For group entering and exiting, it could cost O(d) for each group, for
512 // searching the shallowest subgroup (strictChildOfAlongPath), thus O(d^2)
513 // in total.
514 // Also when exiting group, the group may be decomposited and squashed to a
515 // previous layer. Again finding the host costs O(qd). Merging would cost O(p)
516 // due to copying the chunk list. Subtotal: O((qd + p)d) = O(qd^2 + pd)
517 // Assuming p > d, the total complexity would be O(pqd + qd^2 + pd) = O(pqd)
518 while (chunkIt != paintArtifact.paintChunks().end()) {
519 // Look at the effect node of the next chunk. There are 3 possible cases:
520 // A. The next chunk belongs to the current group but no subgroup.
521 // B. The next chunk does not belong to the current group.
522 // C. The next chunk belongs to some subgroup of the current group.
523 const EffectPaintPropertyNode* chunkEffect =
524 chunkIt->properties.propertyTreeState.effect();
525 if (chunkEffect == &currentGroup) {
526 // Case A: The next chunk belongs to the current group but no subgroup.
527 bool isForeign = paintArtifact.getDisplayItemList()[chunkIt->beginIndex]
528 .isForeignLayer();
529 pendingLayers.push_back(PendingLayer(*chunkIt++, isForeign));
530 if (isForeign)
531 continue;
532 } else {
533 const EffectPaintPropertyNode* subgroup =
534 strictChildOfAlongPath(&currentGroup, chunkEffect);
535 // Case B: This means we need to close the current group without
536 // processing the next chunk.
537 if (!subgroup)
538 break;
539 // Case C: The following chunks belong to a subgroup. Process them by
540 // a recursion call.
541 size_t firstLayerInSubgroup = pendingLayers.size();
542 layerizeGroup(paintArtifact, pendingLayers, geometryMapper, *subgroup,
543 chunkIt);
544 // Now the chunk iterator stepped over the subgroup we just saw.
545 // If the subgroup generated 2 or more layers then the subgroup must be
546 // composited to satisfy grouping requirement.
547 // i.e. Grouping effects generally must be applied atomically,
548 // for example, Opacity(A+B) != Opacity(A) + Opacity(B), thus an effect
549 // either applied 100% within a layer, or not at all applied within layer
550 // (i.e. applied by compositor render surface instead).
551 if (pendingLayers.size() != firstLayerInSubgroup + 1)
552 continue;
553 // Now attempt to "decomposite" subgroup.
554 PendingLayer& subgroupLayer = pendingLayers[firstLayerInSubgroup];
555 if (!canDecompositeEffect(subgroup, subgroupLayer))
556 continue;
557 subgroupLayer.upcast(
558 PropertyTreeState(subgroup->localTransformSpace(),
559 subgroup->outputClip(), &currentGroup),
560 geometryMapper);
561 }
562 // At this point pendingLayers.back() is the either a layer from a
563 // "decomposited" subgroup or a layer created from a chunk we just
564 // processed. Now determine whether it could be merged into a previous
565 // layer.
566 const PendingLayer& newLayer = pendingLayers.back();
567 DCHECK(!newLayer.isForeign);
568 DCHECK_EQ(&currentGroup, newLayer.propertyTreeState.effect());
569 // This iterates pendingLayers[firstLayerInCurrentGroup:-1] in reverse.
570 for (size_t candidateIndex = pendingLayers.size() - 1;
571 candidateIndex-- > firstLayerInCurrentGroup;) {
572 PendingLayer& candidateLayer = pendingLayers[candidateIndex];
573 if (candidateLayer.canMerge(newLayer)) {
574 candidateLayer.merge(newLayer, geometryMapper);
575 pendingLayers.pop_back();
576 break;
577 }
578 if (mightOverlap(newLayer, candidateLayer, geometryMapper))
579 break;
580 }
422 } 581 }
423 } 582 }
424 583
425 void PaintArtifactCompositor::collectPendingLayers( 584 void PaintArtifactCompositor::collectPendingLayers(
426 const PaintArtifact& paintArtifact, 585 const PaintArtifact& paintArtifact,
427 Vector<PendingLayer>& pendingLayers, 586 Vector<PendingLayer>& pendingLayers,
428 GeometryMapper& geometryMapper) { 587 GeometryMapper& geometryMapper) {
429 // n = # of paint chunks. Memoizing canMergeInto() can get it to O(n^2), and 588 Vector<PaintChunk>::const_iterator cursor =
430 // other heuristics can make worst-case behavior better. 589 paintArtifact.paintChunks().begin();
431 for (const PaintChunk& paintChunk : paintArtifact.paintChunks()) { 590 layerizeGroup(paintArtifact, pendingLayers, geometryMapper,
432 bool createNew = true; 591 *EffectPaintPropertyNode::root(), cursor);
433 for (Vector<PendingLayer>::reverse_iterator candidatePendingLayer = 592 DCHECK_EQ(paintArtifact.paintChunks().end(), cursor);
434 pendingLayers.rbegin();
435 candidatePendingLayer != pendingLayers.rend();
436 ++candidatePendingLayer) {
437 if (canMergeInto(paintArtifact, paintChunk, *candidatePendingLayer)) {
438 candidatePendingLayer->add(paintChunk, &geometryMapper);
439 createNew = false;
440 break;
441 }
442 if (mightOverlap(paintChunk, *candidatePendingLayer, geometryMapper)) {
443 break;
444 }
445 }
446 if (createNew)
447 pendingLayers.push_back(PendingLayer(paintChunk));
448 }
449 } 593 }
450 594
451 void PaintArtifactCompositor::update( 595 void PaintArtifactCompositor::update(
452 const PaintArtifact& paintArtifact, 596 const PaintArtifact& paintArtifact,
453 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations, 597 RasterInvalidationTrackingMap<const PaintChunk>* rasterChunkInvalidations,
454 bool storeDebugInfo, 598 bool storeDebugInfo,
455 GeometryMapper& geometryMapper) { 599 GeometryMapper& geometryMapper) {
456 #ifndef NDEBUG 600 #ifndef NDEBUG
457 storeDebugInfo = true; 601 storeDebugInfo = true;
458 #endif 602 #endif
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
526 #ifndef NDEBUG 670 #ifndef NDEBUG
527 void PaintArtifactCompositor::showDebugData() { 671 void PaintArtifactCompositor::showDebugData() {
528 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo) 672 LOG(ERROR) << layersAsJSON(LayerTreeIncludesDebugInfo)
529 ->toPrettyJSONString() 673 ->toPrettyJSONString()
530 .utf8() 674 .utf8()
531 .data(); 675 .data();
532 } 676 }
533 #endif 677 #endif
534 678
535 } // namespace blink 679 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698