[CoordinatedGraphics] The compositing loop is still running even after exiting AC...
[WebKit-https.git] / Source / WebKit / WebProcess / WebPage / CoordinatedGraphics / DrawingAreaCoordinatedGraphics.cpp
1 /*
2  * Copyright (C) 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
4  * Copyright (C) 2019 Igalia S.L.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25  * THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "DrawingAreaCoordinatedGraphics.h"
30
31 #include "DrawingAreaProxyMessages.h"
32 #include "LayerTreeHost.h"
33 #include "ShareableBitmap.h"
34 #include "UpdateInfo.h"
35 #include "WebPage.h"
36 #include "WebPageCreationParameters.h"
37 #include "WebPreferencesKeys.h"
38 #include <WebCore/Frame.h>
39 #include <WebCore/GraphicsContext.h>
40 #include <WebCore/Page.h>
41 #include <WebCore/PageOverlayController.h>
42 #include <WebCore/Settings.h>
43
44 #if USE(GLIB_EVENT_LOOP)
45 #include <wtf/glib/RunLoopSourcePriority.h>
46 #endif
47
48 namespace WebKit {
49 using namespace WebCore;
50
51 DrawingAreaCoordinatedGraphics::DrawingAreaCoordinatedGraphics(WebPage& webPage, const WebPageCreationParameters& parameters)
52     : DrawingArea(DrawingAreaTypeCoordinatedGraphics, parameters.drawingAreaIdentifier, webPage)
53     , m_exitCompositingTimer(RunLoop::main(), this, &DrawingAreaCoordinatedGraphics::exitAcceleratedCompositingMode)
54     , m_discardPreviousLayerTreeHostTimer(RunLoop::main(), this, &DrawingAreaCoordinatedGraphics::discardPreviousLayerTreeHost)
55     , m_displayTimer(RunLoop::main(), this, &DrawingAreaCoordinatedGraphics::displayTimerFired)
56 {
57 #if USE(GLIB_EVENT_LOOP)
58     m_discardPreviousLayerTreeHostTimer.setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer);
59 #if !PLATFORM(WPE)
60     m_displayTimer.setPriority(RunLoopSourcePriority::NonAcceleratedDrawingTimer);
61 #endif
62 #endif
63 }
64
65 DrawingAreaCoordinatedGraphics::~DrawingAreaCoordinatedGraphics()
66 {
67     discardPreviousLayerTreeHost();
68     if (m_layerTreeHost)
69         m_layerTreeHost->invalidate();
70 }
71
72 void DrawingAreaCoordinatedGraphics::setNeedsDisplay()
73 {
74     if (m_layerTreeHost) {
75         ASSERT(m_dirtyRegion.isEmpty());
76         return;
77     }
78
79     setNeedsDisplayInRect(m_webPage.bounds());
80 }
81
82 void DrawingAreaCoordinatedGraphics::setNeedsDisplayInRect(const IntRect& rect)
83 {
84     if (m_layerTreeHost) {
85         ASSERT(m_dirtyRegion.isEmpty());
86         return;
87     }
88
89     if (!m_isPaintingEnabled)
90         return;
91
92     IntRect dirtyRect = rect;
93     dirtyRect.intersect(m_webPage.bounds());
94     if (dirtyRect.isEmpty())
95         return;
96
97     m_dirtyRegion.unite(dirtyRect);
98     scheduleDisplay();
99 }
100
101 void DrawingAreaCoordinatedGraphics::scroll(const IntRect& scrollRect, const IntSize& scrollDelta)
102 {
103     if (!m_isPaintingEnabled)
104         return;
105
106     if (m_layerTreeHost) {
107         ASSERT(m_scrollRect.isEmpty());
108         ASSERT(m_scrollOffset.isEmpty());
109         ASSERT(m_dirtyRegion.isEmpty());
110         m_layerTreeHost->scrollNonCompositedContents(scrollRect);
111         return;
112     }
113
114     if (scrollRect.isEmpty())
115         return;
116
117     if (m_previousLayerTreeHost)
118         m_previousLayerTreeHost->scrollNonCompositedContents(scrollRect);
119
120     if (!m_scrollRect.isEmpty() && scrollRect != m_scrollRect) {
121         unsigned scrollArea = scrollRect.width() * scrollRect.height();
122         unsigned currentScrollArea = m_scrollRect.width() * m_scrollRect.height();
123
124         if (currentScrollArea >= scrollArea) {
125             // The rect being scrolled is at least as large as the rect we'd like to scroll.
126             // Go ahead and just invalidate the scroll rect.
127             setNeedsDisplayInRect(scrollRect);
128             return;
129         }
130
131         // Just repaint the entire current scroll rect, we'll scroll the new rect instead.
132         setNeedsDisplayInRect(m_scrollRect);
133         m_scrollRect = IntRect();
134         m_scrollOffset = IntSize();
135     }
136
137     // Get the part of the dirty region that is in the scroll rect.
138     Region dirtyRegionInScrollRect = intersect(scrollRect, m_dirtyRegion);
139     if (!dirtyRegionInScrollRect.isEmpty()) {
140         // There are parts of the dirty region that are inside the scroll rect.
141         // We need to subtract them from the region, move them and re-add them.
142         m_dirtyRegion.subtract(scrollRect);
143
144         // Move the dirty parts.
145         Region movedDirtyRegionInScrollRect = intersect(translate(dirtyRegionInScrollRect, scrollDelta), scrollRect);
146
147         // And add them back.
148         m_dirtyRegion.unite(movedDirtyRegionInScrollRect);
149     }
150
151     // Compute the scroll repaint region.
152     Region scrollRepaintRegion = subtract(scrollRect, translate(scrollRect, scrollDelta));
153
154     m_dirtyRegion.unite(scrollRepaintRegion);
155     scheduleDisplay();
156
157     m_scrollRect = scrollRect;
158     m_scrollOffset += scrollDelta;
159 }
160
161 void DrawingAreaCoordinatedGraphics::forceRepaint()
162 {
163     if (m_inUpdateBackingStoreState) {
164         m_forceRepaintAfterBackingStoreStateUpdate = true;
165         return;
166     }
167     m_forceRepaintAfterBackingStoreStateUpdate = false;
168
169     if (!m_layerTreeHost) {
170         m_isWaitingForDidUpdate = false;
171         if (m_isPaintingEnabled) {
172             m_dirtyRegion = m_webPage.bounds();
173             display();
174         }
175         return;
176     }
177
178     if (m_layerTreeStateIsFrozen)
179         return;
180
181     setNeedsDisplay();
182     m_webPage.layoutIfNeeded();
183     if (!m_layerTreeHost)
184         return;
185
186     // FIXME: We need to do the same work as the layerHostDidFlushLayers function here,
187     // but clearly it doesn't make sense to call the function with that name.
188     // Consider refactoring and renaming it.
189     if (m_compositingAccordingToProxyMessages)
190         m_layerTreeHost->forceRepaint();
191     else {
192         // Call setShouldNotifyAfterNextScheduledLayerFlush(false) here to
193         // prevent layerHostDidFlushLayers() from being called a second time.
194         m_layerTreeHost->setShouldNotifyAfterNextScheduledLayerFlush(false);
195         layerHostDidFlushLayers();
196     }
197 }
198
199 bool DrawingAreaCoordinatedGraphics::forceRepaintAsync(CallbackID callbackID)
200 {
201     if (m_layerTreeStateIsFrozen)
202         return false;
203
204     return m_layerTreeHost && m_layerTreeHost->forceRepaintAsync(callbackID);
205 }
206
207 void DrawingAreaCoordinatedGraphics::setLayerTreeStateIsFrozen(bool isFrozen)
208 {
209     if (m_layerTreeStateIsFrozen == isFrozen)
210         return;
211
212     m_layerTreeStateIsFrozen = isFrozen;
213
214     if (m_layerTreeHost)
215         m_layerTreeHost->setLayerFlushSchedulingEnabled(!isFrozen);
216
217     if (isFrozen)
218         m_exitCompositingTimer.stop();
219     else if (m_wantsToExitAcceleratedCompositingMode)
220         exitAcceleratedCompositingModeSoon();
221 }
222
223 void DrawingAreaCoordinatedGraphics::updatePreferences(const WebPreferencesStore& store)
224 {
225     Settings& settings = m_webPage.corePage()->settings();
226     settings.setForceCompositingMode(store.getBoolValueForKey(WebPreferencesKey::forceCompositingModeKey()));
227     // Fixed position elements need to be composited and create stacking contexts
228     // in order to be scrolled by the ScrollingCoordinator.
229     settings.setAcceleratedCompositingForFixedPositionEnabled(settings.acceleratedCompositingEnabled());
230
231     m_alwaysUseCompositing = settings.acceleratedCompositingEnabled() && settings.forceCompositingMode();
232     if (m_alwaysUseCompositing && !m_layerTreeHost)
233         enterAcceleratedCompositingMode(nullptr);
234 }
235
236 void DrawingAreaCoordinatedGraphics::mainFrameContentSizeChanged(const IntSize& size)
237 {
238     if (m_layerTreeHost)
239         m_layerTreeHost->contentsSizeChanged(size);
240     else if (m_previousLayerTreeHost)
241         m_previousLayerTreeHost->contentsSizeChanged(size);
242 }
243
244 void DrawingAreaCoordinatedGraphics::deviceOrPageScaleFactorChanged()
245 {
246     if (m_layerTreeHost)
247         m_layerTreeHost->deviceOrPageScaleFactorChanged();
248     else if (m_previousLayerTreeHost)
249         m_previousLayerTreeHost->deviceOrPageScaleFactorChanged();
250 }
251
252 void DrawingAreaCoordinatedGraphics::didChangeViewportAttributes(ViewportAttributes&& attrs)
253 {
254     if (m_layerTreeHost)
255         m_layerTreeHost->didChangeViewportAttributes(WTFMove(attrs));
256     else if (m_previousLayerTreeHost)
257         m_previousLayerTreeHost->didChangeViewportAttributes(WTFMove(attrs));
258 }
259
260 GraphicsLayerFactory* DrawingAreaCoordinatedGraphics::graphicsLayerFactory()
261 {
262     if (!m_layerTreeHost)
263         enterAcceleratedCompositingMode(nullptr);
264     return m_layerTreeHost ? m_layerTreeHost->graphicsLayerFactory() : nullptr;
265 }
266
267 void DrawingAreaCoordinatedGraphics::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
268 {
269     if (m_layerTreeHost) {
270         if (graphicsLayer) {
271             // We're already in accelerated compositing mode, but the root compositing layer changed.
272             m_exitCompositingTimer.stop();
273             m_wantsToExitAcceleratedCompositingMode = false;
274
275             // If we haven't sent the EnterAcceleratedCompositingMode message, make sure that the
276             // layer tree host calls us back after the next layer flush so we can send it then.
277             if (!m_compositingAccordingToProxyMessages)
278                 m_layerTreeHost->setShouldNotifyAfterNextScheduledLayerFlush(true);
279         }
280         m_layerTreeHost->setRootCompositingLayer(graphicsLayer);
281
282         if (!graphicsLayer && !m_alwaysUseCompositing) {
283             // We'll exit accelerated compositing mode on a timer, to avoid re-entering
284             // compositing code via display() and layout.
285             // If we're leaving compositing mode because of a setSize, it is safe to
286             // exit accelerated compositing mode right away.
287             if (m_inUpdateBackingStoreState)
288                 exitAcceleratedCompositingMode();
289             else
290                 exitAcceleratedCompositingModeSoon();
291         }
292         return;
293     }
294
295     if (!graphicsLayer)
296         return;
297
298     // We're actually entering accelerated compositing mode.
299     enterAcceleratedCompositingMode(graphicsLayer);
300 }
301
302 void DrawingAreaCoordinatedGraphics::scheduleCompositingLayerFlush()
303 {
304     if (m_layerTreeHost)
305         m_layerTreeHost->scheduleLayerFlush();
306 }
307
308 void DrawingAreaCoordinatedGraphics::layerHostDidFlushLayers()
309 {
310     ASSERT(m_layerTreeHost);
311     m_layerTreeHost->forceRepaint();
312
313     if (m_shouldSendDidUpdateBackingStoreState && !exitAcceleratedCompositingModePending()) {
314         sendDidUpdateBackingStoreState();
315         return;
316     }
317
318     ASSERT(!m_compositingAccordingToProxyMessages);
319     if (!exitAcceleratedCompositingModePending()) {
320         send(Messages::DrawingAreaProxy::EnterAcceleratedCompositingMode(m_backingStoreStateID, m_layerTreeHost->layerTreeContext()));
321         m_compositingAccordingToProxyMessages = true;
322     }
323 }
324
325 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
326 RefPtr<DisplayRefreshMonitor> DrawingAreaCoordinatedGraphics::createDisplayRefreshMonitor(PlatformDisplayID displayID)
327 {
328     if (!m_layerTreeHost || m_wantsToExitAcceleratedCompositingMode || exitAcceleratedCompositingModePending())
329         return nullptr;
330     return m_layerTreeHost->createDisplayRefreshMonitor(displayID);
331 }
332 #endif
333
334 #if USE(TEXTURE_MAPPER_GL) && PLATFORM(GTK) && PLATFORM(X11) && !USE(REDIRECTED_XCOMPOSITE_WINDOW)
335 void DrawingAreaCoordinatedGraphics::setNativeSurfaceHandleForCompositing(uint64_t handle)
336 {
337     m_nativeSurfaceHandleForCompositing = handle;
338     if (m_layerTreeHost) {
339         m_webPage.corePage()->settings().setAcceleratedCompositingEnabled(true);
340         m_layerTreeHost->setNativeSurfaceHandleForCompositing(handle);
341     }
342 }
343
344 void DrawingAreaCoordinatedGraphics::destroyNativeSurfaceHandleForCompositing(bool& handled)
345 {
346     handled = true;
347     setNativeSurfaceHandleForCompositing(0);
348 }
349 #endif
350
351 void DrawingAreaCoordinatedGraphics::activityStateDidChange(OptionSet<ActivityState::Flag> changed, ActivityStateChangeID, const Vector<CallbackID>&)
352 {
353     if (changed & ActivityState::IsVisible) {
354         if (m_webPage.isVisible())
355             resumePainting();
356         else
357             suspendPainting();
358     }
359 }
360
361 void DrawingAreaCoordinatedGraphics::attachViewOverlayGraphicsLayer(GraphicsLayer* viewOverlayRootLayer)
362 {
363     if (m_layerTreeHost)
364         m_layerTreeHost->setViewOverlayRootLayer(viewOverlayRootLayer);
365     else if (m_previousLayerTreeHost)
366         m_previousLayerTreeHost->setViewOverlayRootLayer(viewOverlayRootLayer);
367 }
368
369 void DrawingAreaCoordinatedGraphics::updateBackingStoreState(uint64_t stateID, bool respondImmediately, float deviceScaleFactor, const IntSize& size, const IntSize& scrollOffset)
370 {
371     if (stateID != m_backingStoreStateID && !m_layerTreeHost)
372         m_dirtyRegion = IntRect(IntPoint(), size);
373
374     ASSERT(!m_inUpdateBackingStoreState);
375     m_inUpdateBackingStoreState = true;
376
377     ASSERT_ARG(stateID, stateID >= m_backingStoreStateID);
378     if (stateID != m_backingStoreStateID) {
379         m_backingStoreStateID = stateID;
380         m_shouldSendDidUpdateBackingStoreState = true;
381
382         m_webPage.setDeviceScaleFactor(deviceScaleFactor);
383         m_webPage.setSize(size);
384         m_webPage.layoutIfNeeded();
385         m_webPage.flushPendingEditorStateUpdate();
386         m_webPage.scrollMainFrameIfNotAtMaxScrollPosition(scrollOffset);
387         m_webPage.willDisplayPage();
388
389         if (m_layerTreeHost)
390             m_layerTreeHost->sizeDidChange(m_webPage.size());
391         else if (m_previousLayerTreeHost)
392             m_previousLayerTreeHost->sizeDidChange(m_webPage.size());
393     } else {
394         ASSERT(size == m_webPage.size());
395         if (!m_shouldSendDidUpdateBackingStoreState) {
396             // We've already sent a DidUpdateBackingStoreState message for this state. We have nothing more to do.
397             m_inUpdateBackingStoreState = false;
398             if (m_forceRepaintAfterBackingStoreStateUpdate)
399                 forceRepaint();
400             return;
401         }
402     }
403
404     // The UI process has updated to a new backing store state. Any Update messages we sent before
405     // this point will be ignored. We wait to set this to false until after updating the page's
406     // size so that any displays triggered by the relayout will be ignored. If we're supposed to
407     // respond to the UpdateBackingStoreState message immediately, we'll do a display anyway in
408     // sendDidUpdateBackingStoreState; otherwise we shouldn't do one right now.
409     m_isWaitingForDidUpdate = false;
410
411     if (respondImmediately) {
412         // Make sure to resume painting if we're supposed to respond immediately, otherwise we'll just
413         // send back an empty UpdateInfo struct.
414         if (m_isPaintingSuspended)
415             resumePainting();
416
417         sendDidUpdateBackingStoreState();
418     }
419
420     m_inUpdateBackingStoreState = false;
421
422     if (m_forceRepaintAfterBackingStoreStateUpdate)
423         forceRepaint();
424 }
425
426 void DrawingAreaCoordinatedGraphics::didUpdate()
427 {
428     // We might get didUpdate messages from the UI process even after we've
429     // entered accelerated compositing mode. Ignore them.
430     if (m_layerTreeHost)
431         return;
432
433     m_isWaitingForDidUpdate = false;
434
435     // Display if needed. We call displayTimerFired here since it will throttle updates to 60fps.
436     displayTimerFired();
437 }
438
439 void DrawingAreaCoordinatedGraphics::sendDidUpdateBackingStoreState()
440 {
441     ASSERT(!m_isWaitingForDidUpdate);
442     ASSERT(m_shouldSendDidUpdateBackingStoreState);
443
444     if (!m_isPaintingSuspended && !m_layerTreeHost) {
445         UpdateInfo updateInfo;
446         display(updateInfo);
447         if (!m_layerTreeHost) {
448             m_shouldSendDidUpdateBackingStoreState = false;
449
450             LayerTreeContext layerTreeContext;
451             send(Messages::DrawingAreaProxy::DidUpdateBackingStoreState(m_backingStoreStateID, updateInfo, layerTreeContext));
452             m_compositingAccordingToProxyMessages = false;
453             return;
454         }
455     }
456
457     ASSERT(m_shouldSendDidUpdateBackingStoreState);
458     m_shouldSendDidUpdateBackingStoreState = false;
459
460     UpdateInfo updateInfo;
461     updateInfo.viewSize = m_webPage.size();
462     updateInfo.deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
463
464     LayerTreeContext layerTreeContext;
465     if (m_layerTreeHost) {
466         layerTreeContext = m_layerTreeHost->layerTreeContext();
467
468         // We don't want the layer tree host to notify after the next scheduled
469         // layer flush because that might end up sending an EnterAcceleratedCompositingMode
470         // message back to the UI process, but the updated layer tree context
471         // will be sent back in the DidUpdateBackingStoreState message.
472         m_layerTreeHost->setShouldNotifyAfterNextScheduledLayerFlush(false);
473         m_layerTreeHost->forceRepaint();
474     }
475
476     send(Messages::DrawingAreaProxy::DidUpdateBackingStoreState(m_backingStoreStateID, updateInfo, layerTreeContext));
477     m_compositingAccordingToProxyMessages = !layerTreeContext.isEmpty();
478 }
479
480 void DrawingAreaCoordinatedGraphics::exitAcceleratedCompositingModeSoon()
481 {
482     if (m_layerTreeStateIsFrozen) {
483         m_wantsToExitAcceleratedCompositingMode = true;
484         return;
485     }
486
487     if (exitAcceleratedCompositingModePending())
488         return;
489
490     m_exitCompositingTimer.startOneShot(0_s);
491 }
492
493 void DrawingAreaCoordinatedGraphics::discardPreviousLayerTreeHost()
494 {
495     m_discardPreviousLayerTreeHostTimer.stop();
496     if (!m_previousLayerTreeHost)
497         return;
498
499     m_previousLayerTreeHost->invalidate();
500     m_previousLayerTreeHost = nullptr;
501 }
502
503 void DrawingAreaCoordinatedGraphics::suspendPainting()
504 {
505     ASSERT(!m_isPaintingSuspended);
506
507     if (m_layerTreeHost)
508         m_layerTreeHost->pauseRendering();
509     else
510         m_displayTimer.stop();
511
512     m_isPaintingSuspended = true;
513
514     m_webPage.corePage()->suspendScriptedAnimations();
515 }
516
517 void DrawingAreaCoordinatedGraphics::resumePainting()
518 {
519     if (!m_isPaintingSuspended) {
520         // FIXME: We can get a call to resumePainting when painting is not suspended.
521         // This happens when sending a synchronous message to create a new page. See <rdar://problem/8976531>.
522         return;
523     }
524
525     if (m_layerTreeHost)
526         m_layerTreeHost->resumeRendering();
527
528     m_isPaintingSuspended = false;
529
530     // FIXME: We shouldn't always repaint everything here.
531     setNeedsDisplay();
532
533     m_webPage.corePage()->resumeScriptedAnimations();
534 }
535
536 void DrawingAreaCoordinatedGraphics::enterAcceleratedCompositingMode(GraphicsLayer* graphicsLayer)
537 {
538     m_discardPreviousLayerTreeHostTimer.stop();
539
540     m_exitCompositingTimer.stop();
541     m_wantsToExitAcceleratedCompositingMode = false;
542
543     ASSERT(!m_layerTreeHost);
544     if (m_previousLayerTreeHost) {
545         m_layerTreeHost = WTFMove(m_previousLayerTreeHost);
546         m_layerTreeHost->setIsDiscardable(false);
547         if (!m_isPaintingSuspended)
548             m_layerTreeHost->resumeRendering();
549         if (!m_layerTreeStateIsFrozen)
550             m_layerTreeHost->setLayerFlushSchedulingEnabled(true);
551     } else {
552 #if USE(COORDINATED_GRAPHICS)
553         m_layerTreeHost = std::make_unique<LayerTreeHost>(m_webPage);
554 #else
555         m_layerTreeHost = nullptr;
556         return;
557 #endif
558         if (m_isPaintingSuspended)
559             m_layerTreeHost->pauseRendering();
560     }
561
562 #if USE(TEXTURE_MAPPER_GL) && PLATFORM(GTK) && PLATFORM(X11) && !USE(REDIRECTED_XCOMPOSITE_WINDOW)
563     if (m_nativeSurfaceHandleForCompositing)
564         m_layerTreeHost->setNativeSurfaceHandleForCompositing(m_nativeSurfaceHandleForCompositing);
565 #endif
566     if (!m_inUpdateBackingStoreState)
567         m_layerTreeHost->setShouldNotifyAfterNextScheduledLayerFlush(true);
568
569     m_layerTreeHost->setRootCompositingLayer(graphicsLayer);
570
571     // Non-composited content will now be handled exclusively by the layer tree host.
572     m_dirtyRegion = Region();
573     m_scrollRect = IntRect();
574     m_scrollOffset = IntSize();
575     m_displayTimer.stop();
576     m_isWaitingForDidUpdate = false;
577 }
578
579 void DrawingAreaCoordinatedGraphics::exitAcceleratedCompositingMode()
580 {
581     if (m_alwaysUseCompositing)
582         return;
583
584     ASSERT(!m_layerTreeStateIsFrozen);
585
586     m_exitCompositingTimer.stop();
587     m_wantsToExitAcceleratedCompositingMode = false;
588
589     ASSERT(m_layerTreeHost);
590     m_previousLayerTreeHost = WTFMove(m_layerTreeHost);
591     m_previousLayerTreeHost->setIsDiscardable(true);
592     m_previousLayerTreeHost->pauseRendering();
593     m_previousLayerTreeHost->setLayerFlushSchedulingEnabled(false);
594     m_discardPreviousLayerTreeHostTimer.startOneShot(5_s);
595
596     m_dirtyRegion = m_webPage.bounds();
597
598     if (m_inUpdateBackingStoreState)
599         return;
600
601     if (m_shouldSendDidUpdateBackingStoreState) {
602         sendDidUpdateBackingStoreState();
603         return;
604     }
605
606     UpdateInfo updateInfo;
607     if (m_isPaintingSuspended) {
608         updateInfo.viewSize = m_webPage.size();
609         updateInfo.deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
610     } else
611         display(updateInfo);
612
613     // Send along a complete update of the page so we can paint the contents right after we exit the
614     // accelerated compositing mode, eliminiating flicker.
615     if (m_compositingAccordingToProxyMessages) {
616         send(Messages::DrawingAreaProxy::ExitAcceleratedCompositingMode(m_backingStoreStateID, updateInfo));
617         m_compositingAccordingToProxyMessages = false;
618     } else {
619         // If we left accelerated compositing mode before we sent an EnterAcceleratedCompositingMode message to the
620         // UI process, we still need to let it know about the new contents, so send an Update message.
621         send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
622     }
623 }
624
625 void DrawingAreaCoordinatedGraphics::scheduleDisplay()
626 {
627     ASSERT(!m_layerTreeHost);
628
629     if (m_isWaitingForDidUpdate)
630         return;
631
632     if (m_isPaintingSuspended)
633         return;
634
635     if (m_dirtyRegion.isEmpty())
636         return;
637
638     if (m_displayTimer.isActive())
639         return;
640
641     m_displayTimer.startOneShot(0_s);
642 }
643
644 void DrawingAreaCoordinatedGraphics::displayTimerFired()
645 {
646     display();
647 }
648
649 void DrawingAreaCoordinatedGraphics::display()
650 {
651     ASSERT(!m_layerTreeHost);
652     ASSERT(!m_isWaitingForDidUpdate);
653     ASSERT(!m_inUpdateBackingStoreState);
654
655     if (m_isPaintingSuspended)
656         return;
657
658     if (m_dirtyRegion.isEmpty())
659         return;
660
661     if (m_shouldSendDidUpdateBackingStoreState) {
662         sendDidUpdateBackingStoreState();
663         return;
664     }
665
666     UpdateInfo updateInfo;
667     display(updateInfo);
668
669     if (m_layerTreeHost) {
670         // The call to update caused layout which turned on accelerated compositing.
671         // Don't send an Update message in this case.
672         return;
673     }
674
675     send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
676     m_isWaitingForDidUpdate = true;
677 }
678
679 static bool shouldPaintBoundsRect(const IntRect& bounds, const Vector<IntRect>& rects)
680 {
681     const size_t rectThreshold = 10;
682     const double wastedSpaceThreshold = 0.75;
683
684     if (rects.size() <= 1 || rects.size() > rectThreshold)
685         return true;
686
687     // Attempt to guess whether or not we should use the region bounds rect or the individual rects.
688     // We do this by computing the percentage of "wasted space" in the bounds. If that wasted space
689     // is too large, then we will do individual rect painting instead.
690     unsigned boundsArea = bounds.width() * bounds.height();
691     unsigned rectsArea = 0;
692     for (size_t i = 0; i < rects.size(); ++i)
693         rectsArea += rects[i].width() * rects[i].height();
694
695     double wastedSpace = 1 - (static_cast<double>(rectsArea) / boundsArea);
696
697     return wastedSpace <= wastedSpaceThreshold;
698 }
699
700 void DrawingAreaCoordinatedGraphics::display(UpdateInfo& updateInfo)
701 {
702     ASSERT(!m_isPaintingSuspended);
703     ASSERT(!m_layerTreeHost);
704     ASSERT(!m_webPage.size().isEmpty());
705
706     m_webPage.layoutIfNeeded();
707     m_webPage.flushPendingEditorStateUpdate();
708
709     // The layout may have put the page into accelerated compositing mode. If the LayerTreeHost is
710     // in charge of displaying, we have nothing more to do.
711     if (m_layerTreeHost)
712         return;
713
714     m_webPage.willDisplayPage();
715     updateInfo.viewSize = m_webPage.size();
716     updateInfo.deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
717
718     IntRect bounds = m_dirtyRegion.bounds();
719     ASSERT(m_webPage.bounds().contains(bounds));
720
721     IntSize bitmapSize = bounds.size();
722     float deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor();
723     bitmapSize.scale(deviceScaleFactor);
724     auto bitmap = ShareableBitmap::createShareable(bitmapSize, { });
725     if (!bitmap)
726         return;
727
728     if (!bitmap->createHandle(updateInfo.bitmapHandle))
729         return;
730
731     Vector<IntRect> rects = m_dirtyRegion.rects();
732     if (shouldPaintBoundsRect(bounds, rects)) {
733         rects.clear();
734         rects.append(bounds);
735     }
736
737     updateInfo.scrollRect = m_scrollRect;
738     updateInfo.scrollOffset = m_scrollOffset;
739
740     m_dirtyRegion = Region();
741     m_scrollRect = IntRect();
742     m_scrollOffset = IntSize();
743
744     auto graphicsContext = bitmap->createGraphicsContext();
745     graphicsContext->applyDeviceScaleFactor(deviceScaleFactor);
746
747     updateInfo.updateRectBounds = bounds;
748
749     graphicsContext->translate(-bounds.x(), -bounds.y());
750
751     for (const auto& rect : rects) {
752         m_webPage.drawRect(*graphicsContext, rect);
753         updateInfo.updateRects.append(rect);
754     }
755
756     // Layout can trigger more calls to setNeedsDisplay and we don't want to process them
757     // until the UI process has painted the update, so we stop the timer here.
758     m_displayTimer.stop();
759 }
760
761 } // namespace WebKit