1/*
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.
7
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.
12
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.
17
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.
22 */
23
24#include "config.h"
25#include "CoordinatedGraphicsLayer.h"
26
27#if USE(COORDINATED_GRAPHICS)
28
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"
42#ifndef NDEBUG
43#include <wtf/SetForScope.h>
44#endif
45#include <wtf/text/CString.h>
46
47#if USE(GLIB_EVENT_LOOP)
48#include <wtf/glib/RunLoopSourcePriority.h>
49#endif
50
51namespace WebCore {
52
53Ref<GraphicsLayer> GraphicsLayer::create(GraphicsLayerFactory* factory, GraphicsLayerClient& client, Type layerType)
54{
55 if (!factory)
56 return adoptRef(*new CoordinatedGraphicsLayer(layerType, client));
57
58 return factory->createGraphicsLayer(layerType, client);
59}
60
61void CoordinatedGraphicsLayer::notifyFlushRequired()
62{
63 if (!m_coordinator)
64 return;
65
66 if (m_coordinator->isFlushingLayerChanges())
67 return;
68
69 client().notifyFlushRequired(this);
70}
71
72void CoordinatedGraphicsLayer::didChangeAnimations()
73{
74 m_nicosia.delta.animationsChanged = true;
75 notifyFlushRequired();
76}
77
78void CoordinatedGraphicsLayer::didChangeChildren()
79{
80 m_nicosia.delta.childrenChanged = true;
81 notifyFlushRequired();
82}
83
84void CoordinatedGraphicsLayer::didChangeFilters()
85{
86 m_nicosia.delta.filtersChanged = true;
87 notifyFlushRequired();
88}
89
90void CoordinatedGraphicsLayer::didUpdateTileBuffers()
91{
92 if (!isShowingRepaintCounter())
93 return;
94
95 auto repaintCount = incrementRepaintCount();
96 m_nicosia.repaintCounter.count = repaintCount;
97 m_nicosia.delta.repaintCounterChanged = true;
98}
99
100void CoordinatedGraphicsLayer::setShouldUpdateVisibleRect()
101{
102 m_shouldUpdateVisibleRect = true;
103 for (auto& child : children())
104 downcast<CoordinatedGraphicsLayer>(child.get()).setShouldUpdateVisibleRect();
105 if (replicaLayer())
106 downcast<CoordinatedGraphicsLayer>(*replicaLayer()).setShouldUpdateVisibleRect();
107}
108
109void CoordinatedGraphicsLayer::didChangeGeometry()
110{
111 notifyFlushRequired();
112 setShouldUpdateVisibleRect();
113}
114
115CoordinatedGraphicsLayer::CoordinatedGraphicsLayer(Type layerType, GraphicsLayerClient& client)
116 : GraphicsLayer(layerType, client)
117#ifndef NDEBUG
118 , m_isPurging(false)
119#endif
120 , m_shouldUpdateVisibleRect(true)
121 , m_movingVisibleRect(false)
122 , m_pendingContentsScaleAdjustment(false)
123 , m_pendingVisibleRectAdjustment(false)
124 , m_shouldUpdatePlatformLayer(false)
125 , m_coordinator(0)
126 , m_compositedNativeImagePtr(0)
127 , m_animationStartedTimer(*this, &CoordinatedGraphicsLayer::animationStartedTimerFired)
128 , m_requestPendingTileCreationTimer(RunLoop::main(), this, &CoordinatedGraphicsLayer::requestPendingTileCreationTimerFired)
129{
130 static Nicosia::PlatformLayer::LayerID nextLayerID = 1;
131 m_id = nextLayerID++;
132
133 m_nicosia.layer = Nicosia::CompositionLayer::create(m_id,
134 Nicosia::CompositionLayerTextureMapperImpl::createFactory());
135
136 // Enforce a complete flush on the first occasion.
137 m_nicosia.delta.value = UINT_MAX;
138
139#if USE(GLIB_EVENT_LOOP)
140 m_requestPendingTileCreationTimer.setPriority(RunLoopSourcePriority::LayerFlushTimer);
141#endif
142}
143
144CoordinatedGraphicsLayer::~CoordinatedGraphicsLayer()
145{
146 if (m_coordinator) {
147 purgeBackingStores();
148 m_coordinator->detachLayer(this);
149 }
150 ASSERT(!m_nicosia.imageBacking);
151 ASSERT(!m_nicosia.backingStore);
152 willBeDestroyed();
153}
154
155bool CoordinatedGraphicsLayer::isCoordinatedGraphicsLayer() const
156{
157 return true;
158}
159
160Nicosia::PlatformLayer::LayerID CoordinatedGraphicsLayer::id() const
161{
162 return m_id;
163}
164
165auto CoordinatedGraphicsLayer::primaryLayerID() const -> PlatformLayerID
166{
167 return id();
168}
169
170bool CoordinatedGraphicsLayer::setChildren(Vector<Ref<GraphicsLayer>>&& children)
171{
172 bool ok = GraphicsLayer::setChildren(WTFMove(children));
173 if (!ok)
174 return false;
175 didChangeChildren();
176 return true;
177}
178
179void CoordinatedGraphicsLayer::addChild(Ref<GraphicsLayer>&& layer)
180{
181 GraphicsLayer* rawLayer = layer.ptr();
182 GraphicsLayer::addChild(WTFMove(layer));
183 downcast<CoordinatedGraphicsLayer>(*rawLayer).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
184 didChangeChildren();
185}
186
187void CoordinatedGraphicsLayer::addChildAtIndex(Ref<GraphicsLayer>&& layer, int index)
188{
189 GraphicsLayer* rawLayer = layer.ptr();
190 GraphicsLayer::addChildAtIndex(WTFMove(layer), index);
191 downcast<CoordinatedGraphicsLayer>(*rawLayer).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
192 didChangeChildren();
193}
194
195void CoordinatedGraphicsLayer::addChildAbove(Ref<GraphicsLayer>&& layer, GraphicsLayer* sibling)
196{
197 GraphicsLayer* rawLayer = layer.ptr();
198 GraphicsLayer::addChildAbove(WTFMove(layer), sibling);
199 downcast<CoordinatedGraphicsLayer>(*rawLayer).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
200 didChangeChildren();
201}
202
203void CoordinatedGraphicsLayer::addChildBelow(Ref<GraphicsLayer>&& layer, GraphicsLayer* sibling)
204{
205 GraphicsLayer* rawLayer = layer.ptr();
206 GraphicsLayer::addChildBelow(WTFMove(layer), sibling);
207 downcast<CoordinatedGraphicsLayer>(*rawLayer).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
208 didChangeChildren();
209}
210
211bool CoordinatedGraphicsLayer::replaceChild(GraphicsLayer* oldChild, Ref<GraphicsLayer>&& newChild)
212{
213 GraphicsLayer* rawLayer = newChild.ptr();
214 bool ok = GraphicsLayer::replaceChild(oldChild, WTFMove(newChild));
215 if (!ok)
216 return false;
217 downcast<CoordinatedGraphicsLayer>(*rawLayer).setCoordinatorIncludingSubLayersIfNeeded(m_coordinator);
218 didChangeChildren();
219 return true;
220}
221
222void CoordinatedGraphicsLayer::removeFromParent()
223{
224 if (CoordinatedGraphicsLayer* parentLayer = downcast<CoordinatedGraphicsLayer>(parent()))
225 parentLayer->didChangeChildren();
226 GraphicsLayer::removeFromParent();
227}
228
229void CoordinatedGraphicsLayer::setPosition(const FloatPoint& p)
230{
231 if (position() == p)
232 return;
233
234 GraphicsLayer::setPosition(p);
235 m_nicosia.delta.positionChanged = true;
236 didChangeGeometry();
237}
238
239void CoordinatedGraphicsLayer::setAnchorPoint(const FloatPoint3D& p)
240{
241 if (anchorPoint() == p)
242 return;
243
244 GraphicsLayer::setAnchorPoint(p);
245 m_nicosia.delta.anchorPointChanged = true;
246 didChangeGeometry();
247}
248
249void CoordinatedGraphicsLayer::setSize(const FloatSize& size)
250{
251 if (this->size() == size)
252 return;
253
254 GraphicsLayer::setSize(size);
255 m_nicosia.delta.sizeChanged = true;
256
257 if (maskLayer())
258 maskLayer()->setSize(size);
259 didChangeGeometry();
260}
261
262void CoordinatedGraphicsLayer::setTransform(const TransformationMatrix& t)
263{
264 if (transform() == t)
265 return;
266
267 GraphicsLayer::setTransform(t);
268 m_nicosia.delta.transformChanged = true;
269
270 didChangeGeometry();
271}
272
273void CoordinatedGraphicsLayer::setChildrenTransform(const TransformationMatrix& t)
274{
275 if (childrenTransform() == t)
276 return;
277
278 GraphicsLayer::setChildrenTransform(t);
279 m_nicosia.delta.childrenTransformChanged = true;
280
281 didChangeGeometry();
282}
283
284void CoordinatedGraphicsLayer::setPreserves3D(bool b)
285{
286 if (preserves3D() == b)
287 return;
288
289 GraphicsLayer::setPreserves3D(b);
290 m_nicosia.delta.flagsChanged = true;
291
292 didChangeGeometry();
293}
294
295void CoordinatedGraphicsLayer::setMasksToBounds(bool b)
296{
297 if (masksToBounds() == b)
298 return;
299 GraphicsLayer::setMasksToBounds(b);
300 m_nicosia.delta.flagsChanged = true;
301
302 didChangeGeometry();
303}
304
305void CoordinatedGraphicsLayer::setDrawsContent(bool b)
306{
307 if (drawsContent() == b)
308 return;
309 GraphicsLayer::setDrawsContent(b);
310 m_nicosia.delta.flagsChanged = true;
311
312 notifyFlushRequired();
313}
314
315void CoordinatedGraphicsLayer::setContentsVisible(bool b)
316{
317 if (contentsAreVisible() == b)
318 return;
319 GraphicsLayer::setContentsVisible(b);
320 m_nicosia.delta.flagsChanged = true;
321
322 if (maskLayer())
323 maskLayer()->setContentsVisible(b);
324
325 notifyFlushRequired();
326}
327
328void CoordinatedGraphicsLayer::setContentsOpaque(bool b)
329{
330 if (contentsOpaque() == b)
331 return;
332
333 GraphicsLayer::setContentsOpaque(b);
334 m_nicosia.delta.flagsChanged = true;
335
336 // Demand a repaint of the whole layer.
337 if (!m_needsDisplay.completeLayer) {
338 m_needsDisplay.completeLayer = true;
339 m_needsDisplay.rects.clear();
340
341 addRepaintRect({ { }, m_size });
342 }
343
344 notifyFlushRequired();
345}
346
347void CoordinatedGraphicsLayer::setBackfaceVisibility(bool b)
348{
349 if (backfaceVisibility() == b)
350 return;
351
352 GraphicsLayer::setBackfaceVisibility(b);
353 m_nicosia.delta.flagsChanged = true;
354
355 notifyFlushRequired();
356}
357
358void CoordinatedGraphicsLayer::setOpacity(float opacity)
359{
360 if (this->opacity() == opacity)
361 return;
362
363 GraphicsLayer::setOpacity(opacity);
364 m_nicosia.delta.opacityChanged = true;
365
366 notifyFlushRequired();
367}
368
369void CoordinatedGraphicsLayer::setContentsRect(const FloatRect& r)
370{
371 if (contentsRect() == r)
372 return;
373
374 GraphicsLayer::setContentsRect(r);
375 m_nicosia.delta.contentsRectChanged = true;
376
377 notifyFlushRequired();
378}
379
380void CoordinatedGraphicsLayer::setContentsTileSize(const FloatSize& s)
381{
382 if (contentsTileSize() == s)
383 return;
384
385 GraphicsLayer::setContentsTileSize(s);
386 m_nicosia.delta.contentsTilingChanged = true;
387 notifyFlushRequired();
388}
389
390void CoordinatedGraphicsLayer::setContentsTilePhase(const FloatSize& p)
391{
392 if (contentsTilePhase() == p)
393 return;
394
395 GraphicsLayer::setContentsTilePhase(p);
396 m_nicosia.delta.contentsTilingChanged = true;
397 notifyFlushRequired();
398}
399
400bool GraphicsLayer::supportsContentsTiling()
401{
402 return true;
403}
404
405void CoordinatedGraphicsLayer::setContentsNeedsDisplay()
406{
407#if USE(COORDINATED_GRAPHICS) && USE(NICOSIA)
408 if (m_nicosia.contentLayer)
409 m_shouldUpdatePlatformLayer = true;
410#endif
411
412 notifyFlushRequired();
413 addRepaintRect(contentsRect());
414}
415
416void CoordinatedGraphicsLayer::setContentsToPlatformLayer(PlatformLayer* platformLayer, ContentsLayerPurpose)
417{
418#if USE(COORDINATED_GRAPHICS) && USE(NICOSIA)
419 auto* contentLayer = downcast<Nicosia::ContentLayer>(platformLayer);
420 if (m_nicosia.contentLayer != contentLayer) {
421 m_nicosia.contentLayer = contentLayer;
422 m_nicosia.delta.contentLayerChanged = true;
423 if (m_nicosia.contentLayer)
424 m_shouldUpdatePlatformLayer = true;
425 }
426 notifyFlushRequired();
427#else
428 UNUSED_PARAM(platformLayer);
429#endif
430}
431
432bool CoordinatedGraphicsLayer::filtersCanBeComposited(const FilterOperations& filters) const
433{
434 if (!filters.size())
435 return false;
436
437 for (const auto& filterOperation : filters.operations()) {
438 if (filterOperation->type() == FilterOperation::REFERENCE)
439 return false;
440 }
441
442 return true;
443}
444
445bool CoordinatedGraphicsLayer::setFilters(const FilterOperations& newFilters)
446{
447 bool canCompositeFilters = filtersCanBeComposited(newFilters);
448 if (filters() == newFilters)
449 return canCompositeFilters;
450
451 if (canCompositeFilters) {
452 if (!GraphicsLayer::setFilters(newFilters))
453 return false;
454 didChangeFilters();
455 } else if (filters().size()) {
456 clearFilters();
457 didChangeFilters();
458 }
459
460 return canCompositeFilters;
461}
462
463void CoordinatedGraphicsLayer::setContentsToSolidColor(const Color& color)
464{
465 if (m_solidColor == color)
466 return;
467
468 m_solidColor = color;
469 m_nicosia.delta.solidColorChanged = true;
470
471 notifyFlushRequired();
472}
473
474void CoordinatedGraphicsLayer::setShowDebugBorder(bool show)
475{
476 if (isShowingDebugBorder() == show)
477 return;
478
479 GraphicsLayer::setShowDebugBorder(show);
480 m_nicosia.debugBorder.visible = show;
481 m_nicosia.delta.debugBorderChanged = true;
482
483 if (m_nicosia.debugBorder.visible)
484 updateDebugIndicators();
485
486 notifyFlushRequired();
487}
488
489void CoordinatedGraphicsLayer::setShowRepaintCounter(bool show)
490{
491 if (isShowingRepaintCounter() == show)
492 return;
493
494 GraphicsLayer::setShowRepaintCounter(show);
495 m_nicosia.repaintCounter.visible = show;
496 m_nicosia.delta.repaintCounterChanged = true;
497
498 notifyFlushRequired();
499}
500
501void CoordinatedGraphicsLayer::setContentsToImage(Image* image)
502{
503 NativeImagePtr nativeImagePtr = image ? image->nativeImageForCurrentFrame() : nullptr;
504 if (m_compositedImage == image && m_compositedNativeImagePtr == nativeImagePtr)
505 return;
506
507 m_compositedImage = image;
508 m_compositedNativeImagePtr = nativeImagePtr;
509
510 GraphicsLayer::setContentsToImage(image);
511 notifyFlushRequired();
512}
513
514void CoordinatedGraphicsLayer::setMaskLayer(RefPtr<GraphicsLayer>&& layer)
515{
516 if (layer == maskLayer())
517 return;
518
519 GraphicsLayer* rawLayer = layer.get();
520 GraphicsLayer::setMaskLayer(WTFMove(layer));
521
522 if (!rawLayer)
523 return;
524
525 rawLayer->setSize(size());
526 rawLayer->setContentsVisible(contentsAreVisible());
527
528 m_nicosia.delta.maskChanged = true;
529
530 notifyFlushRequired();
531}
532
533bool CoordinatedGraphicsLayer::shouldDirectlyCompositeImage(Image* image) const
534{
535 if (!image || !image->isBitmapImage())
536 return false;
537
538 enum { MaxDimenstionForDirectCompositing = 2000 };
539 if (image->width() > MaxDimenstionForDirectCompositing || image->height() > MaxDimenstionForDirectCompositing)
540 return false;
541
542 return true;
543}
544
545void CoordinatedGraphicsLayer::setReplicatedByLayer(RefPtr<GraphicsLayer>&& layer)
546{
547 if (layer == replicaLayer())
548 return;
549
550 GraphicsLayer::setReplicatedByLayer(WTFMove(layer));
551 m_nicosia.delta.replicaChanged = true;
552 notifyFlushRequired();
553}
554
555void CoordinatedGraphicsLayer::setNeedsDisplay()
556{
557 if (!drawsContent() || !contentsAreVisible() || m_size.isEmpty() || m_needsDisplay.completeLayer)
558 return;
559
560 m_needsDisplay.completeLayer = true;
561 m_needsDisplay.rects.clear();
562
563 notifyFlushRequired();
564 addRepaintRect({ { }, m_size });
565}
566
567void CoordinatedGraphicsLayer::setNeedsDisplayInRect(const FloatRect& initialRect, ShouldClipToLayer shouldClip)
568{
569 if (!drawsContent() || !contentsAreVisible() || m_size.isEmpty() || m_needsDisplay.completeLayer)
570 return;
571
572 auto rect = initialRect;
573 if (shouldClip == ClipToLayer)
574 rect.intersect({ { }, m_size });
575
576 if (rect.isEmpty())
577 return;
578
579 auto& rects = m_needsDisplay.rects;
580 bool alreadyRecorded = std::any_of(rects.begin(), rects.end(),
581 [&](auto& dirtyRect) { return dirtyRect.contains(rect); });
582 if (alreadyRecorded)
583 return;
584
585 if (rects.size() < 32)
586 rects.append(rect);
587 else
588 rects[0].unite(rect);
589
590 notifyFlushRequired();
591 addRepaintRect(rect);
592}
593
594void CoordinatedGraphicsLayer::flushCompositingState(const FloatRect& rect)
595{
596 if (CoordinatedGraphicsLayer* mask = downcast<CoordinatedGraphicsLayer>(maskLayer()))
597 mask->flushCompositingStateForThisLayerOnly();
598
599 if (CoordinatedGraphicsLayer* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer()))
600 replica->flushCompositingStateForThisLayerOnly();
601
602 flushCompositingStateForThisLayerOnly();
603
604 for (auto& child : children())
605 child->flushCompositingState(rect);
606}
607
608void CoordinatedGraphicsLayer::setDebugBorder(const Color& color, float width)
609{
610 ASSERT(m_nicosia.debugBorder.visible);
611 if (m_nicosia.debugBorder.color != color) {
612 m_nicosia.debugBorder.color = color;
613 m_nicosia.delta.debugBorderChanged = true;
614 }
615
616 if (m_nicosia.debugBorder.width != width) {
617 m_nicosia.debugBorder.width = width;
618 m_nicosia.delta.debugBorderChanged = true;
619 }
620}
621
622void CoordinatedGraphicsLayer::updatePlatformLayer()
623{
624 if (!m_shouldUpdatePlatformLayer)
625 return;
626
627 m_shouldUpdatePlatformLayer = false;
628#if USE(COORDINATED_GRAPHICS) && USE(NICOSIA)
629 if (m_nicosia.contentLayer)
630 downcast<Nicosia::ContentLayerTextureMapperImpl>(m_nicosia.contentLayer->impl()).swapBuffersIfNeeded();
631#endif
632}
633
634void CoordinatedGraphicsLayer::flushCompositingStateForThisLayerOnly()
635{
636 // Whether it kicked or not, we don't need this timer running anymore.
637 m_requestPendingTileCreationTimer.stop();
638
639 // When we have a transform animation, we need to update visible rect every frame to adjust the visible rect of a backing store.
640 bool hasActiveTransformAnimation = selfOrAncestorHasActiveTransformAnimation();
641 if (hasActiveTransformAnimation)
642 m_movingVisibleRect = true;
643
644 // Sets the values.
645 computePixelAlignment(m_adjustedPosition, m_adjustedSize, m_adjustedAnchorPoint, m_pixelAlignmentOffset);
646
647 computeTransformedVisibleRect();
648 updatePlatformLayer();
649
650 // Only unset m_movingVisibleRect after we have updated the visible rect after the animation stopped.
651 if (!hasActiveTransformAnimation)
652 m_movingVisibleRect = false;
653
654 // Determine the backing store presence. Content is painted later, in the updateContentBuffers() traversal.
655 if (shouldHaveBackingStore()) {
656 if (!m_nicosia.backingStore) {
657 m_nicosia.backingStore = Nicosia::BackingStore::create(Nicosia::BackingStoreTextureMapperImpl::createFactory());
658 m_nicosia.delta.backingStoreChanged = true;
659 }
660 } else if (m_nicosia.backingStore) {
661 auto& layerState = downcast<Nicosia::BackingStoreTextureMapperImpl>(m_nicosia.backingStore->impl()).layerState();
662 layerState.isPurging = true;
663 layerState.mainBackingStore = nullptr;
664
665 m_nicosia.backingStore = nullptr;
666 m_nicosia.delta.backingStoreChanged = true;
667 }
668
669 // Determine image backing presence according to the composited image source.
670 if (m_compositedNativeImagePtr) {
671 ASSERT(m_compositedImage);
672 auto& image = *m_compositedImage;
673 uintptr_t imageID = reinterpret_cast<uintptr_t>(&image);
674 uintptr_t nativeImageID = reinterpret_cast<uintptr_t>(m_compositedNativeImagePtr.get());
675
676 // Respawn the ImageBacking object if the underlying image changed.
677 if (m_nicosia.imageBacking) {
678 auto& impl = downcast<Nicosia::ImageBackingTextureMapperImpl>(m_nicosia.imageBacking->impl());
679 if (impl.layerState().imageID != imageID) {
680 impl.layerState().update = Nicosia::ImageBackingTextureMapperImpl::Update { };
681 m_nicosia.imageBacking = nullptr;
682 }
683 }
684 if (!m_nicosia.imageBacking) {
685 m_nicosia.imageBacking = Nicosia::ImageBacking::create(Nicosia::ImageBackingTextureMapperImpl::createFactory());
686 m_nicosia.delta.imageBackingChanged = true;
687 }
688
689 // Update the image contents only when the image layer is visible and the native image changed.
690 auto& impl = downcast<Nicosia::ImageBackingTextureMapperImpl>(m_nicosia.imageBacking->impl());
691 auto& layerState = impl.layerState();
692 layerState.imageID = imageID;
693 layerState.update.isVisible = transformedVisibleRect().intersects(IntRect(contentsRect()));
694 if (layerState.update.isVisible && layerState.nativeImageID != nativeImageID) {
695 auto buffer = Nicosia::Buffer::create(IntSize(image.size()),
696 !image.currentFrameKnownToBeOpaque() ? Nicosia::Buffer::SupportsAlpha : Nicosia::Buffer::NoFlags);
697 Nicosia::PaintingContext::paint(buffer,
698 [&image](GraphicsContext& context)
699 {
700 IntRect rect { { }, IntSize { image.size() } };
701 context.drawImage(image, rect, rect, ImagePaintingOptions(CompositeCopy));
702 });
703 layerState.nativeImageID = nativeImageID;
704 layerState.update.buffer = WTFMove(buffer);
705 m_nicosia.delta.imageBackingChanged = true;
706 }
707 } else if (m_nicosia.imageBacking) {
708 auto& layerState = downcast<Nicosia::ImageBackingTextureMapperImpl>(m_nicosia.imageBacking->impl()).layerState();
709 layerState.update = Nicosia::ImageBackingTextureMapperImpl::Update { };
710 m_nicosia.imageBacking = nullptr;
711 m_nicosia.delta.imageBackingChanged = true;
712 }
713
714 {
715 m_nicosia.layer->updateState(
716 [this](Nicosia::CompositionLayer::LayerState& state)
717 {
718 // OR the local delta value into the layer's pending state delta. After that,
719 // go through each local change and update the pending state accordingly.
720 auto& localDelta = m_nicosia.delta;
721 state.delta.value |= localDelta.value;
722
723 if (localDelta.positionChanged)
724 state.position = m_adjustedPosition;
725 if (localDelta.anchorPointChanged)
726 state.anchorPoint = m_adjustedAnchorPoint;
727 if (localDelta.sizeChanged)
728 state.size = m_adjustedSize;
729
730 if (localDelta.transformChanged)
731 state.transform = transform();
732 if (localDelta.childrenTransformChanged)
733 state.childrenTransform = childrenTransform();
734
735 if (localDelta.contentsRectChanged)
736 state.contentsRect = contentsRect();
737 if (localDelta.contentsTilingChanged) {
738 state.contentsTilePhase = contentsTilePhase();
739 state.contentsTileSize = contentsTileSize();
740 }
741
742 if (localDelta.opacityChanged)
743 state.opacity = opacity();
744 if (localDelta.solidColorChanged)
745 state.solidColor = m_solidColor;
746
747 if (localDelta.filtersChanged)
748 state.filters = filters();
749 if (localDelta.animationsChanged)
750 state.animations = m_animations.getActiveAnimations();
751
752 if (localDelta.childrenChanged) {
753 state.children = WTF::map(children(),
754 [](auto& child)
755 {
756 return downcast<CoordinatedGraphicsLayer>(child.get()).m_nicosia.layer;
757 });
758 }
759
760 if (localDelta.maskChanged) {
761 auto* mask = downcast<CoordinatedGraphicsLayer>(maskLayer());
762 state.mask = mask ? mask->m_nicosia.layer : nullptr;
763 }
764
765 if (localDelta.replicaChanged) {
766 auto* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer());
767 state.replica = replica ? replica->m_nicosia.layer : nullptr;
768 }
769
770 if (localDelta.flagsChanged) {
771 state.flags.contentsOpaque = contentsOpaque();
772 state.flags.drawsContent = drawsContent();
773 state.flags.contentsVisible = contentsAreVisible();
774 state.flags.backfaceVisible = backfaceVisibility();
775 state.flags.masksToBounds = masksToBounds();
776 state.flags.preserves3D = preserves3D();
777 }
778
779 if (localDelta.repaintCounterChanged)
780 state.repaintCounter = m_nicosia.repaintCounter;
781 if (localDelta.debugBorderChanged)
782 state.debugBorder = m_nicosia.debugBorder;
783
784 if (localDelta.backingStoreChanged)
785 state.backingStore = m_nicosia.backingStore;
786 if (localDelta.contentLayerChanged)
787 state.contentLayer = m_nicosia.contentLayer;
788 if (localDelta.imageBackingChanged)
789 state.imageBacking = m_nicosia.imageBacking;
790 });
791 m_nicosia.performLayerSync = !!m_nicosia.delta.value;
792 m_nicosia.delta = { };
793 }
794}
795
796void CoordinatedGraphicsLayer::syncPendingStateChangesIncludingSubLayers()
797{
798 if (m_nicosia.performLayerSync)
799 m_coordinator->syncLayerState();
800 m_nicosia.performLayerSync = false;
801
802 if (maskLayer())
803 downcast<CoordinatedGraphicsLayer>(*maskLayer()).syncPendingStateChangesIncludingSubLayers();
804
805 for (auto& child : children())
806 downcast<CoordinatedGraphicsLayer>(child.get()).syncPendingStateChangesIncludingSubLayers();
807}
808
809void CoordinatedGraphicsLayer::deviceOrPageScaleFactorChanged()
810{
811 if (shouldHaveBackingStore())
812 m_pendingContentsScaleAdjustment = true;
813}
814
815float CoordinatedGraphicsLayer::effectiveContentsScale()
816{
817 return selfOrAncestorHaveNonAffineTransforms() ? 1 : deviceScaleFactor() * pageScaleFactor();
818}
819
820static void clampToContentsRectIfRectIsInfinite(FloatRect& rect, const FloatSize& contentsSize)
821{
822 if (rect.width() >= LayoutUnit::nearlyMax() || rect.width() <= LayoutUnit::nearlyMin()) {
823 rect.setX(0);
824 rect.setWidth(contentsSize.width());
825 }
826
827 if (rect.height() >= LayoutUnit::nearlyMax() || rect.height() <= LayoutUnit::nearlyMin()) {
828 rect.setY(0);
829 rect.setHeight(contentsSize.height());
830 }
831}
832
833IntRect CoordinatedGraphicsLayer::transformedVisibleRect()
834{
835 // Non-invertible layers are not visible.
836 if (!m_layerTransform.combined().isInvertible())
837 return IntRect();
838
839 // Return a projection of the visible rect (surface coordinates) onto the layer's plane (layer coordinates).
840 // The resulting quad might be squewed and the visible rect is the bounding box of this quad,
841 // so it might spread further than the real visible area (and then even more amplified by the cover rect multiplier).
842 ASSERT(m_cachedInverseTransform == m_layerTransform.combined().inverse().valueOr(TransformationMatrix()));
843 FloatRect rect = m_cachedInverseTransform.clampedBoundsOfProjectedQuad(FloatQuad(m_coordinator->visibleContentsRect()));
844 clampToContentsRectIfRectIsInfinite(rect, size());
845 return enclosingIntRect(rect);
846}
847
848void CoordinatedGraphicsLayer::updateContentBuffersIncludingSubLayers()
849{
850 if (CoordinatedGraphicsLayer* mask = downcast<CoordinatedGraphicsLayer>(maskLayer()))
851 mask->updateContentBuffers();
852
853 if (CoordinatedGraphicsLayer* replica = downcast<CoordinatedGraphicsLayer>(replicaLayer()))
854 replica->updateContentBuffers();
855
856 updateContentBuffers();
857
858 for (auto& child : children())
859 downcast<CoordinatedGraphicsLayer>(child.get()).updateContentBuffersIncludingSubLayers();
860}
861
862void CoordinatedGraphicsLayer::updateContentBuffers()
863{
864 if (!m_nicosia.backingStore)
865 return;
866
867 // Prepare for painting on the impl-contained backing store. isFlushing is used there
868 // for internal sanity checks.
869 auto& impl = downcast<Nicosia::BackingStoreTextureMapperImpl>(m_nicosia.backingStore->impl());
870 auto& layerState = impl.layerState();
871 layerState.isFlushing = true;
872
873 // Helper lambda that finished the flush update and determines layer sync necessity.
874 auto finishUpdate =
875 [this, &layerState] {
876 auto& update = layerState.update;
877 m_nicosia.performLayerSync |= !update.tilesToCreate.isEmpty()
878 || !update.tilesToRemove.isEmpty() || !update.tilesToUpdate.isEmpty();
879 layerState.isFlushing = false;
880 };
881
882 // Address the content scale adjustment.
883 if (m_pendingContentsScaleAdjustment) {
884 if (layerState.mainBackingStore && layerState.mainBackingStore->contentsScale() != effectiveContentsScale()) {
885 // Discard the TiledBackingStore object to reconstruct it with new content scale.
886 layerState.mainBackingStore = nullptr;
887 }
888 m_pendingContentsScaleAdjustment = false;
889 }
890
891 // Ensure the TiledBackingStore object, and enforce a complete repaint if it's not been present yet.
892 if (!layerState.mainBackingStore) {
893 layerState.mainBackingStore = std::make_unique<TiledBackingStore>(impl, effectiveContentsScale());
894 m_pendingVisibleRectAdjustment = true;
895 }
896
897 // Bail if there's no painting recorded or enforced.
898 if (!m_pendingVisibleRectAdjustment && !m_needsDisplay.completeLayer && m_needsDisplay.rects.isEmpty()) {
899 finishUpdate();
900 return;
901 }
902
903 if (!m_needsDisplay.completeLayer) {
904 for (auto& rect : m_needsDisplay.rects)
905 layerState.mainBackingStore->invalidate(enclosingIntRect(rect));
906 } else
907 layerState.mainBackingStore->invalidate({ { }, IntSize { m_size } });
908
909 m_needsDisplay.completeLayer = false;
910 m_needsDisplay.rects.clear();
911
912 if (m_pendingVisibleRectAdjustment) {
913 m_pendingVisibleRectAdjustment = false;
914 layerState.mainBackingStore->createTilesIfNeeded(transformedVisibleRect(), IntRect(0, 0, m_size.width(), m_size.height()));
915 }
916
917 ASSERT(m_coordinator && m_coordinator->isFlushingLayerChanges());
918
919 // With all the affected tiles created and/or invalidated, we can finally paint them.
920 auto dirtyTiles = layerState.mainBackingStore->dirtyTiles();
921 if (!dirtyTiles.isEmpty()) {
922 bool didUpdateTiles = false;
923
924 for (auto& tileReference : dirtyTiles) {
925 auto& tile = tileReference.get();
926 tile.ensureTileID();
927
928 auto& tileRect = tile.rect();
929 auto& dirtyRect = tile.dirtyRect();
930
931 auto coordinatedBuffer = Nicosia::Buffer::create(dirtyRect.size(), contentsOpaque() ? Nicosia::Buffer::NoFlags : Nicosia::Buffer::SupportsAlpha);
932 SurfaceUpdateInfo updateInfo;
933 updateInfo.updateRect = dirtyRect;
934 updateInfo.updateRect.move(-tileRect.x(), -tileRect.y());
935 updateInfo.buffer = coordinatedBuffer.copyRef();
936
937 if (!m_coordinator->paintingEngine().paint(*this, WTFMove(coordinatedBuffer),
938 dirtyRect, layerState.mainBackingStore->mapToContents(dirtyRect),
939 IntRect { { 0, 0 }, dirtyRect.size() }, layerState.mainBackingStore->contentsScale()))
940 continue;
941
942 impl.updateTile(tile.tileID(), updateInfo, tileRect);
943
944 tile.markClean();
945 didUpdateTiles |= true;
946 }
947
948 if (didUpdateTiles)
949 didUpdateTileBuffers();
950 }
951
952 // Request a new update immediately if some tiles are still pending creation. Do this on a timer
953 // as we're in a layer flush and flush requests at this point would be discarded.
954 if (layerState.hasPendingTileCreation) {
955 setNeedsVisibleRectAdjustment();
956 m_requestPendingTileCreationTimer.startOneShot(0_s);
957 }
958
959 finishUpdate();
960}
961
962void CoordinatedGraphicsLayer::purgeBackingStores()
963{
964#ifndef NDEBUG
965 SetForScope<bool> updateModeProtector(m_isPurging, true);
966#endif
967 if (m_nicosia.backingStore) {
968 auto& layerState = downcast<Nicosia::BackingStoreTextureMapperImpl>(m_nicosia.backingStore->impl()).layerState();
969 layerState.isPurging = true;
970 layerState.mainBackingStore = nullptr;
971
972 m_nicosia.backingStore = nullptr;
973 }
974
975 if (m_nicosia.imageBacking) {
976 auto& layerState = downcast<Nicosia::ImageBackingTextureMapperImpl>(m_nicosia.imageBacking->impl()).layerState();
977 layerState.imageID = 0;
978 layerState.nativeImageID = 0;
979 layerState.update = { };
980
981 m_nicosia.imageBacking = nullptr;
982 }
983
984 notifyFlushRequired();
985}
986
987void CoordinatedGraphicsLayer::setCoordinator(CoordinatedGraphicsLayerClient* coordinator)
988{
989 m_coordinator = coordinator;
990}
991
992void CoordinatedGraphicsLayer::setCoordinatorIncludingSubLayersIfNeeded(CoordinatedGraphicsLayerClient* coordinator)
993{
994 if (!coordinator || m_coordinator == coordinator)
995 return;
996
997 // If the coordinators are different it means that we are attaching a layer that was created by a different
998 // CompositingCoordinator than the current one. This happens because the layer was taken out of the tree
999 // and then added back after AC was disabled and enabled again. We need to set the new coordinator to the
1000 // layer and its children.
1001 //
1002 // During each layer flush, the state stores the values that have changed since the previous one, and these
1003 // are updated once in the scene. When adding CoordinatedGraphicsLayers back to the tree, the fields that
1004 // are not updated during the next flush won't be sent to the scene, so they won't be updated there and the
1005 // rendering will fail.
1006 //
1007 // For example the drawsContent flag. This is set when the layer is created and is not updated anymore (unless
1008 // the content changes). When the layer is added back to the tree, the state won't reflect any change in the
1009 // flag value, so the scene won't update it and the layer won't be rendered.
1010 //
1011 // We need to update here the layer changeMask so the scene gets all the current values.
1012 m_nicosia.delta.value = UINT_MAX;
1013
1014 coordinator->attachLayer(this);
1015 for (auto& child : children())
1016 downcast<CoordinatedGraphicsLayer>(child.get()).setCoordinatorIncludingSubLayersIfNeeded(coordinator);
1017}
1018
1019const RefPtr<Nicosia::CompositionLayer>& CoordinatedGraphicsLayer::compositionLayer() const
1020{
1021 return m_nicosia.layer;
1022}
1023
1024void CoordinatedGraphicsLayer::setNeedsVisibleRectAdjustment()
1025{
1026 if (shouldHaveBackingStore())
1027 m_pendingVisibleRectAdjustment = true;
1028}
1029
1030static inline bool isIntegral(float value)
1031{
1032 return static_cast<int>(value) == value;
1033}
1034
1035FloatPoint CoordinatedGraphicsLayer::computePositionRelativeToBase()
1036{
1037 FloatPoint offset;
1038 for (const GraphicsLayer* currLayer = this; currLayer; currLayer = currLayer->parent())
1039 offset += currLayer->position();
1040
1041 return offset;
1042}
1043
1044void CoordinatedGraphicsLayer::computePixelAlignment(FloatPoint& position, FloatSize& size, FloatPoint3D& anchorPoint, FloatSize& alignmentOffset)
1045{
1046 if (isIntegral(effectiveContentsScale())) {
1047 position = m_position;
1048 size = m_size;
1049 anchorPoint = m_anchorPoint;
1050 alignmentOffset = FloatSize();
1051 return;
1052 }
1053
1054 FloatPoint positionRelativeToBase = computePositionRelativeToBase();
1055
1056 FloatRect baseRelativeBounds(positionRelativeToBase, m_size);
1057 FloatRect scaledBounds = baseRelativeBounds;
1058
1059 // Scale by the effective scale factor to compute the screen-relative bounds.
1060 scaledBounds.scale(effectiveContentsScale());
1061
1062 // Round to integer boundaries.
1063 // NOTE: When using enclosingIntRect (as mac) it will have different sizes depending on position.
1064 FloatRect alignedBounds = enclosingIntRect(scaledBounds);
1065
1066 // Convert back to layer coordinates.
1067 alignedBounds.scale(1 / effectiveContentsScale());
1068
1069 // Convert back to layer coordinates.
1070 alignmentOffset = baseRelativeBounds.location() - alignedBounds.location();
1071
1072 position = m_position - alignmentOffset;
1073 size = alignedBounds.size();
1074
1075 // Now we have to compute a new anchor point which compensates for rounding.
1076 float anchorPointX = m_anchorPoint.x();
1077 float anchorPointY = m_anchorPoint.y();
1078
1079 if (alignedBounds.width())
1080 anchorPointX = (baseRelativeBounds.width() * anchorPointX + alignmentOffset.width()) / alignedBounds.width();
1081
1082 if (alignedBounds.height())
1083 anchorPointY = (baseRelativeBounds.height() * anchorPointY + alignmentOffset.height()) / alignedBounds.height();
1084
1085 anchorPoint = FloatPoint3D(anchorPointX, anchorPointY, m_anchorPoint.z() * effectiveContentsScale());
1086}
1087
1088void CoordinatedGraphicsLayer::computeTransformedVisibleRect()
1089{
1090 if (!m_shouldUpdateVisibleRect && !m_movingVisibleRect)
1091 return;
1092
1093 m_shouldUpdateVisibleRect = false;
1094 TransformationMatrix currentTransform = transform();
1095 if (m_movingVisibleRect)
1096 client().getCurrentTransform(this, currentTransform);
1097 m_layerTransform.setLocalTransform(currentTransform);
1098
1099 m_layerTransform.setAnchorPoint(m_adjustedAnchorPoint);
1100 m_layerTransform.setPosition(m_adjustedPosition);
1101 m_layerTransform.setSize(m_adjustedSize);
1102
1103 m_layerTransform.setFlattening(!preserves3D());
1104 m_layerTransform.setChildrenTransform(childrenTransform());
1105 m_layerTransform.combineTransforms(parent() ? downcast<CoordinatedGraphicsLayer>(*parent()).m_layerTransform.combinedForChildren() : TransformationMatrix());
1106
1107 m_cachedInverseTransform = m_layerTransform.combined().inverse().valueOr(TransformationMatrix());
1108
1109 // The combined transform will be used in tiledBackingStoreVisibleRect.
1110 setNeedsVisibleRectAdjustment();
1111}
1112
1113bool CoordinatedGraphicsLayer::shouldHaveBackingStore() const
1114{
1115 return drawsContent() && contentsAreVisible() && !m_size.isEmpty();
1116}
1117
1118bool CoordinatedGraphicsLayer::selfOrAncestorHasActiveTransformAnimation() const
1119{
1120 if (m_animations.hasActiveAnimationsOfType(AnimatedPropertyTransform))
1121 return true;
1122
1123 if (!parent())
1124 return false;
1125
1126 return downcast<CoordinatedGraphicsLayer>(*parent()).selfOrAncestorHasActiveTransformAnimation();
1127}
1128
1129bool CoordinatedGraphicsLayer::selfOrAncestorHaveNonAffineTransforms()
1130{
1131 if (!m_layerTransform.combined().isAffine())
1132 return true;
1133
1134 if (!parent())
1135 return false;
1136
1137 return downcast<CoordinatedGraphicsLayer>(*parent()).selfOrAncestorHaveNonAffineTransforms();
1138}
1139
1140bool CoordinatedGraphicsLayer::addAnimation(const KeyframeValueList& valueList, const FloatSize& boxSize, const Animation* anim, const String& keyframesName, double delayAsNegativeTimeOffset)
1141{
1142 ASSERT(!keyframesName.isEmpty());
1143
1144 if (!anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2 || (valueList.property() != AnimatedPropertyTransform && valueList.property() != AnimatedPropertyOpacity && valueList.property() != AnimatedPropertyFilter))
1145 return false;
1146
1147 if (valueList.property() == AnimatedPropertyFilter) {
1148 int listIndex = validateFilterOperations(valueList);
1149 if (listIndex < 0)
1150 return false;
1151
1152 const auto& filters = static_cast<const FilterAnimationValue&>(valueList.at(listIndex)).value();
1153 if (!filtersCanBeComposited(filters))
1154 return false;
1155 }
1156
1157 bool listsMatch = false;
1158 bool ignoredHasBigRotation;
1159
1160 if (valueList.property() == AnimatedPropertyTransform)
1161 listsMatch = validateTransformOperations(valueList, ignoredHasBigRotation) >= 0;
1162
1163 m_lastAnimationStartTime = MonotonicTime::now() - Seconds(delayAsNegativeTimeOffset);
1164 m_animations.add(TextureMapperAnimation(keyframesName, valueList, boxSize, *anim, listsMatch, m_lastAnimationStartTime, 0_s, TextureMapperAnimation::AnimationState::Playing));
1165 m_animationStartedTimer.startOneShot(0_s);
1166 didChangeAnimations();
1167 return true;
1168}
1169
1170void CoordinatedGraphicsLayer::pauseAnimation(const String& animationName, double time)
1171{
1172 m_animations.pause(animationName, Seconds(time));
1173 didChangeAnimations();
1174}
1175
1176void CoordinatedGraphicsLayer::removeAnimation(const String& animationName)
1177{
1178 m_animations.remove(animationName);
1179 didChangeAnimations();
1180}
1181
1182void CoordinatedGraphicsLayer::suspendAnimations(MonotonicTime time)
1183{
1184 m_animations.suspend(time);
1185 didChangeAnimations();
1186}
1187
1188void CoordinatedGraphicsLayer::resumeAnimations()
1189{
1190 m_animations.resume();
1191 didChangeAnimations();
1192}
1193
1194void CoordinatedGraphicsLayer::animationStartedTimerFired()
1195{
1196 client().notifyAnimationStarted(this, "", m_lastAnimationStartTime);
1197}
1198
1199void CoordinatedGraphicsLayer::requestPendingTileCreationTimerFired()
1200{
1201 notifyFlushRequired();
1202}
1203
1204bool CoordinatedGraphicsLayer::usesContentsLayer() const
1205{
1206 return m_nicosia.contentLayer || m_compositedImage;
1207}
1208
1209} // namespace WebCore
1210
1211#endif // USE(COORDINATED_GRAPHICS)
1212