2 Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
3 Copyright (C) 2010 Apple Inc. All rights reserved.
4 Copyright (C) 2012 Company 100, Inc.
5 Copyright (C) 2012 Intel Corporation. All rights reserved.
6 Copyright (C) 2017 Sony Interactive Entertainment Inc.
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
25 #include "CoordinatedGraphicsLayer.h"
27 #if USE(COORDINATED_GRAPHICS)
29 #include "FloatQuad.h"
30 #include "GraphicsContext.h"
31 #include "GraphicsLayer.h"
32 #include "GraphicsLayerFactory.h"
33 #include "NicosiaBackingStoreTextureMapperImpl.h"
34 #include "NicosiaCompositionLayerTextureMapperImpl.h"
35 #include "NicosiaContentLayerTextureMapperImpl.h"
36 #include "NicosiaImageBackingTextureMapperImpl.h"
37 #include "NicosiaPaintingContext.h"
38 #include "NicosiaPaintingEngine.h"
39 #include "ScrollableArea.h"
40 #include "TextureMapperPlatformLayerProxyProvider.h"
41 #include "TiledBackingStore.h"
43 #include <wtf/SetForScope.h>
45 #include <wtf/text/CString.h>
49 std::unique_ptr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient& client, Type layerType)
52 return std::make_unique<CoordinatedGraphicsLayer>(layerType, client);
54 return factory->createGraphicsLayer(layerType, client);
57 void CoordinatedGraphicsLayer::notifyFlushRequired()
62 if (m_coordinator->isFlushingLayerChanges())
65 client().notifyFlushRequired(this);
68 void CoordinatedGraphicsLayer::didChangeAnimations()
70 m_nicosia.delta.animationsChanged = true;
71 notifyFlushRequired();
74 void CoordinatedGraphicsLayer::didChangeChildren()
76 m_nicosia.delta.childrenChanged = true;
77 notifyFlushRequired();
80 void CoordinatedGraphicsLayer::didChangeFilters()
82 m_nicosia.delta.filtersChanged = true;
83 notifyFlushRequired();
86 void CoordinatedGraphicsLayer::didUpdateTileBuffers()
88 if (!isShowingRepaintCounter())
91 auto repaintCount = incrementRepaintCount();
92 m_nicosia.repaintCounter.count = repaintCount;
93 m_nicosia.delta.repaintCounterChanged = true;
96 void CoordinatedGraphicsLayer::setShouldUpdateVisibleRect()
98 m_shouldUpdateVisibleRect = true;
99 for (auto& child : children())
100 downcast<CoordinatedGraphicsLayer>(*child).setShouldUpdateVisibleRect();
102 downcast<CoordinatedGraphicsLayer>(*replicaLayer()).setShouldUpdateVisibleRect();
105 void CoordinatedGraphicsLayer::didChangeGeometry()
107 notifyFlushRequired();
108 setShouldUpdateVisibleRect();
111 CoordinatedGraphicsLayer::CoordinatedGraphicsLayer(Type layerType, GraphicsLayerClient& client)
112 : GraphicsLayer(layerType, client)
116 , m_shouldUpdateVisibleRect(true)
117 , m_movingVisibleRect(false)
118 , m_pendingContentsScaleAdjustment(false)
119 , m_pendingVisibleRectAdjustment(false)
120 , m_shouldUpdatePlatformLayer(false)
122 , m_compositedNativeImagePtr(0)
123 , m_animationStartedTimer(*this, &CoordinatedGraphicsLayer::animationStartedTimerFired)
125 static Nicosia::PlatformLayer::LayerID nextLayerID = 1;
126 m_id = nextLayerID++;
128 m_nicosia.layer = Nicosia::CompositionLayer::create(m_id,
129 Nicosia::CompositionLayerTextureMapperImpl::createFactory());
131 // Enforce a complete flush on the first occasion.
132 m_nicosia.delta.value = UINT_MAX;
135 CoordinatedGraphicsLayer::~CoordinatedGraphicsLayer()
138 purgeBackingStores();
139 m_coordinator->detachLayer(this);
141 ASSERT(!m_nicosia.imageBacking);
142 ASSERT(!m_nicosia.backingStore);
146 bool CoordinatedGraphicsLayer::isCoordinatedGraphicsLayer() const
151 Nicosia::PlatformLayer::LayerID CoordinatedGraphicsLayer::id() const
156 auto CoordinatedGraphicsLayer::primaryLayerID() const -> PlatformLayerID
161 bool CoordinatedGraphicsLayer::setChildren(const Vector<GraphicsLayer*>& children)
163 bool ok = GraphicsLayer::setChildren(children);
170 void CoordinatedGraphicsLayer::addChild(GraphicsLayer* layer)
172 GraphicsLayer::addChild(layer);
173 downcast<CoordinatedGraphicsLayer>(*layer).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
177 void CoordinatedGraphicsLayer::addChildAtIndex(GraphicsLayer* layer, int index)
179 GraphicsLayer::addChildAtIndex(layer, index);
180 downcast<CoordinatedGraphicsLayer>(*layer).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
184 void CoordinatedGraphicsLayer::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling)
186 GraphicsLayer::addChildAbove(layer, sibling);
187 downcast<CoordinatedGraphicsLayer>(*layer).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
191 void CoordinatedGraphicsLayer::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling)
193 GraphicsLayer::addChildBelow(layer, sibling);
194 downcast<CoordinatedGraphicsLayer>(*layer).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
198 bool CoordinatedGraphicsLayer::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild)
200 bool ok = GraphicsLayer::replaceChild(oldChild, newChild);
203 downcast<CoordinatedGraphicsLayer>(*newChild).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
208 void CoordinatedGraphicsLayer::removeFromParent()
210 if (CoordinatedGraphicsLayer* parentLayer = downcast<CoordinatedGraphicsLayer>(parent()))
211 parentLayer->didChangeChildren();
212 GraphicsLayer::removeFromParent();
215 void CoordinatedGraphicsLayer::setPosition(const FloatPoint& p)
220 GraphicsLayer::setPosition(p);
221 m_nicosia.delta.positionChanged = true;
225 void CoordinatedGraphicsLayer::setAnchorPoint(const FloatPoint3D& p)
227 if (anchorPoint() == p)
230 GraphicsLayer::setAnchorPoint(p);
231 m_nicosia.delta.anchorPointChanged = true;
235 void CoordinatedGraphicsLayer::setSize(const FloatSize& size)
237 if (this->size() == size)
240 GraphicsLayer::setSize(size);
241 m_nicosia.delta.sizeChanged = true;
244 maskLayer()->setSize(size);
248 void CoordinatedGraphicsLayer::setTransform(const TransformationMatrix& t)
250 if (transform() == t)
253 GraphicsLayer::setTransform(t);
254 m_nicosia.delta.transformChanged = true;
259 void CoordinatedGraphicsLayer::setChildrenTransform(const TransformationMatrix& t)
261 if (childrenTransform() == t)
264 GraphicsLayer::setChildrenTransform(t);
265 m_nicosia.delta.childrenTransformChanged = true;
270 void CoordinatedGraphicsLayer::setPreserves3D(bool b)
272 if (preserves3D() == b)
275 GraphicsLayer::setPreserves3D(b);
276 m_nicosia.delta.flagsChanged = true;
281 void CoordinatedGraphicsLayer::setMasksToBounds(bool b)
283 if (masksToBounds() == b)
285 GraphicsLayer::setMasksToBounds(b);
286 m_nicosia.delta.flagsChanged = true;
291 void CoordinatedGraphicsLayer::setDrawsContent(bool b)
293 if (drawsContent() == b)
295 GraphicsLayer::setDrawsContent(b);
296 m_nicosia.delta.flagsChanged = true;
298 notifyFlushRequired();
301 void CoordinatedGraphicsLayer::setContentsVisible(bool b)
303 if (contentsAreVisible() == b)
305 GraphicsLayer::setContentsVisible(b);
306 m_nicosia.delta.flagsChanged = true;
309 maskLayer()->setContentsVisible(b);
311 notifyFlushRequired();
314 void CoordinatedGraphicsLayer::setContentsOpaque(bool b)
316 if (contentsOpaque() == b)
319 GraphicsLayer::setContentsOpaque(b);
320 m_nicosia.delta.flagsChanged = true;
322 // Demand a repaint of the whole layer.
323 if (!m_needsDisplay.completeLayer) {
324 m_needsDisplay.completeLayer = true;
325 m_needsDisplay.rects.clear();
327 addRepaintRect({ { }, m_size });
330 notifyFlushRequired();
333 void CoordinatedGraphicsLayer::setBackfaceVisibility(bool b)
335 if (backfaceVisibility() == b)
338 GraphicsLayer::setBackfaceVisibility(b);
339 m_nicosia.delta.flagsChanged = true;
341 notifyFlushRequired();
344 void CoordinatedGraphicsLayer::setOpacity(float opacity)
346 if (this->opacity() == opacity)
349 GraphicsLayer::setOpacity(opacity);
350 m_nicosia.delta.opacityChanged = true;
352 notifyFlushRequired();
355 void CoordinatedGraphicsLayer::setContentsRect(const FloatRect& r)
357 if (contentsRect() == r)
360 GraphicsLayer::setContentsRect(r);
361 m_nicosia.delta.contentsRectChanged = true;
363 notifyFlushRequired();
366 void CoordinatedGraphicsLayer::setContentsTileSize(const FloatSize& s)
368 if (contentsTileSize() == s)
371 GraphicsLayer::setContentsTileSize(s);
372 m_nicosia.delta.contentsTilingChanged = true;
373 notifyFlushRequired();
376 void CoordinatedGraphicsLayer::setContentsTilePhase(const FloatSize& p)
378 if (contentsTilePhase() == p)
381 GraphicsLayer::setContentsTilePhase(p);
382 m_nicosia.delta.contentsTilingChanged = true;
383 notifyFlushRequired();
386 bool GraphicsLayer::supportsContentsTiling()
391 void CoordinatedGraphicsLayer::setContentsNeedsDisplay()
393 #if USE(COORDINATED_GRAPHICS_THREADED) && USE(NICOSIA)
394 if (m_nicosia.contentLayer)
395 m_shouldUpdatePlatformLayer = true;
398 notifyFlushRequired();
399 addRepaintRect(contentsRect());
402 void CoordinatedGraphicsLayer::setContentsToPlatformLayer(PlatformLayer* platformLayer, ContentsLayerPurpose)
404 #if USE(COORDINATED_GRAPHICS_THREADED) && USE(NICOSIA)
405 auto* contentLayer = downcast<Nicosia::ContentLayer>(platformLayer);
406 if (m_nicosia.contentLayer != contentLayer) {
407 m_nicosia.contentLayer = contentLayer;
408 m_nicosia.delta.contentLayerChanged = true;
410 notifyFlushRequired();
412 UNUSED_PARAM(platformLayer);
416 bool CoordinatedGraphicsLayer::filtersCanBeComposited(const FilterOperations& filters) const
421 for (const auto& filterOperation : filters.operations()) {
422 if (filterOperation->type() == FilterOperation::REFERENCE)
429 bool CoordinatedGraphicsLayer::setFilters(const FilterOperations& newFilters)
431 bool canCompositeFilters = filtersCanBeComposited(newFilters);
432 if (filters() == newFilters)
433 return canCompositeFilters;
435 if (canCompositeFilters) {
436 if (!GraphicsLayer::setFilters(newFilters))
439 } else if (filters().size()) {
444 return canCompositeFilters;
447 void CoordinatedGraphicsLayer::setContentsToSolidColor(const Color& color)
449 if (m_solidColor == color)
452 m_solidColor = color;
453 m_nicosia.delta.solidColorChanged = true;
455 notifyFlushRequired();
458 void CoordinatedGraphicsLayer::setShowDebugBorder(bool show)
460 if (isShowingDebugBorder() == show)
463 GraphicsLayer::setShowDebugBorder(show);
464 m_nicosia.debugBorder.visible = show;
465 m_nicosia.delta.debugBorderChanged = true;
467 if (m_nicosia.debugBorder.visible)
468 updateDebugIndicators();
470 notifyFlushRequired();
473 void CoordinatedGraphicsLayer::setShowRepaintCounter(bool show)
475 if (isShowingRepaintCounter() == show)
478 GraphicsLayer::setShowRepaintCounter(show);
479 m_nicosia.repaintCounter.visible = show;
480 m_nicosia.delta.repaintCounterChanged = true;
482 notifyFlushRequired();
485 void CoordinatedGraphicsLayer::setContentsToImage(Image* image)
487 NativeImagePtr nativeImagePtr = image ? image->nativeImageForCurrentFrame() : nullptr;
488 if (m_compositedImage == image && m_compositedNativeImagePtr == nativeImagePtr)
491 m_compositedImage = image;
492 m_compositedNativeImagePtr = nativeImagePtr;
494 GraphicsLayer::setContentsToImage(image);
495 notifyFlushRequired();
498 void CoordinatedGraphicsLayer::setMaskLayer(GraphicsLayer* layer)
500 if (layer == maskLayer())
503 GraphicsLayer::setMaskLayer(layer);
508 layer->setSize(size());
509 layer->setContentsVisible(contentsAreVisible());
511 m_nicosia.delta.maskChanged = true;
513 notifyFlushRequired();
516 bool CoordinatedGraphicsLayer::shouldDirectlyCompositeImage(Image* image) const
518 if (!image || !image->isBitmapImage())
521 enum { MaxDimenstionForDirectCompositing = 2000 };
522 if (image->width() > MaxDimenstionForDirectCompositing || image->height() > MaxDimenstionForDirectCompositing)
528 void CoordinatedGraphicsLayer::setReplicatedByLayer(GraphicsLayer* layer)
530 if (layer == replicaLayer())
533 GraphicsLayer::setReplicatedByLayer(layer);
534 m_nicosia.delta.replicaChanged = true;
535 notifyFlushRequired();
538 void CoordinatedGraphicsLayer::setNeedsDisplay()
540 if (!drawsContent() || !contentsAreVisible() || m_size.isEmpty() || m_needsDisplay.completeLayer)
543 m_needsDisplay.completeLayer = true;
544 m_needsDisplay.rects.clear();
546 notifyFlushRequired();
547 addRepaintRect({ { }, m_size });
550 void CoordinatedGraphicsLayer::setNeedsDisplayInRect(const FloatRect& initialRect, ShouldClipToLayer shouldClip)
552 if (!drawsContent() || !contentsAreVisible() || m_size.isEmpty() || m_needsDisplay.completeLayer)
555 auto rect = initialRect;
556 if (shouldClip == ClipToLayer)
557 rect.intersect({ { }, m_size });
562 auto& rects = m_needsDisplay.rects;
563 bool alreadyRecorded = std::any_of(rects.begin(), rects.end(),
564 [&](auto& dirtyRect) { return dirtyRect.contains(rect); });
568 if (rects.size() < 32)
571 rects[0].unite(rect);
573 notifyFlushRequired();
574 addRepaintRect(rect);
577 void CoordinatedGraphicsLayer::flushCompositingState(const FloatRect& rect)
579 if (CoordinatedGraphicsLayer* mask = downcast<CoordinatedGraphicsLayer>(maskLayer()))
580 mask->flushCompositingStateForThisLayerOnly();
582 if (CoordinatedGraphicsLayer* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer()))
583 replica->flushCompositingStateForThisLayerOnly();
585 flushCompositingStateForThisLayerOnly();
587 for (auto& child : children())
588 child->flushCompositingState(rect);
591 void CoordinatedGraphicsLayer::setDebugBorder(const Color& color, float width)
593 ASSERT(m_nicosia.debugBorder.visible);
594 if (m_nicosia.debugBorder.color != color) {
595 m_nicosia.debugBorder.color = color;
596 m_nicosia.delta.debugBorderChanged = true;
599 if (m_nicosia.debugBorder.width != width) {
600 m_nicosia.debugBorder.width = width;
601 m_nicosia.delta.debugBorderChanged = true;
605 void CoordinatedGraphicsLayer::updatePlatformLayer()
607 if (!m_shouldUpdatePlatformLayer)
610 m_shouldUpdatePlatformLayer = false;
611 #if USE(COORDINATED_GRAPHICS_THREADED) && USE(NICOSIA)
612 if (m_nicosia.contentLayer)
613 downcast<Nicosia::ContentLayerTextureMapperImpl>(m_nicosia.contentLayer->impl()).swapBuffersIfNeeded();
617 void CoordinatedGraphicsLayer::flushCompositingStateForThisLayerOnly()
619 // When we have a transform animation, we need to update visible rect every frame to adjust the visible rect of a backing store.
620 bool hasActiveTransformAnimation = selfOrAncestorHasActiveTransformAnimation();
621 if (hasActiveTransformAnimation)
622 m_movingVisibleRect = true;
625 computePixelAlignment(m_adjustedPosition, m_adjustedSize, m_adjustedAnchorPoint, m_pixelAlignmentOffset);
627 computeTransformedVisibleRect();
628 updatePlatformLayer();
630 // Only unset m_movingVisibleRect after we have updated the visible rect after the animation stopped.
631 if (!hasActiveTransformAnimation)
632 m_movingVisibleRect = false;
634 // Determine the backing store presence. Content is painted later, in the updateContentBuffers() traversal.
635 if (shouldHaveBackingStore()) {
636 if (!m_nicosia.backingStore) {
637 m_nicosia.backingStore = Nicosia::BackingStore::create(Nicosia::BackingStoreTextureMapperImpl::createFactory());
638 m_nicosia.delta.backingStoreChanged = true;
640 } else if (m_nicosia.backingStore) {
641 auto& layerState = downcast<Nicosia::BackingStoreTextureMapperImpl>(m_nicosia.backingStore->impl()).layerState();
642 layerState.isPurging = true;
643 layerState.mainBackingStore = nullptr;
644 layerState.previousBackingStore = nullptr;
646 m_nicosia.backingStore = nullptr;
647 m_nicosia.delta.backingStoreChanged = true;
650 // Determine image backing presence according to the composited image source.
651 if (m_compositedNativeImagePtr) {
652 ASSERT(m_compositedImage);
653 auto& image = *m_compositedImage;
654 uintptr_t imageID = reinterpret_cast<uintptr_t>(&image);
655 uintptr_t nativeImageID = reinterpret_cast<uintptr_t>(m_compositedNativeImagePtr.get());
657 // Respawn the ImageBacking object if the underlying image changed.
658 if (m_nicosia.imageBacking) {
659 auto& impl = downcast<Nicosia::ImageBackingTextureMapperImpl>(m_nicosia.imageBacking->impl());
660 if (impl.layerState().imageID != imageID) {
661 impl.layerState().update = Nicosia::ImageBackingTextureMapperImpl::Update { };
662 m_nicosia.imageBacking = nullptr;
665 if (!m_nicosia.imageBacking) {
666 m_nicosia.imageBacking = Nicosia::ImageBacking::create(Nicosia::ImageBackingTextureMapperImpl::createFactory());
667 m_nicosia.delta.imageBackingChanged = true;
670 // Update the image contents only when the image layer is visible and the native image changed.
671 auto& impl = downcast<Nicosia::ImageBackingTextureMapperImpl>(m_nicosia.imageBacking->impl());
672 auto& layerState = impl.layerState();
673 layerState.imageID = imageID;
674 layerState.update.isVisible = transformedVisibleRect().intersects(IntRect(contentsRect()));
675 if (layerState.update.isVisible && layerState.nativeImageID != nativeImageID) {
676 auto buffer = Nicosia::Buffer::create(IntSize(image.size()),
677 !image.currentFrameKnownToBeOpaque() ? Nicosia::Buffer::SupportsAlpha : Nicosia::Buffer::NoFlags);
678 Nicosia::PaintingContext::paint(buffer,
679 [&image](GraphicsContext& context)
681 IntRect rect { { }, IntSize { image.size() } };
682 context.drawImage(image, rect, rect, ImagePaintingOptions(CompositeCopy));
684 layerState.nativeImageID = nativeImageID;
685 layerState.update.buffer = WTFMove(buffer);
686 m_nicosia.delta.imageBackingChanged = true;
688 } else if (m_nicosia.imageBacking) {
689 auto& layerState = downcast<Nicosia::ImageBackingTextureMapperImpl>(m_nicosia.imageBacking->impl()).layerState();
690 layerState.update = Nicosia::ImageBackingTextureMapperImpl::Update { };
691 m_nicosia.imageBacking = nullptr;
692 m_nicosia.delta.imageBackingChanged = true;
696 m_nicosia.layer->updateState(
697 [this](Nicosia::CompositionLayer::LayerState& state)
699 // OR the local delta value into the layer's pending state delta. After that,
700 // go through each local change and update the pending state accordingly.
701 auto& localDelta = m_nicosia.delta;
702 state.delta.value |= localDelta.value;
704 if (localDelta.positionChanged)
705 state.position = m_adjustedPosition;
706 if (localDelta.anchorPointChanged)
707 state.anchorPoint = m_adjustedAnchorPoint;
708 if (localDelta.sizeChanged)
709 state.size = m_adjustedSize;
711 if (localDelta.transformChanged)
712 state.transform = transform();
713 if (localDelta.childrenTransformChanged)
714 state.childrenTransform = childrenTransform();
716 if (localDelta.contentsRectChanged)
717 state.contentsRect = contentsRect();
718 if (localDelta.contentsTilingChanged) {
719 state.contentsTilePhase = contentsTilePhase();
720 state.contentsTileSize = contentsTileSize();
723 if (localDelta.opacityChanged)
724 state.opacity = opacity();
725 if (localDelta.solidColorChanged)
726 state.solidColor = m_solidColor;
728 if (localDelta.filtersChanged)
729 state.filters = filters();
730 if (localDelta.animationsChanged)
731 state.animations = m_animations.getActiveAnimations();
733 if (localDelta.childrenChanged) {
734 state.children = WTF::map(children(),
737 return downcast<CoordinatedGraphicsLayer>(child)->m_nicosia.layer;
741 if (localDelta.maskChanged) {
742 auto* mask = downcast<CoordinatedGraphicsLayer>(maskLayer());
743 state.mask = mask ? mask->m_nicosia.layer : nullptr;
746 if (localDelta.replicaChanged) {
747 auto* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer());
748 state.replica = replica ? replica->m_nicosia.layer : nullptr;
751 if (localDelta.flagsChanged) {
752 state.flags.contentsOpaque = contentsOpaque();
753 state.flags.drawsContent = drawsContent();
754 state.flags.contentsVisible = contentsAreVisible();
755 state.flags.backfaceVisible = backfaceVisibility();
756 state.flags.masksToBounds = masksToBounds();
757 state.flags.preserves3D = preserves3D();
760 if (localDelta.repaintCounterChanged)
761 state.repaintCounter = m_nicosia.repaintCounter;
762 if (localDelta.debugBorderChanged)
763 state.debugBorder = m_nicosia.debugBorder;
765 if (localDelta.backingStoreChanged)
766 state.backingStore = m_nicosia.backingStore;
767 if (localDelta.contentLayerChanged)
768 state.contentLayer = m_nicosia.contentLayer;
769 if (localDelta.imageBackingChanged)
770 state.imageBacking = m_nicosia.imageBacking;
772 m_nicosia.performLayerSync = !!m_nicosia.delta.value;
773 m_nicosia.delta = { };
777 void CoordinatedGraphicsLayer::syncPendingStateChangesIncludingSubLayers()
779 if (m_nicosia.performLayerSync)
780 m_coordinator->syncLayerState();
781 m_nicosia.performLayerSync = false;
784 downcast<CoordinatedGraphicsLayer>(*maskLayer()).syncPendingStateChangesIncludingSubLayers();
786 for (auto& child : children())
787 downcast<CoordinatedGraphicsLayer>(*child).syncPendingStateChangesIncludingSubLayers();
790 void CoordinatedGraphicsLayer::deviceOrPageScaleFactorChanged()
792 if (shouldHaveBackingStore())
793 m_pendingContentsScaleAdjustment = true;
796 float CoordinatedGraphicsLayer::effectiveContentsScale()
798 return selfOrAncestorHaveNonAffineTransforms() ? 1 : deviceScaleFactor() * pageScaleFactor();
801 static void clampToContentsRectIfRectIsInfinite(FloatRect& rect, const FloatSize& contentsSize)
803 if (rect.width() >= LayoutUnit::nearlyMax() || rect.width() <= LayoutUnit::nearlyMin()) {
805 rect.setWidth(contentsSize.width());
808 if (rect.height() >= LayoutUnit::nearlyMax() || rect.height() <= LayoutUnit::nearlyMin()) {
810 rect.setHeight(contentsSize.height());
814 IntRect CoordinatedGraphicsLayer::transformedVisibleRect()
816 // Non-invertible layers are not visible.
817 if (!m_layerTransform.combined().isInvertible())
820 // Return a projection of the visible rect (surface coordinates) onto the layer's plane (layer coordinates).
821 // The resulting quad might be squewed and the visible rect is the bounding box of this quad,
822 // so it might spread further than the real visible area (and then even more amplified by the cover rect multiplier).
823 ASSERT(m_cachedInverseTransform == m_layerTransform.combined().inverse().value_or(TransformationMatrix()));
824 FloatRect rect = m_cachedInverseTransform.clampedBoundsOfProjectedQuad(FloatQuad(m_coordinator->visibleContentsRect()));
825 clampToContentsRectIfRectIsInfinite(rect, size());
826 return enclosingIntRect(rect);
829 void CoordinatedGraphicsLayer::updateContentBuffersIncludingSubLayers()
831 if (CoordinatedGraphicsLayer* mask = downcast<CoordinatedGraphicsLayer>(maskLayer()))
832 mask->updateContentBuffers();
834 if (CoordinatedGraphicsLayer* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer()))
835 replica->updateContentBuffers();
837 updateContentBuffers();
839 for (auto& child : children())
840 downcast<CoordinatedGraphicsLayer>(*child).updateContentBuffersIncludingSubLayers();
843 void CoordinatedGraphicsLayer::updateContentBuffers()
845 if (!m_nicosia.backingStore)
848 // Prepare for painting on the impl-contained backing store. isFlushing is used there
849 // for internal sanity checks.
850 auto& impl = downcast<Nicosia::BackingStoreTextureMapperImpl>(m_nicosia.backingStore->impl());
851 auto& layerState = impl.layerState();
852 layerState.isFlushing = true;
854 // Helper lambda that finished the flush update and determines layer sync necessity.
856 [this, &layerState] {
857 auto& update = layerState.update;
858 m_nicosia.performLayerSync = !update.tilesToCreate.isEmpty()
859 || !update.tilesToRemove.isEmpty() || !update.tilesToUpdate.isEmpty();
860 layerState.isFlushing = false;
863 // Address the content scale adjustment.
864 // FIXME: the previousBackingStore logic is likely possible to remove.
865 // https://bugs.webkit.org/show_bug.cgi?id=188693
866 if (m_pendingContentsScaleAdjustment) {
867 if (layerState.mainBackingStore && layerState.mainBackingStore->contentsScale() != effectiveContentsScale()) {
868 // Between creating the new backing store and painting the content, we do not
869 // want to drop the previous one as that might result in briefly seeing flickering
870 // as the old tiles may be dropped before something replaces them.
871 layerState.previousBackingStore = WTFMove(layerState.mainBackingStore);
873 // No reason to save the previous backing store for non-visible areas.
874 layerState.previousBackingStore->removeAllNonVisibleTiles(transformedVisibleRect(), IntRect(0, 0, size().width(), size().height()));
876 m_pendingContentsScaleAdjustment = false;
879 // Ensure the TiledBackingStore object, and enforce a complete repaint if it's not been present yet.
880 if (!layerState.mainBackingStore) {
881 layerState.mainBackingStore = std::make_unique<TiledBackingStore>(impl, effectiveContentsScale());
882 m_pendingVisibleRectAdjustment = true;
885 // Bail if there's no painting recorded or enforced.
886 if (!m_pendingVisibleRectAdjustment && !m_needsDisplay.completeLayer && m_needsDisplay.rects.isEmpty()) {
891 if (!m_needsDisplay.completeLayer) {
892 for (auto& rect : m_needsDisplay.rects)
893 layerState.mainBackingStore->invalidate(IntRect { rect });
895 layerState.mainBackingStore->invalidate({ { }, IntSize { m_size } });
897 m_needsDisplay.completeLayer = false;
898 m_needsDisplay.rects.clear();
900 if (m_pendingVisibleRectAdjustment) {
901 m_pendingVisibleRectAdjustment = false;
902 layerState.mainBackingStore->createTilesIfNeeded(transformedVisibleRect(), IntRect(0, 0, m_size.width(), m_size.height()));
905 ASSERT(m_coordinator && m_coordinator->isFlushingLayerChanges());
907 // With all the affected tiles created and/or invalidated, we can finally paint them.
908 auto dirtyTiles = layerState.mainBackingStore->dirtyTiles();
909 if (!dirtyTiles.isEmpty()) {
910 bool didUpdateTiles = false;
912 for (auto& tileReference : dirtyTiles) {
913 auto& tile = tileReference.get();
916 auto& tileRect = tile.rect();
917 auto& dirtyRect = tile.dirtyRect();
919 auto coordinatedBuffer = Nicosia::Buffer::create(dirtyRect.size(), contentsOpaque() ? Nicosia::Buffer::NoFlags : Nicosia::Buffer::SupportsAlpha);
920 SurfaceUpdateInfo updateInfo;
921 updateInfo.updateRect = dirtyRect;
922 updateInfo.updateRect.move(-tileRect.x(), -tileRect.y());
923 updateInfo.buffer = coordinatedBuffer.copyRef();
925 if (!m_coordinator->paintingEngine().paint(*this, WTFMove(coordinatedBuffer),
926 dirtyRect, layerState.mainBackingStore->mapToContents(dirtyRect),
927 IntRect { { 0, 0 }, dirtyRect.size() }, layerState.mainBackingStore->contentsScale()))
930 impl.updateTile(tile.tileID(), updateInfo, tileRect);
933 didUpdateTiles |= true;
937 didUpdateTileBuffers();
940 // The previous backing store is kept around to avoid flickering between
941 // removing the existing tiles and painting the new ones. The first time
942 // the visibleRect is full painted we remove the previous backing store.
943 if (layerState.previousBackingStore && layerState.mainBackingStore->visibleAreaIsCovered())
944 layerState.previousBackingStore = nullptr;
946 // Request a second update immediately if some tiles are still pending creation.
947 if (layerState.hasPendingTileCreation) {
948 setNeedsVisibleRectAdjustment();
949 notifyFlushRequired();
955 void CoordinatedGraphicsLayer::purgeBackingStores()
958 SetForScope<bool> updateModeProtector(m_isPurging, true);
960 if (m_nicosia.backingStore) {
961 auto& layerState = downcast<Nicosia::BackingStoreTextureMapperImpl>(m_nicosia.backingStore->impl()).layerState();
962 layerState.isPurging = true;
963 layerState.mainBackingStore = nullptr;
964 layerState.previousBackingStore = nullptr;
966 m_nicosia.backingStore = nullptr;
969 if (m_nicosia.imageBacking) {
970 auto& layerState = downcast<Nicosia::ImageBackingTextureMapperImpl>(m_nicosia.imageBacking->impl()).layerState();
971 layerState.imageID = 0;
972 layerState.nativeImageID = 0;
973 layerState.update = { };
975 m_nicosia.imageBacking = nullptr;
978 notifyFlushRequired();
981 void CoordinatedGraphicsLayer::setCoordinator(CoordinatedGraphicsLayerClient* coordinator)
983 m_coordinator = coordinator;
986 void CoordinatedGraphicsLayer::setCoordinatorIncludingSubLayersIfNeeded(CoordinatedGraphicsLayerClient* coordinator)
988 if (m_coordinator == coordinator)
991 // If the coordinators are different it means that we are attaching a layer that was created by a different
992 // CompositingCoordinator than the current one. This happens because the layer was taken out of the tree
993 // and then added back after AC was disabled and enabled again. We need to set the new coordinator to the
994 // layer and its children.
996 // During each layer flush, the state stores the values that have changed since the previous one, and these
997 // are updated once in the scene. When adding CoordinatedGraphicsLayers back to the tree, the fields that
998 // are not updated during the next flush won't be sent to the scene, so they won't be updated there and the
999 // rendering will fail.
1001 // For example the drawsContent flag. This is set when the layer is created and is not updated anymore (unless
1002 // the content changes). When the layer is added back to the tree, the state won't reflect any change in the
1003 // flag value, so the scene won't update it and the layer won't be rendered.
1005 // We need to update here the layer changeMask so the scene gets all the current values.
1006 m_nicosia.delta.value = UINT_MAX;
1008 coordinator->attachLayer(this);
1009 for (auto& child : children())
1010 downcast<CoordinatedGraphicsLayer>(*child).setCoordinatorIncludingSubLayersIfNeeded(coordinator);
1013 const RefPtr<Nicosia::CompositionLayer>& CoordinatedGraphicsLayer::compositionLayer() const
1015 return m_nicosia.layer;
1018 void CoordinatedGraphicsLayer::setNeedsVisibleRectAdjustment()
1020 if (shouldHaveBackingStore())
1021 m_pendingVisibleRectAdjustment = true;
1024 static inline bool isIntegral(float value)
1026 return static_cast<int>(value) == value;
1029 FloatPoint CoordinatedGraphicsLayer::computePositionRelativeToBase()
1032 for (const GraphicsLayer* currLayer = this; currLayer; currLayer = currLayer->parent())
1033 offset += currLayer->position();
1038 void CoordinatedGraphicsLayer::computePixelAlignment(FloatPoint& position, FloatSize& size, FloatPoint3D& anchorPoint, FloatSize& alignmentOffset)
1040 if (isIntegral(effectiveContentsScale())) {
1041 position = m_position;
1043 anchorPoint = m_anchorPoint;
1044 alignmentOffset = FloatSize();
1048 FloatPoint positionRelativeToBase = computePositionRelativeToBase();
1050 FloatRect baseRelativeBounds(positionRelativeToBase, m_size);
1051 FloatRect scaledBounds = baseRelativeBounds;
1053 // Scale by the effective scale factor to compute the screen-relative bounds.
1054 scaledBounds.scale(effectiveContentsScale());
1056 // Round to integer boundaries.
1057 // NOTE: When using enclosingIntRect (as mac) it will have different sizes depending on position.
1058 FloatRect alignedBounds = enclosingIntRect(scaledBounds);
1060 // Convert back to layer coordinates.
1061 alignedBounds.scale(1 / effectiveContentsScale());
1063 // Convert back to layer coordinates.
1064 alignmentOffset = baseRelativeBounds.location() - alignedBounds.location();
1066 position = m_position - alignmentOffset;
1067 size = alignedBounds.size();
1069 // Now we have to compute a new anchor point which compensates for rounding.
1070 float anchorPointX = m_anchorPoint.x();
1071 float anchorPointY = m_anchorPoint.y();
1073 if (alignedBounds.width())
1074 anchorPointX = (baseRelativeBounds.width() * anchorPointX + alignmentOffset.width()) / alignedBounds.width();
1076 if (alignedBounds.height())
1077 anchorPointY = (baseRelativeBounds.height() * anchorPointY + alignmentOffset.height()) / alignedBounds.height();
1079 anchorPoint = FloatPoint3D(anchorPointX, anchorPointY, m_anchorPoint.z() * effectiveContentsScale());
1082 void CoordinatedGraphicsLayer::computeTransformedVisibleRect()
1084 if (!m_shouldUpdateVisibleRect && !m_movingVisibleRect)
1087 m_shouldUpdateVisibleRect = false;
1088 TransformationMatrix currentTransform = transform();
1089 if (m_movingVisibleRect)
1090 client().getCurrentTransform(this, currentTransform);
1091 m_layerTransform.setLocalTransform(currentTransform);
1093 m_layerTransform.setAnchorPoint(m_adjustedAnchorPoint);
1094 m_layerTransform.setPosition(m_adjustedPosition);
1095 m_layerTransform.setSize(m_adjustedSize);
1097 m_layerTransform.setFlattening(!preserves3D());
1098 m_layerTransform.setChildrenTransform(childrenTransform());
1099 m_layerTransform.combineTransforms(parent() ? downcast<CoordinatedGraphicsLayer>(*parent()).m_layerTransform.combinedForChildren() : TransformationMatrix());
1101 m_cachedInverseTransform = m_layerTransform.combined().inverse().value_or(TransformationMatrix());
1103 // The combined transform will be used in tiledBackingStoreVisibleRect.
1104 setNeedsVisibleRectAdjustment();
1107 bool CoordinatedGraphicsLayer::shouldHaveBackingStore() const
1109 return drawsContent() && contentsAreVisible() && !m_size.isEmpty();
1112 bool CoordinatedGraphicsLayer::selfOrAncestorHasActiveTransformAnimation() const
1114 if (m_animations.hasActiveAnimationsOfType(AnimatedPropertyTransform))
1120 return downcast<CoordinatedGraphicsLayer>(*parent()).selfOrAncestorHasActiveTransformAnimation();
1123 bool CoordinatedGraphicsLayer::selfOrAncestorHaveNonAffineTransforms()
1125 if (!m_layerTransform.combined().isAffine())
1131 return downcast<CoordinatedGraphicsLayer>(*parent()).selfOrAncestorHaveNonAffineTransforms();
1134 bool CoordinatedGraphicsLayer::addAnimation(const KeyframeValueList& valueList, const FloatSize& boxSize, const Animation* anim, const String& keyframesName, double delayAsNegativeTimeOffset)
1136 ASSERT(!keyframesName.isEmpty());
1138 if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2 || (valueList.property() != AnimatedPropertyTransform && valueList.property() != AnimatedPropertyOpacity && valueList.property() != AnimatedPropertyFilter))
1141 if (valueList.property() == AnimatedPropertyFilter) {
1142 int listIndex = validateFilterOperations(valueList);
1146 const auto& filters = static_cast<const FilterAnimationValue&>(valueList.at(listIndex)).value();
1147 if (!filtersCanBeComposited(filters))
1151 bool listsMatch = false;
1152 bool ignoredHasBigRotation;
1154 if (valueList.property() == AnimatedPropertyTransform)
1155 listsMatch = validateTransformOperations(valueList, ignoredHasBigRotation) >= 0;
1157 m_lastAnimationStartTime = MonotonicTime::now() - Seconds(delayAsNegativeTimeOffset);
1158 m_animations.add(TextureMapperAnimation(keyframesName, valueList, boxSize, *anim, listsMatch, m_lastAnimationStartTime, 0_s, TextureMapperAnimation::AnimationState::Playing));
1159 m_animationStartedTimer.startOneShot(0_s);
1160 didChangeAnimations();
1164 void CoordinatedGraphicsLayer::pauseAnimation(const String& animationName, double time)
1166 m_animations.pause(animationName, Seconds(time));
1167 didChangeAnimations();
1170 void CoordinatedGraphicsLayer::removeAnimation(const String& animationName)
1172 m_animations.remove(animationName);
1173 didChangeAnimations();
1176 void CoordinatedGraphicsLayer::suspendAnimations(MonotonicTime time)
1178 m_animations.suspend(time);
1179 didChangeAnimations();
1182 void CoordinatedGraphicsLayer::resumeAnimations()
1184 m_animations.resume();
1185 didChangeAnimations();
1188 void CoordinatedGraphicsLayer::animationStartedTimerFired()
1190 client().notifyAnimationStarted(this, "", m_lastAnimationStartTime);
1193 bool CoordinatedGraphicsLayer::usesContentsLayer() const
1195 return m_nicosia.contentLayer || m_compositedImage;
1198 } // namespace WebCore
1200 #endif // USE(COORDINATED_GRAPHICS)