1/*
2 * Copyright (C) 2012-2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "RemoteLayerTreeDrawingArea.h"
28
29#import "DrawingAreaProxyMessages.h"
30#import "GraphicsLayerCARemote.h"
31#import "PlatformCALayerRemote.h"
32#import "RemoteLayerBackingStoreCollection.h"
33#import "RemoteLayerTreeContext.h"
34#import "RemoteLayerTreeDisplayRefreshMonitor.h"
35#import "RemoteLayerTreeDrawingAreaProxyMessages.h"
36#import "RemoteScrollingCoordinator.h"
37#import "RemoteScrollingCoordinatorTransaction.h"
38#import "WebPage.h"
39#import "WebProcess.h"
40#import <WebCore/Frame.h>
41#import <WebCore/FrameView.h>
42#import <WebCore/MainFrame.h>
43#import <WebCore/RenderLayerCompositor.h>
44#import <WebCore/RenderView.h>
45#import <WebCore/Settings.h>
46#import <WebCore/TiledBacking.h>
47#import <QuartzCore/QuartzCore.h>
48
49using namespace WebCore;
50
51namespace WebKit {
52
53RemoteLayerTreeDrawingArea::RemoteLayerTreeDrawingArea(WebPage& webPage, const WebPageCreationParameters&)
54    : DrawingArea(DrawingAreaTypeRemoteLayerTree, webPage)
55    , m_remoteLayerTreeContext(std::make_unique<RemoteLayerTreeContext>(webPage))
56    , m_rootLayer(GraphicsLayer::create(graphicsLayerFactory(), *this))
57    , m_exposedRect(FloatRect::infiniteRect())
58    , m_scrolledExposedRect(FloatRect::infiniteRect())
59    , m_layerFlushTimer(this, &RemoteLayerTreeDrawingArea::layerFlushTimerFired)
60    , m_isFlushingSuspended(false)
61    , m_hasDeferredFlush(false)
62    , m_isThrottlingLayerFlushes(false)
63    , m_isLayerFlushThrottlingTemporarilyDisabledForInteraction(false)
64    , m_isInitialThrottledLayerFlush(false)
65    , m_waitingForBackingStoreSwap(false)
66    , m_hadFlushDeferredWhileWaitingForBackingStoreSwap(false)
67    , m_displayRefreshMonitorsToNotify(nullptr)
68    , m_currentTransactionID(0)
69{
70    webPage.corePage()->settings().setForceCompositingMode(true);
71#if PLATFORM(IOS)
72    webPage.corePage()->settings().setDelegatesPageScaling(true);
73#endif
74
75    m_commitQueue = dispatch_queue_create("com.apple.WebKit.WebContent.RemoteLayerTreeDrawingArea.CommitQueue", nullptr);
76}
77
78RemoteLayerTreeDrawingArea::~RemoteLayerTreeDrawingArea()
79{
80    dispatch_release(m_commitQueue);
81}
82
83void RemoteLayerTreeDrawingArea::setNeedsDisplay()
84{
85}
86
87void RemoteLayerTreeDrawingArea::setNeedsDisplayInRect(const IntRect&)
88{
89}
90
91void RemoteLayerTreeDrawingArea::scroll(const IntRect& scrollRect, const IntSize& scrollDelta)
92{
93}
94
95GraphicsLayerFactory* RemoteLayerTreeDrawingArea::graphicsLayerFactory()
96{
97    return m_remoteLayerTreeContext.get();
98}
99
100PassRefPtr<DisplayRefreshMonitor> RemoteLayerTreeDrawingArea::createDisplayRefreshMonitor(PlatformDisplayID displayID)
101{
102    RefPtr<RemoteLayerTreeDisplayRefreshMonitor> monitor = RemoteLayerTreeDisplayRefreshMonitor::create(displayID, *this);
103    m_displayRefreshMonitors.add(monitor.get());
104    return monitor.release();
105}
106
107void RemoteLayerTreeDrawingArea::willDestroyDisplayRefreshMonitor(DisplayRefreshMonitor* monitor)
108{
109    auto remoteMonitor = static_cast<RemoteLayerTreeDisplayRefreshMonitor*>(monitor);
110    m_displayRefreshMonitors.remove(remoteMonitor);
111
112    if (m_displayRefreshMonitorsToNotify)
113        m_displayRefreshMonitorsToNotify->remove(remoteMonitor);
114}
115
116void RemoteLayerTreeDrawingArea::setRootCompositingLayer(GraphicsLayer* rootLayer)
117{
118    Vector<GraphicsLayer*> children;
119    if (rootLayer) {
120        children.append(rootLayer);
121        children.append(m_webPage.pageOverlayController().viewOverlayRootLayer());
122    }
123    m_rootLayer->setChildren(children);
124
125    scheduleCompositingLayerFlush();
126}
127
128void RemoteLayerTreeDrawingArea::updateGeometry(const IntSize& viewSize, const IntSize& layerPosition)
129{
130    m_viewSize = viewSize;
131    m_webPage.setSize(viewSize);
132
133    scheduleCompositingLayerFlush();
134
135    m_webPage.send(Messages::DrawingAreaProxy::DidUpdateGeometry());
136}
137
138bool RemoteLayerTreeDrawingArea::shouldUseTiledBackingForFrameView(const FrameView* frameView)
139{
140    return frameView && frameView->frame().isMainFrame();
141}
142
143void RemoteLayerTreeDrawingArea::updatePreferences(const WebPreferencesStore&)
144{
145    Settings& settings = m_webPage.corePage()->settings();
146
147    // Fixed position elements need to be composited and create stacking contexts
148    // in order to be scrolled by the ScrollingCoordinator.
149    settings.setAcceleratedCompositingForFixedPositionEnabled(true);
150    settings.setFixedPositionCreatesStackingContext(true);
151
152    m_rootLayer->setShowDebugBorder(settings.showDebugBorders());
153}
154
155#if PLATFORM(IOS)
156void RemoteLayerTreeDrawingArea::setDeviceScaleFactor(float deviceScaleFactor)
157{
158    m_webPage.setDeviceScaleFactor(deviceScaleFactor);
159}
160#endif
161
162void RemoteLayerTreeDrawingArea::setLayerTreeStateIsFrozen(bool isFrozen)
163{
164    if (m_isFlushingSuspended == isFrozen)
165        return;
166
167    m_isFlushingSuspended = isFrozen;
168
169    if (!m_isFlushingSuspended && m_hasDeferredFlush) {
170        m_hasDeferredFlush = false;
171        scheduleCompositingLayerFlush();
172    }
173}
174
175void RemoteLayerTreeDrawingArea::forceRepaint()
176{
177    if (m_isFlushingSuspended)
178        return;
179
180    for (Frame* frame = &m_webPage.corePage()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
181        FrameView* frameView = frame->view();
182        if (!frameView || !frameView->tiledBacking())
183            continue;
184
185        frameView->tiledBacking()->forceRepaint();
186    }
187
188    flushLayers();
189}
190
191void RemoteLayerTreeDrawingArea::acceleratedAnimationDidStart(uint64_t layerID, const String& key, double startTime)
192{
193    m_remoteLayerTreeContext->animationDidStart(layerID, key, startTime);
194}
195
196void RemoteLayerTreeDrawingArea::setExposedRect(const FloatRect& exposedRect)
197{
198    m_exposedRect = exposedRect;
199    updateScrolledExposedRect();
200}
201
202#if PLATFORM(IOS)
203void RemoteLayerTreeDrawingArea::setExposedContentRect(const FloatRect& exposedContentRect)
204{
205    FrameView* frameView = m_webPage.mainFrameView();
206    if (!frameView)
207        return;
208    if (frameView->exposedContentRect() == exposedContentRect)
209        return;
210
211    frameView->setExposedContentRect(exposedContentRect);
212    scheduleCompositingLayerFlush();
213}
214#endif
215
216void RemoteLayerTreeDrawingArea::updateScrolledExposedRect()
217{
218    FrameView* frameView = m_webPage.mainFrameView();
219    if (!frameView)
220        return;
221
222    m_scrolledExposedRect = m_exposedRect;
223
224#if !PLATFORM(IOS)
225    if (!m_exposedRect.isInfinite()) {
226        IntPoint scrollPositionWithOrigin = frameView->scrollPosition() + toIntSize(frameView->scrollOrigin());
227        m_scrolledExposedRect.moveBy(scrollPositionWithOrigin);
228    }
229#endif
230
231    frameView->setExposedRect(m_scrolledExposedRect);
232
233    m_webPage.pageOverlayController().didChangeExposedRect();
234}
235
236TiledBacking* RemoteLayerTreeDrawingArea::mainFrameTiledBacking() const
237{
238    FrameView* frameView = m_webPage.mainFrameView();
239    return frameView ? frameView->tiledBacking() : nullptr;
240}
241
242void RemoteLayerTreeDrawingArea::scheduleCompositingLayerFlushImmediately()
243{
244    m_layerFlushTimer.startOneShot(0_ms);
245}
246
247void RemoteLayerTreeDrawingArea::scheduleCompositingLayerFlush()
248{
249    if (m_isFlushingSuspended) {
250        m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = false;
251        m_hasDeferredFlush = true;
252        return;
253    }
254    if (m_isLayerFlushThrottlingTemporarilyDisabledForInteraction) {
255        m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = false;
256        scheduleCompositingLayerFlushImmediately();
257        return;
258    }
259
260    if (m_layerFlushTimer.isActive())
261        return;
262
263    const auto initialFlushDelay = 500_ms;
264    const auto flushDelay = 1500_ms;
265    auto throttleDelay = m_isThrottlingLayerFlushes ? (m_isInitialThrottledLayerFlush ? initialFlushDelay : flushDelay) : 0_ms;
266    m_isInitialThrottledLayerFlush = false;
267
268    m_layerFlushTimer.startOneShot(throttleDelay);
269}
270
271bool RemoteLayerTreeDrawingArea::adjustLayerFlushThrottling(WebCore::LayerFlushThrottleState::Flags flags)
272{
273    if (flags & WebCore::LayerFlushThrottleState::UserIsInteracting)
274        m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = true;
275
276    bool wasThrottlingLayerFlushes = m_isThrottlingLayerFlushes;
277    m_isThrottlingLayerFlushes = flags & WebCore::LayerFlushThrottleState::Enabled;
278
279    if (!wasThrottlingLayerFlushes && m_isThrottlingLayerFlushes)
280        m_isInitialThrottledLayerFlush = true;
281
282    // Re-schedule the flush if we stopped throttling.
283    if (wasThrottlingLayerFlushes && !m_isThrottlingLayerFlushes && m_layerFlushTimer.isActive()) {
284        m_layerFlushTimer.stop();
285        scheduleCompositingLayerFlush();
286    }
287    return true;
288}
289
290void RemoteLayerTreeDrawingArea::layerFlushTimerFired(WebCore::Timer<RemoteLayerTreeDrawingArea>*)
291{
292    flushLayers();
293}
294
295void RemoteLayerTreeDrawingArea::flushLayers()
296{
297    if (!m_rootLayer)
298        return;
299
300    if (m_isFlushingSuspended) {
301        m_hasDeferredFlush = true;
302        return;
303    }
304
305    if (m_waitingForBackingStoreSwap) {
306        m_hadFlushDeferredWhileWaitingForBackingStoreSwap = true;
307        return;
308    }
309
310    RELEASE_ASSERT(!m_pendingBackingStoreFlusher || m_pendingBackingStoreFlusher->hasFlushed());
311
312    RemoteLayerBackingStoreCollection& backingStoreCollection = m_remoteLayerTreeContext->backingStoreCollection();
313    backingStoreCollection.willFlushLayers();
314
315    m_webPage.layoutIfNeeded();
316
317    FloatRect visibleRect(FloatPoint(), m_viewSize);
318    visibleRect.intersect(m_scrolledExposedRect);
319    m_webPage.pageOverlayController().flushPageOverlayLayers(visibleRect);
320    m_webPage.mainFrameView()->flushCompositingStateIncludingSubframes();
321    m_rootLayer->flushCompositingStateForThisLayerOnly();
322
323    // FIXME: Minimize these transactions if nothing changed.
324    RemoteLayerTreeTransaction layerTransaction;
325    layerTransaction.setTransactionID(takeNextTransactionID());
326    layerTransaction.setCallbackIDs(WTF::move(m_pendingCallbackIDs));
327    m_remoteLayerTreeContext->buildTransaction(layerTransaction, *toGraphicsLayerCARemote(m_rootLayer.get())->platformCALayer());
328    backingStoreCollection.willCommitLayerTree(layerTransaction);
329    m_webPage.willCommitLayerTree(layerTransaction);
330
331    RemoteScrollingCoordinatorTransaction scrollingTransaction;
332#if ENABLE(ASYNC_SCROLLING)
333    if (m_webPage.scrollingCoordinator())
334        toRemoteScrollingCoordinator(m_webPage.scrollingCoordinator())->buildTransaction(scrollingTransaction);
335#endif
336
337    m_waitingForBackingStoreSwap = true;
338
339    m_webPage.send(Messages::RemoteLayerTreeDrawingAreaProxy::WillCommitLayerTree(layerTransaction.transactionID()));
340
341    Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree message(layerTransaction, scrollingTransaction);
342    auto commitEncoder = std::make_unique<IPC::MessageEncoder>(Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree::receiverName(), Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree::name(), m_webPage.pageID());
343    commitEncoder->encode(message.arguments());
344
345    // FIXME: Move all backing store flushing management to RemoteLayerBackingStoreCollection.
346    bool hadAnyChangedBackingStore = false;
347    Vector<RetainPtr<CGContextRef>> contextsToFlush;
348    for (auto& layer : layerTransaction.changedLayers()) {
349        if (layer->properties().changedProperties & RemoteLayerTreeTransaction::LayerChanges::BackingStoreChanged) {
350            hadAnyChangedBackingStore = true;
351            if (layer->properties().backingStore) {
352                if (auto contextPendingFlush = layer->properties().backingStore->takeFrontContextPendingFlush())
353                    contextsToFlush.append(contextPendingFlush);
354            }
355        }
356
357        layer->didCommit();
358    }
359
360    backingStoreCollection.didFlushLayers();
361
362    if (hadAnyChangedBackingStore)
363        backingStoreCollection.scheduleVolatilityTimer();
364
365    RefPtr<BackingStoreFlusher> backingStoreFlusher = BackingStoreFlusher::create(WebProcess::shared().parentProcessConnection(), WTF::move(commitEncoder), WTF::move(contextsToFlush));
366    m_pendingBackingStoreFlusher = backingStoreFlusher;
367
368    uint64_t pageID = m_webPage.pageID();
369    dispatch_async(m_commitQueue, [backingStoreFlusher, pageID] {
370        backingStoreFlusher->flush();
371
372        if (WebPage *webPage = WebProcess::shared().webPage(pageID)) {
373            std::chrono::milliseconds timestamp = std::chrono::milliseconds(static_cast<std::chrono::milliseconds::rep>(monotonicallyIncreasingTime() * 1000));
374            dispatch_async(dispatch_get_main_queue(), ^{
375                webPage->didFlushLayerTreeAtTime(timestamp);
376            });
377        }
378    });
379}
380
381void RemoteLayerTreeDrawingArea::didUpdate()
382{
383    // FIXME: This should use a counted replacement for setLayerTreeStateIsFrozen, but
384    // the callers of that function are not strictly paired.
385
386    m_waitingForBackingStoreSwap = false;
387
388    if (m_hadFlushDeferredWhileWaitingForBackingStoreSwap) {
389        scheduleCompositingLayerFlush();
390        m_hadFlushDeferredWhileWaitingForBackingStoreSwap = false;
391    }
392
393    // This empty transaction serves to trigger CA's garbage collection of IOSurfaces. See <rdar://problem/16110687>
394    [CATransaction begin];
395    [CATransaction commit];
396
397    HashSet<RemoteLayerTreeDisplayRefreshMonitor*> monitorsToNotify = m_displayRefreshMonitors;
398    ASSERT(!m_displayRefreshMonitorsToNotify);
399    m_displayRefreshMonitorsToNotify = &monitorsToNotify;
400    while (!monitorsToNotify.isEmpty())
401        monitorsToNotify.takeAny()->didUpdateLayers();
402    m_displayRefreshMonitorsToNotify = nullptr;
403}
404
405void RemoteLayerTreeDrawingArea::mainFrameContentSizeChanged(const IntSize& contentsSize)
406{
407    m_rootLayer->setSize(contentsSize);
408    m_webPage.pageOverlayController().didChangeDocumentSize();
409}
410
411bool RemoteLayerTreeDrawingArea::markLayersVolatileImmediatelyIfPossible()
412{
413    return m_remoteLayerTreeContext->backingStoreCollection().markAllBackingStoreVolatileImmediatelyIfPossible();
414}
415
416PassRefPtr<RemoteLayerTreeDrawingArea::BackingStoreFlusher> RemoteLayerTreeDrawingArea::BackingStoreFlusher::create(IPC::Connection* connection, std::unique_ptr<IPC::MessageEncoder> encoder, Vector<RetainPtr<CGContextRef>> contextsToFlush)
417{
418    return adoptRef(new RemoteLayerTreeDrawingArea::BackingStoreFlusher(connection, WTF::move(encoder), WTF::move(contextsToFlush)));
419}
420
421RemoteLayerTreeDrawingArea::BackingStoreFlusher::BackingStoreFlusher(IPC::Connection* connection, std::unique_ptr<IPC::MessageEncoder> encoder, Vector<RetainPtr<CGContextRef>> contextsToFlush)
422    : m_connection(connection)
423    , m_commitEncoder(WTF::move(encoder))
424    , m_contextsToFlush(WTF::move(contextsToFlush))
425    , m_hasFlushed(false)
426{
427}
428
429void RemoteLayerTreeDrawingArea::BackingStoreFlusher::flush()
430{
431    ASSERT(!m_hasFlushed);
432
433    for (auto& context : m_contextsToFlush)
434        CGContextFlush(context.get());
435    m_hasFlushed = true;
436
437    m_connection->sendMessage(WTF::move(m_commitEncoder));
438}
439
440void RemoteLayerTreeDrawingArea::viewStateDidChange(ViewState::Flags, bool wantsDidUpdateViewState)
441{
442    // FIXME: Should we suspend painting while not visible, like TiledCoreAnimationDrawingArea? Probably.
443
444    if (wantsDidUpdateViewState)
445        scheduleCompositingLayerFlushImmediately();
446}
447
448void RemoteLayerTreeDrawingArea::addTransactionCallbackID(uint64_t callbackID)
449{
450    m_pendingCallbackIDs.append(static_cast<RemoteLayerTreeTransaction::TransactionCallbackID>(callbackID));
451    scheduleCompositingLayerFlush();
452}
453
454} // namespace WebKit
455