Assertion failure !isInAcceleratedCompositingMode() in DrawingAreaProxyCoordinatedGra...
[WebKit-https.git] / Source / WebKit / UIProcess / CoordinatedGraphics / DrawingAreaProxyCoordinatedGraphics.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) 2016-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 "DrawingAreaProxyCoordinatedGraphics.h"
30
31 #include "DrawingAreaMessages.h"
32 #include "DrawingAreaProxyMessages.h"
33 #include "LayerTreeContext.h"
34 #include "UpdateInfo.h"
35 #include "WebPageProxy.h"
36 #include "WebPreferences.h"
37 #include "WebProcessProxy.h"
38 #include <WebCore/Region.h>
39
40 #if PLATFORM(GTK)
41 #include <gtk/gtk.h>
42 #endif
43
44 #if PLATFORM(WAYLAND)
45 #include "WaylandCompositor.h"
46 #include <WebCore/PlatformDisplay.h>
47 #endif
48
49 #if USE(GLIB_EVENT_LOOP)
50 #include <wtf/glib/RunLoopSourcePriority.h>
51 #endif
52
53 namespace WebKit {
54 using namespace WebCore;
55
56 DrawingAreaProxyCoordinatedGraphics::DrawingAreaProxyCoordinatedGraphics(WebPageProxy& webPageProxy, WebProcessProxy& process)
57     : DrawingAreaProxy(DrawingAreaTypeCoordinatedGraphics, webPageProxy, process)
58 #if !PLATFORM(WPE)
59     , m_discardBackingStoreTimer(RunLoop::current(), this, &DrawingAreaProxyCoordinatedGraphics::discardBackingStore)
60 #endif
61 {
62 #if USE(GLIB_EVENT_LOOP) && !PLATFORM(WPE)
63     m_discardBackingStoreTimer.setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer);
64 #endif
65 }
66
67 DrawingAreaProxyCoordinatedGraphics::~DrawingAreaProxyCoordinatedGraphics()
68 {
69     // Make sure to exit accelerated compositing mode.
70     if (isInAcceleratedCompositingMode())
71         exitAcceleratedCompositingMode();
72 }
73
74 #if !PLATFORM(WPE)
75 void DrawingAreaProxyCoordinatedGraphics::paint(BackingStore::PlatformGraphicsContext context, const IntRect& rect, Region& unpaintedRegion)
76 {
77     unpaintedRegion = rect;
78
79     if (isInAcceleratedCompositingMode())
80         return;
81
82     ASSERT(m_currentBackingStoreStateID <= m_nextBackingStoreStateID);
83     if (m_currentBackingStoreStateID < m_nextBackingStoreStateID) {
84         // Tell the web process to do a full backing store update now, in case we previously told
85         // it about our next state but didn't request an immediate update.
86         sendUpdateBackingStoreState(RespondImmediately);
87
88         // If we haven't yet received our first bits from the WebProcess then don't paint anything.
89         if (!m_hasReceivedFirstUpdate)
90             return;
91
92         if (m_isWaitingForDidUpdateBackingStoreState) {
93             // Wait for a DidUpdateBackingStoreState message that contains the new bits before we paint
94             // what's currently in the backing store.
95             waitForAndDispatchDidUpdateBackingStoreState();
96         }
97
98         // Dispatching DidUpdateBackingStoreState (either beneath sendUpdateBackingStoreState or
99         // beneath waitForAndDispatchDidUpdateBackingStoreState) could destroy our backing store or
100         // change the compositing mode.
101         if (!m_backingStore || isInAcceleratedCompositingMode())
102             return;
103     } else {
104         ASSERT(!m_isWaitingForDidUpdateBackingStoreState);
105         if (!m_backingStore) {
106             // The view has asked us to paint before the web process has painted anything. There's
107             // nothing we can do.
108             return;
109         }
110     }
111
112     m_backingStore->paint(context, rect);
113     unpaintedRegion.subtract(IntRect(IntPoint(), m_backingStore->size()));
114
115     discardBackingStoreSoon();
116 }
117 #endif
118
119 void DrawingAreaProxyCoordinatedGraphics::sizeDidChange()
120 {
121     backingStoreStateDidChange(RespondImmediately);
122 }
123
124 void DrawingAreaProxyCoordinatedGraphics::deviceScaleFactorDidChange()
125 {
126     backingStoreStateDidChange(RespondImmediately);
127 }
128
129 void DrawingAreaProxyCoordinatedGraphics::waitForBackingStoreUpdateOnNextPaint()
130 {
131     m_hasReceivedFirstUpdate = true;
132 }
133
134 void DrawingAreaProxyCoordinatedGraphics::setBackingStoreIsDiscardable(bool isBackingStoreDiscardable)
135 {
136 #if !PLATFORM(WPE)
137     if (m_isBackingStoreDiscardable == isBackingStoreDiscardable)
138         return;
139
140     m_isBackingStoreDiscardable = isBackingStoreDiscardable;
141     if (m_isBackingStoreDiscardable)
142         discardBackingStoreSoon();
143     else
144         m_discardBackingStoreTimer.stop();
145 #endif
146 }
147
148 void DrawingAreaProxyCoordinatedGraphics::update(uint64_t backingStoreStateID, const UpdateInfo& updateInfo)
149 {
150     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
151     if (backingStoreStateID < m_currentBackingStoreStateID)
152         return;
153
154     // FIXME: Handle the case where the view is hidden.
155
156 #if !PLATFORM(WPE)
157     incorporateUpdate(updateInfo);
158 #endif
159     send(Messages::DrawingArea::DidUpdate());
160 }
161
162 void DrawingAreaProxyCoordinatedGraphics::didUpdateBackingStoreState(uint64_t backingStoreStateID, const UpdateInfo& updateInfo, const LayerTreeContext& layerTreeContext)
163 {
164     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_nextBackingStoreStateID);
165     ASSERT_ARG(backingStoreStateID, backingStoreStateID > m_currentBackingStoreStateID);
166     m_currentBackingStoreStateID = backingStoreStateID;
167
168     m_isWaitingForDidUpdateBackingStoreState = false;
169
170     // Stop the responsiveness timer that was started in sendUpdateBackingStoreState.
171     process().responsivenessTimer().stop();
172
173     if (layerTreeContext != m_layerTreeContext) {
174         if (layerTreeContext.isEmpty() && !m_layerTreeContext.isEmpty()) {
175             exitAcceleratedCompositingMode();
176             ASSERT(m_layerTreeContext.isEmpty());
177         } else if (!layerTreeContext.isEmpty() && m_layerTreeContext.isEmpty()) {
178             enterAcceleratedCompositingMode(layerTreeContext);
179             ASSERT(layerTreeContext == m_layerTreeContext);
180         } else {
181             updateAcceleratedCompositingMode(layerTreeContext);
182             ASSERT(layerTreeContext == m_layerTreeContext);
183         }
184     }
185
186     if (m_nextBackingStoreStateID != m_currentBackingStoreStateID)
187         sendUpdateBackingStoreState(RespondImmediately);
188     else {
189         m_hasReceivedFirstUpdate = true;
190
191 #if USE(TEXTURE_MAPPER_GL) && PLATFORM(GTK) && PLATFORM(X11) && !USE(REDIRECTED_XCOMPOSITE_WINDOW)
192         if (m_pendingNativeSurfaceHandleForCompositing) {
193             setNativeSurfaceHandleForCompositing(m_pendingNativeSurfaceHandleForCompositing);
194             m_pendingNativeSurfaceHandleForCompositing = 0;
195         }
196 #endif
197     }
198
199 #if !PLATFORM(WPE)
200     if (isInAcceleratedCompositingMode()) {
201         ASSERT(!m_backingStore);
202         return;
203     }
204
205     // If we have a backing store the right size, reuse it.
206     if (m_backingStore && (m_backingStore->size() != updateInfo.viewSize || m_backingStore->deviceScaleFactor() != updateInfo.deviceScaleFactor))
207         m_backingStore = nullptr;
208     incorporateUpdate(updateInfo);
209 #endif
210 }
211
212 void DrawingAreaProxyCoordinatedGraphics::enterAcceleratedCompositingMode(uint64_t backingStoreStateID, const LayerTreeContext& layerTreeContext)
213 {
214     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
215     if (backingStoreStateID < m_currentBackingStoreStateID)
216         return;
217
218     enterAcceleratedCompositingMode(layerTreeContext);
219 }
220
221 void DrawingAreaProxyCoordinatedGraphics::exitAcceleratedCompositingMode(uint64_t backingStoreStateID, const UpdateInfo& updateInfo)
222 {
223     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
224     if (backingStoreStateID < m_currentBackingStoreStateID)
225         return;
226
227     exitAcceleratedCompositingMode();
228 #if !PLATFORM(WPE)
229     incorporateUpdate(updateInfo);
230 #endif
231 }
232
233 void DrawingAreaProxyCoordinatedGraphics::updateAcceleratedCompositingMode(uint64_t backingStoreStateID, const LayerTreeContext& layerTreeContext)
234 {
235     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
236     if (backingStoreStateID < m_currentBackingStoreStateID)
237         return;
238
239     updateAcceleratedCompositingMode(layerTreeContext);
240 }
241
242 #if !PLATFORM(WPE)
243 void DrawingAreaProxyCoordinatedGraphics::incorporateUpdate(const UpdateInfo& updateInfo)
244 {
245     ASSERT(!isInAcceleratedCompositingMode());
246
247     if (updateInfo.updateRectBounds.isEmpty())
248         return;
249
250     if (!m_backingStore)
251         m_backingStore = std::make_unique<BackingStore>(updateInfo.viewSize, updateInfo.deviceScaleFactor, m_webPageProxy);
252
253     m_backingStore->incorporateUpdate(updateInfo);
254
255     Region damageRegion;
256     if (updateInfo.scrollRect.isEmpty()) {
257         for (const auto& rect : updateInfo.updateRects)
258             damageRegion.unite(rect);
259     } else
260         damageRegion = IntRect(IntPoint(), m_webPageProxy.viewSize());
261     m_webPageProxy.setViewNeedsDisplay(damageRegion);
262 }
263 #endif
264
265 bool DrawingAreaProxyCoordinatedGraphics::alwaysUseCompositing() const
266 {
267     return m_webPageProxy.preferences().acceleratedCompositingEnabled() && m_webPageProxy.preferences().forceCompositingMode();
268 }
269
270 void DrawingAreaProxyCoordinatedGraphics::enterAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext)
271 {
272     ASSERT(!isInAcceleratedCompositingMode());
273 #if !PLATFORM(WPE)
274     m_backingStore = nullptr;
275 #endif
276     m_layerTreeContext = layerTreeContext;
277     m_webPageProxy.enterAcceleratedCompositingMode(layerTreeContext);
278 }
279
280 void DrawingAreaProxyCoordinatedGraphics::exitAcceleratedCompositingMode()
281 {
282     ASSERT(isInAcceleratedCompositingMode());
283
284     m_layerTreeContext = { };
285     m_webPageProxy.exitAcceleratedCompositingMode();
286 }
287
288 void DrawingAreaProxyCoordinatedGraphics::updateAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext)
289 {
290     ASSERT(isInAcceleratedCompositingMode());
291
292     m_layerTreeContext = layerTreeContext;
293     m_webPageProxy.updateAcceleratedCompositingMode(layerTreeContext);
294 }
295
296 void DrawingAreaProxyCoordinatedGraphics::backingStoreStateDidChange(RespondImmediatelyOrNot respondImmediatelyOrNot)
297 {
298     ++m_nextBackingStoreStateID;
299     sendUpdateBackingStoreState(respondImmediatelyOrNot);
300 }
301
302 void DrawingAreaProxyCoordinatedGraphics::sendUpdateBackingStoreState(RespondImmediatelyOrNot respondImmediatelyOrNot)
303 {
304     ASSERT(m_currentBackingStoreStateID < m_nextBackingStoreStateID);
305
306     if (!m_webPageProxy.hasRunningProcess())
307         return;
308
309     if (m_isWaitingForDidUpdateBackingStoreState)
310         return;
311
312     if (m_webPageProxy.viewSize().isEmpty() && !m_webPageProxy.useFixedLayout())
313         return;
314
315     m_isWaitingForDidUpdateBackingStoreState = respondImmediatelyOrNot == RespondImmediately;
316
317     send(Messages::DrawingArea::UpdateBackingStoreState(m_nextBackingStoreStateID, respondImmediatelyOrNot == RespondImmediately, m_webPageProxy.deviceScaleFactor(), m_size, m_scrollOffset));
318     m_scrollOffset = IntSize();
319
320     if (m_isWaitingForDidUpdateBackingStoreState) {
321         // Start the responsiveness timer. We will stop it when we hear back from the WebProcess
322         // in didUpdateBackingStoreState.
323         process().responsivenessTimer().start();
324     }
325
326     if (m_isWaitingForDidUpdateBackingStoreState && !m_layerTreeContext.isEmpty()) {
327         // Wait for the DidUpdateBackingStoreState message. Normally we do this in DrawingAreaProxyCoordinatedGraphics::paint, but that
328         // function is never called when in accelerated compositing mode.
329         waitForAndDispatchDidUpdateBackingStoreState();
330     }
331 }
332
333 void DrawingAreaProxyCoordinatedGraphics::waitForAndDispatchDidUpdateBackingStoreState()
334 {
335     ASSERT(m_isWaitingForDidUpdateBackingStoreState);
336
337     if (!m_webPageProxy.hasRunningProcess())
338         return;
339     if (process().state() == WebProcessProxy::State::Launching)
340         return;
341     if (!m_webPageProxy.isViewVisible())
342         return;
343 #if PLATFORM(WAYLAND) && USE(EGL)
344     // Never block the UI process in Wayland when waiting for DidUpdateBackingStoreState after a resize,
345     // because the nested compositor needs to handle the web process requests that happens while resizing.
346     if (PlatformDisplay::sharedDisplay().type() == PlatformDisplay::Type::Wayland && isInAcceleratedCompositingMode())
347         return;
348 #endif
349
350     // FIXME: waitForAndDispatchImmediately will always return the oldest DidUpdateBackingStoreState message that
351     // hasn't yet been processed. But it might be better to skip ahead to some other DidUpdateBackingStoreState
352     // message, if multiple DidUpdateBackingStoreState messages are waiting to be processed. For instance, we could
353     // choose the most recent one, or the one that is closest to our current size.
354
355     // The timeout, in seconds, we use when waiting for a DidUpdateBackingStoreState message when we're asked to paint.
356     process().connection()->waitForAndDispatchImmediately<Messages::DrawingAreaProxy::DidUpdateBackingStoreState>(m_identifier.toUInt64(), Seconds::fromMilliseconds(500));
357 }
358
359 #if !PLATFORM(WPE)
360 void DrawingAreaProxyCoordinatedGraphics::discardBackingStoreSoon()
361 {
362     if (!m_backingStore || !m_isBackingStoreDiscardable || m_discardBackingStoreTimer.isActive())
363         return;
364
365     // We'll wait this many seconds after the last paint before throwing away our backing store to save memory.
366     // FIXME: It would be smarter to make this delay based on how expensive painting is. See <http://webkit.org/b/55733>.
367     static const Seconds discardBackingStoreDelay = 2_s;
368
369     m_discardBackingStoreTimer.startOneShot(discardBackingStoreDelay);
370 }
371
372 void DrawingAreaProxyCoordinatedGraphics::discardBackingStore()
373 {
374     if (!m_backingStore)
375         return;
376     m_backingStore = nullptr;
377     backingStoreStateDidChange(DoNotRespondImmediately);
378 }
379 #endif
380
381 #if USE(TEXTURE_MAPPER_GL) && PLATFORM(GTK) && PLATFORM(X11) && !USE(REDIRECTED_XCOMPOSITE_WINDOW)
382 void DrawingAreaProxyCoordinatedGraphics::setNativeSurfaceHandleForCompositing(uint64_t handle)
383 {
384     if (!m_hasReceivedFirstUpdate) {
385         m_pendingNativeSurfaceHandleForCompositing = handle;
386         return;
387     }
388     send(Messages::DrawingArea::SetNativeSurfaceHandleForCompositing(handle), IPC::SendOption::DispatchMessageEvenWhenWaitingForSyncReply);
389 }
390
391 void DrawingAreaProxyCoordinatedGraphics::destroyNativeSurfaceHandleForCompositing()
392 {
393     if (m_pendingNativeSurfaceHandleForCompositing) {
394         m_pendingNativeSurfaceHandleForCompositing = 0;
395         return;
396     }
397     bool handled;
398     sendSync(Messages::DrawingArea::DestroyNativeSurfaceHandleForCompositing(), Messages::DrawingArea::DestroyNativeSurfaceHandleForCompositing::Reply(handled));
399 }
400 #endif
401
402 DrawingAreaProxyCoordinatedGraphics::DrawingMonitor::DrawingMonitor(WebPageProxy& webPage)
403     : m_timer(RunLoop::main(), this, &DrawingMonitor::stop)
404 #if PLATFORM(GTK)
405     , m_webPage(webPage)
406 #endif
407 {
408 #if USE(GLIB_EVENT_LOOP)
409 #if PLATFORM(GTK)
410     // Give redraws more priority.
411     m_timer.setPriority(GDK_PRIORITY_REDRAW - 10);
412 #else
413     m_timer.setPriority(RunLoopSourcePriority::RunLoopDispatcher);
414 #endif
415 #endif
416 }
417
418 DrawingAreaProxyCoordinatedGraphics::DrawingMonitor::~DrawingMonitor()
419 {
420     m_callback = nullptr;
421     stop();
422 }
423
424 int DrawingAreaProxyCoordinatedGraphics::DrawingMonitor::webViewDrawCallback(DrawingAreaProxyCoordinatedGraphics::DrawingMonitor* monitor)
425 {
426     monitor->didDraw();
427     return FALSE;
428 }
429
430 void DrawingAreaProxyCoordinatedGraphics::DrawingMonitor::start(WTF::Function<void(CallbackBase::Error)>&& callback)
431 {
432     m_startTime = MonotonicTime::now();
433     m_callback = WTFMove(callback);
434 #if PLATFORM(GTK)
435     g_signal_connect_swapped(m_webPage.viewWidget(), "draw", reinterpret_cast<GCallback>(webViewDrawCallback), this);
436     m_timer.startOneShot(1_s);
437 #else
438     m_timer.startOneShot(0_s);
439 #endif
440 }
441
442 void DrawingAreaProxyCoordinatedGraphics::DrawingMonitor::stop()
443 {
444     m_timer.stop();
445 #if PLATFORM(GTK)
446     g_signal_handlers_disconnect_by_func(m_webPage.viewWidget(), reinterpret_cast<gpointer>(webViewDrawCallback), this);
447 #endif
448     m_startTime = MonotonicTime();
449     if (m_callback) {
450         m_callback(CallbackBase::Error::None);
451         m_callback = nullptr;
452     }
453 }
454
455 void DrawingAreaProxyCoordinatedGraphics::DrawingMonitor::didDraw()
456 {
457     // We wait up to 1 second for draw events. If there are several draw events queued quickly,
458     // we want to wait until all of them have been processed, so after receiving a draw, we wait
459     // up to 100ms for the next one or stop.
460     if (MonotonicTime::now() - m_startTime > 1_s)
461         stop();
462     else
463         m_timer.startOneShot(100_ms);
464 }
465
466 void DrawingAreaProxyCoordinatedGraphics::dispatchAfterEnsuringDrawing(WTF::Function<void(CallbackBase::Error)>&& callbackFunction)
467 {
468     if (!m_webPageProxy.hasRunningProcess()) {
469         callbackFunction(CallbackBase::Error::OwnerWasInvalidated);
470         return;
471     }
472
473     if (!m_drawingMonitor)
474         m_drawingMonitor = std::make_unique<DrawingAreaProxyCoordinatedGraphics::DrawingMonitor>(m_webPageProxy);
475     m_drawingMonitor->start(WTFMove(callbackFunction));
476 }
477
478 } // namespace WebKit