blob: 5e50e0ba1ef1246c6ae08f29d92a830e0db4f237 [file] [log] [blame]
/*
* Copyright (C) 2012-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "RemoteLayerTreeDrawingArea.h"
#import "DrawingAreaProxyMessages.h"
#import "GraphicsLayerCARemote.h"
#import "PlatformCALayerRemote.h"
#import "RemoteLayerBackingStoreCollection.h"
#import "RemoteLayerTreeContext.h"
#import "RemoteLayerTreeDisplayRefreshMonitor.h"
#import "RemoteLayerTreeDrawingAreaProxyMessages.h"
#import "RemoteScrollingCoordinator.h"
#import "RemoteScrollingCoordinatorTransaction.h"
#import "WebPage.h"
#import "WebPageCreationParameters.h"
#import "WebPageProxyMessages.h"
#import "WebProcess.h"
#import <QuartzCore/QuartzCore.h>
#import <WebCore/DebugPageOverlays.h>
#import <WebCore/Frame.h>
#import <WebCore/FrameView.h>
#import <WebCore/InspectorController.h>
#import <WebCore/PageOverlayController.h>
#import <WebCore/RenderLayerCompositor.h>
#import <WebCore/RenderView.h>
#import <WebCore/Settings.h>
#import <WebCore/TiledBacking.h>
#import <pal/spi/cocoa/QuartzCoreSPI.h>
#import <wtf/SetForScope.h>
#import <wtf/SystemTracing.h>
namespace WebKit {
using namespace WebCore;
RemoteLayerTreeDrawingArea::RemoteLayerTreeDrawingArea(WebPage& webPage, const WebPageCreationParameters& parameters)
: DrawingArea(DrawingAreaTypeRemoteLayerTree, parameters.drawingAreaIdentifier, webPage)
, m_remoteLayerTreeContext(std::make_unique<RemoteLayerTreeContext>(webPage))
, m_rootLayer(GraphicsLayer::create(graphicsLayerFactory(), *this))
, m_layerFlushTimer(*this, &RemoteLayerTreeDrawingArea::flushLayers)
{
webPage.corePage()->settings().setForceCompositingMode(true);
#if PLATFORM(IOS_FAMILY)
webPage.corePage()->settings().setDelegatesPageScaling(true);
#endif
m_rootLayer->setName("drawing area root");
m_commitQueue = dispatch_queue_create("com.apple.WebKit.WebContent.RemoteLayerTreeDrawingArea.CommitQueue", nullptr);
// In order to ensure that we get a unique DisplayRefreshMonitor per-DrawingArea (necessary because DisplayRefreshMonitor
// is driven by this class), give each page a unique DisplayID derived from WebPage's unique ID.
// FIXME: While using the high end of the range of DisplayIDs makes a collision with real, non-RemoteLayerTreeDrawingArea
// DisplayIDs less likely, it is not entirely safe to have a RemoteLayerTreeDrawingArea and TiledCoreAnimationDrawingArea
// coeexist in the same process.
webPage.windowScreenDidChange(std::numeric_limits<uint32_t>::max() - webPage.pageID().toUInt64());
}
RemoteLayerTreeDrawingArea::~RemoteLayerTreeDrawingArea()
{
dispatch_release(m_commitQueue);
}
void RemoteLayerTreeDrawingArea::setNeedsDisplay()
{
}
void RemoteLayerTreeDrawingArea::setNeedsDisplayInRect(const IntRect&)
{
}
void RemoteLayerTreeDrawingArea::scroll(const IntRect& scrollRect, const IntSize& scrollDelta)
{
}
GraphicsLayerFactory* RemoteLayerTreeDrawingArea::graphicsLayerFactory()
{
return m_remoteLayerTreeContext.get();
}
RefPtr<DisplayRefreshMonitor> RemoteLayerTreeDrawingArea::createDisplayRefreshMonitor(PlatformDisplayID displayID)
{
auto monitor = RemoteLayerTreeDisplayRefreshMonitor::create(displayID, *this);
m_displayRefreshMonitors.add(monitor.ptr());
return WTFMove(monitor);
}
void RemoteLayerTreeDrawingArea::willDestroyDisplayRefreshMonitor(DisplayRefreshMonitor* monitor)
{
auto remoteMonitor = static_cast<RemoteLayerTreeDisplayRefreshMonitor*>(monitor);
m_displayRefreshMonitors.remove(remoteMonitor);
if (m_displayRefreshMonitorsToNotify)
m_displayRefreshMonitorsToNotify->remove(remoteMonitor);
}
void RemoteLayerTreeDrawingArea::updateRootLayers()
{
Vector<Ref<GraphicsLayer>> children;
if (m_contentLayer) {
children.append(*m_contentLayer);
if (m_viewOverlayRootLayer)
children.append(*m_viewOverlayRootLayer);
}
m_rootLayer->setChildren(WTFMove(children));
}
void RemoteLayerTreeDrawingArea::attachViewOverlayGraphicsLayer(GraphicsLayer* viewOverlayRootLayer)
{
m_viewOverlayRootLayer = viewOverlayRootLayer;
updateRootLayers();
}
void RemoteLayerTreeDrawingArea::setRootCompositingLayer(GraphicsLayer* rootLayer)
{
m_contentLayer = rootLayer;
updateRootLayers();
scheduleCompositingLayerFlush();
}
void RemoteLayerTreeDrawingArea::updateGeometry(const IntSize& viewSize, bool flushSynchronously, const WTF::MachSendRight&)
{
m_viewSize = viewSize;
m_webPage.setSize(viewSize);
scheduleCompositingLayerFlush();
send(Messages::DrawingAreaProxy::DidUpdateGeometry());
}
bool RemoteLayerTreeDrawingArea::shouldUseTiledBackingForFrameView(const FrameView& frameView)
{
return frameView.frame().isMainFrame() || m_webPage.corePage()->settings().asyncFrameScrollingEnabled();
}
void RemoteLayerTreeDrawingArea::updatePreferences(const WebPreferencesStore&)
{
Settings& settings = m_webPage.corePage()->settings();
// Fixed position elements need to be composited and create stacking contexts
// in order to be scrolled by the ScrollingCoordinator.
settings.setAcceleratedCompositingForFixedPositionEnabled(true);
m_rootLayer->setShowDebugBorder(settings.showDebugBorders());
DebugPageOverlays::settingsChanged(*m_webPage.corePage());
}
#if PLATFORM(IOS_FAMILY)
void RemoteLayerTreeDrawingArea::setDeviceScaleFactor(float deviceScaleFactor)
{
m_webPage.setDeviceScaleFactor(deviceScaleFactor);
}
#endif
void RemoteLayerTreeDrawingArea::setLayerTreeStateIsFrozen(bool isFrozen)
{
if (m_isFlushingSuspended == isFrozen)
return;
m_isFlushingSuspended = isFrozen;
if (!m_isFlushingSuspended && m_hasDeferredFlush) {
m_hasDeferredFlush = false;
scheduleInitialDeferredPaint();
}
}
void RemoteLayerTreeDrawingArea::forceRepaint()
{
if (m_isFlushingSuspended)
return;
for (Frame* frame = &m_webPage.corePage()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
FrameView* frameView = frame->view();
if (!frameView || !frameView->tiledBacking())
continue;
frameView->tiledBacking()->forceRepaint();
}
flushLayers();
}
void RemoteLayerTreeDrawingArea::acceleratedAnimationDidStart(uint64_t layerID, const String& key, MonotonicTime startTime)
{
m_remoteLayerTreeContext->animationDidStart(layerID, key, startTime);
}
void RemoteLayerTreeDrawingArea::acceleratedAnimationDidEnd(uint64_t layerID, const String& key)
{
m_remoteLayerTreeContext->animationDidEnd(layerID, key);
}
void RemoteLayerTreeDrawingArea::setViewExposedRect(Optional<WebCore::FloatRect> viewExposedRect)
{
m_viewExposedRect = viewExposedRect;
updateScrolledExposedRect();
}
#if PLATFORM(IOS_FAMILY)
WebCore::FloatRect RemoteLayerTreeDrawingArea::exposedContentRect() const
{
FrameView* frameView = m_webPage.mainFrameView();
if (!frameView)
return FloatRect();
return frameView->exposedContentRect();
}
void RemoteLayerTreeDrawingArea::setExposedContentRect(const FloatRect& exposedContentRect)
{
FrameView* frameView = m_webPage.mainFrameView();
if (!frameView)
return;
if (frameView->exposedContentRect() == exposedContentRect)
return;
frameView->setExposedContentRect(exposedContentRect);
scheduleCompositingLayerFlush();
}
#endif
void RemoteLayerTreeDrawingArea::updateScrolledExposedRect()
{
FrameView* frameView = m_webPage.mainFrameView();
if (!frameView)
return;
m_scrolledViewExposedRect = m_viewExposedRect;
#if !PLATFORM(IOS_FAMILY)
if (m_viewExposedRect)
m_scrolledViewExposedRect.value().moveBy(frameView->scrollOffset());
#endif
frameView->setViewExposedRect(m_scrolledViewExposedRect);
}
TiledBacking* RemoteLayerTreeDrawingArea::mainFrameTiledBacking() const
{
FrameView* frameView = m_webPage.mainFrameView();
return frameView ? frameView->tiledBacking() : nullptr;
}
void RemoteLayerTreeDrawingArea::scheduleCompositingLayerFlushImmediately()
{
m_layerFlushTimer.startOneShot(0_s);
}
void RemoteLayerTreeDrawingArea::scheduleInitialDeferredPaint()
{
ASSERT(!m_isFlushingSuspended);
m_flushingInitialDeferredPaint = true;
if (m_layerFlushTimer.isActive())
return;
scheduleCompositingLayerFlushImmediately();
}
void RemoteLayerTreeDrawingArea::scheduleCompositingLayerFlush()
{
if (m_isFlushingSuspended) {
m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = false;
m_hasDeferredFlush = true;
return;
}
if (m_isLayerFlushThrottlingTemporarilyDisabledForInteraction) {
m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = false;
scheduleCompositingLayerFlushImmediately();
return;
}
if (m_layerFlushTimer.isActive())
return;
const Seconds initialFlushDelay = 500_ms;
const Seconds flushDelay = 1500_ms;
Seconds throttleDelay = m_isThrottlingLayerFlushes ? (m_isInitialThrottledLayerFlush ? initialFlushDelay : flushDelay) : 0_s;
m_isInitialThrottledLayerFlush = false;
m_layerFlushTimer.startOneShot(throttleDelay);
}
bool RemoteLayerTreeDrawingArea::adjustLayerFlushThrottling(WebCore::LayerFlushThrottleState::Flags flags)
{
if (flags & WebCore::LayerFlushThrottleState::UserIsInteracting)
m_isLayerFlushThrottlingTemporarilyDisabledForInteraction = true;
bool wasThrottlingLayerFlushes = m_isThrottlingLayerFlushes;
m_isThrottlingLayerFlushes = flags & WebCore::LayerFlushThrottleState::Enabled;
if (!wasThrottlingLayerFlushes && m_isThrottlingLayerFlushes)
m_isInitialThrottledLayerFlush = true;
// Re-schedule the flush if we stopped throttling.
if (wasThrottlingLayerFlushes && !m_isThrottlingLayerFlushes && m_layerFlushTimer.isActive()) {
m_layerFlushTimer.stop();
scheduleCompositingLayerFlush();
}
return true;
}
void RemoteLayerTreeDrawingArea::addCommitHandlers()
{
if (m_webPage.firstFlushAfterCommit())
return;
[CATransaction addCommitHandler:[retainedPage = makeRefPtr(&m_webPage)] {
if (Page* corePage = retainedPage->corePage()) {
if (Frame* coreFrame = retainedPage->mainFrame())
corePage->inspectorController().willComposite(*coreFrame);
}
} forPhase:kCATransactionPhasePreCommit];
[CATransaction addCommitHandler:[retainedPage = makeRefPtr(&m_webPage)] {
if (Page* corePage = retainedPage->corePage()) {
if (Frame* coreFrame = retainedPage->mainFrame())
corePage->inspectorController().didComposite(*coreFrame);
}
retainedPage->setFirstFlushAfterCommit(false);
} forPhase:kCATransactionPhasePostCommit];
m_webPage.setFirstFlushAfterCommit(true);
}
void RemoteLayerTreeDrawingArea::flushLayers()
{
if (m_isFlushingSuspended) {
m_hasDeferredFlush = true;
return;
}
if (m_waitingForBackingStoreSwap) {
m_hadFlushDeferredWhileWaitingForBackingStoreSwap = true;
return;
}
if (m_flushingInitialDeferredPaint) {
m_flushingInitialDeferredPaint = false;
// Reschedule the flush timer for the second paint if painting is being throttled.
if (m_isThrottlingLayerFlushes)
scheduleCompositingLayerFlush();
}
// This function is not reentrant, e.g. a rAF callback may force repaint.
if (m_inFlushLayers)
return;
SetForScope<bool> change(m_inFlushLayers, true);
m_webPage.updateRendering();
FloatRect visibleRect(FloatPoint(), m_viewSize);
if (m_scrolledViewExposedRect)
visibleRect.intersect(m_scrolledViewExposedRect.value());
addCommitHandlers();
if (m_nextFlushIsForImmediatePaint)
m_webPage.mainFrameView()->invalidateImagesWithAsyncDecodes();
m_webPage.mainFrameView()->flushCompositingStateIncludingSubframes();
// Because our view-relative overlay root layer is not attached to the FrameView's GraphicsLayer tree, we need to flush it manually.
if (m_viewOverlayRootLayer)
m_viewOverlayRootLayer->flushCompositingState(visibleRect);
RELEASE_ASSERT(!m_pendingBackingStoreFlusher || m_pendingBackingStoreFlusher->hasFlushed());
RemoteLayerBackingStoreCollection& backingStoreCollection = m_remoteLayerTreeContext->backingStoreCollection();
backingStoreCollection.willFlushLayers();
m_rootLayer->flushCompositingStateForThisLayerOnly();
// FIXME: Minimize these transactions if nothing changed.
RemoteLayerTreeTransaction layerTransaction;
layerTransaction.setTransactionID(takeNextTransactionID());
layerTransaction.setCallbackIDs(WTFMove(m_pendingCallbackIDs));
m_remoteLayerTreeContext->setNextFlushIsForImmediatePaint(m_nextFlushIsForImmediatePaint);
m_remoteLayerTreeContext->buildTransaction(layerTransaction, *downcast<GraphicsLayerCARemote>(m_rootLayer.get()).platformCALayer());
m_remoteLayerTreeContext->setNextFlushIsForImmediatePaint(false);
backingStoreCollection.willCommitLayerTree(layerTransaction);
m_webPage.willCommitLayerTree(layerTransaction);
layerTransaction.setNewlyReachedPaintingMilestones(m_pendingNewlyReachedPaintingMilestones);
m_pendingNewlyReachedPaintingMilestones = { };
layerTransaction.setActivityStateChangeID(m_activityStateChangeID);
m_activityStateChangeID = ActivityStateChangeAsynchronous;
RemoteScrollingCoordinatorTransaction scrollingTransaction;
#if ENABLE(ASYNC_SCROLLING)
if (m_webPage.scrollingCoordinator())
downcast<RemoteScrollingCoordinator>(*m_webPage.scrollingCoordinator()).buildTransaction(scrollingTransaction);
#endif
m_nextFlushIsForImmediatePaint = false;
m_waitingForBackingStoreSwap = true;
send(Messages::RemoteLayerTreeDrawingAreaProxy::WillCommitLayerTree(layerTransaction.transactionID()));
Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree message(layerTransaction, scrollingTransaction);
auto commitEncoder = std::make_unique<IPC::Encoder>(Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree::receiverName(), Messages::RemoteLayerTreeDrawingAreaProxy::CommitLayerTree::name(), m_identifier.toUInt64());
commitEncoder->encode(message.arguments());
// FIXME: Move all backing store flushing management to RemoteLayerBackingStoreCollection.
bool hadAnyChangedBackingStore = false;
Vector<RetainPtr<CGContextRef>> contextsToFlush;
for (auto& layer : layerTransaction.changedLayers()) {
if (layer->properties().changedProperties & RemoteLayerTreeTransaction::BackingStoreChanged) {
hadAnyChangedBackingStore = true;
if (layer->properties().backingStore) {
if (auto contextPendingFlush = layer->properties().backingStore->takeFrontContextPendingFlush())
contextsToFlush.append(contextPendingFlush);
}
}
layer->didCommit();
}
backingStoreCollection.didFlushLayers();
if (hadAnyChangedBackingStore)
backingStoreCollection.scheduleVolatilityTimer();
RefPtr<BackingStoreFlusher> backingStoreFlusher = BackingStoreFlusher::create(WebProcess::singleton().parentProcessConnection(), WTFMove(commitEncoder), WTFMove(contextsToFlush));
m_pendingBackingStoreFlusher = backingStoreFlusher;
auto pageID = m_webPage.pageID();
dispatch_async(m_commitQueue, [backingStoreFlusher = WTFMove(backingStoreFlusher), pageID] {
backingStoreFlusher->flush();
MonotonicTime timestamp = MonotonicTime::now();
dispatch_async(dispatch_get_main_queue(), [pageID, timestamp] {
if (WebPage* webPage = WebProcess::singleton().webPage(pageID))
webPage->didFlushLayerTreeAtTime(timestamp);
});
});
}
void RemoteLayerTreeDrawingArea::didUpdate()
{
// FIXME: This should use a counted replacement for setLayerTreeStateIsFrozen, but
// the callers of that function are not strictly paired.
m_waitingForBackingStoreSwap = false;
if (m_hadFlushDeferredWhileWaitingForBackingStoreSwap) {
scheduleCompositingLayerFlush();
m_hadFlushDeferredWhileWaitingForBackingStoreSwap = false;
}
// This empty transaction serves to trigger CA's garbage collection of IOSurfaces. See <rdar://problem/16110687>
[CATransaction begin];
[CATransaction commit];
HashSet<RemoteLayerTreeDisplayRefreshMonitor*> monitorsToNotify = m_displayRefreshMonitors;
ASSERT(!m_displayRefreshMonitorsToNotify);
m_displayRefreshMonitorsToNotify = &monitorsToNotify;
while (!monitorsToNotify.isEmpty())
monitorsToNotify.takeAny()->didUpdateLayers();
m_displayRefreshMonitorsToNotify = nullptr;
}
void RemoteLayerTreeDrawingArea::mainFrameContentSizeChanged(const IntSize& contentsSize)
{
m_rootLayer->setSize(contentsSize);
}
bool RemoteLayerTreeDrawingArea::markLayersVolatileImmediatelyIfPossible()
{
return m_remoteLayerTreeContext->backingStoreCollection().markAllBackingStoreVolatileImmediatelyIfPossible();
}
Ref<RemoteLayerTreeDrawingArea::BackingStoreFlusher> RemoteLayerTreeDrawingArea::BackingStoreFlusher::create(IPC::Connection* connection, std::unique_ptr<IPC::Encoder> encoder, Vector<RetainPtr<CGContextRef>> contextsToFlush)
{
return adoptRef(*new RemoteLayerTreeDrawingArea::BackingStoreFlusher(connection, WTFMove(encoder), WTFMove(contextsToFlush)));
}
RemoteLayerTreeDrawingArea::BackingStoreFlusher::BackingStoreFlusher(IPC::Connection* connection, std::unique_ptr<IPC::Encoder> encoder, Vector<RetainPtr<CGContextRef>> contextsToFlush)
: m_connection(connection)
, m_commitEncoder(WTFMove(encoder))
, m_contextsToFlush(WTFMove(contextsToFlush))
, m_hasFlushed(false)
{
}
void RemoteLayerTreeDrawingArea::BackingStoreFlusher::flush()
{
ASSERT(!m_hasFlushed);
TraceScope tracingScope(BackingStoreFlushStart, BackingStoreFlushEnd);
for (auto& context : m_contextsToFlush)
CGContextFlush(context.get());
m_hasFlushed = true;
m_connection->sendMessage(WTFMove(m_commitEncoder), { });
}
void RemoteLayerTreeDrawingArea::activityStateDidChange(OptionSet<WebCore::ActivityState::Flag>, ActivityStateChangeID activityStateChangeID, const Vector<CallbackID>& callbackIDs)
{
// FIXME: Should we suspend painting while not visible, like TiledCoreAnimationDrawingArea? Probably.
if (activityStateChangeID != ActivityStateChangeAsynchronous) {
m_nextFlushIsForImmediatePaint = true;
m_activityStateChangeID = activityStateChangeID;
scheduleCompositingLayerFlushImmediately();
}
// FIXME: We may want to match behavior in TiledCoreAnimationDrawingArea by firing these callbacks after the next compositing flush, rather than immediately after
// handling an activity state change.
for (auto& callbackID : callbackIDs)
m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
}
void RemoteLayerTreeDrawingArea::addTransactionCallbackID(CallbackID callbackID)
{
// Assume that if someone is listening for this transaction's completion, that they want it to
// be a "complete" paint (including images that would normally be asynchronously decoding).
m_nextFlushIsForImmediatePaint = true;
m_pendingCallbackIDs.append(static_cast<RemoteLayerTreeTransaction::TransactionCallbackID>(callbackID));
scheduleCompositingLayerFlush();
}
void RemoteLayerTreeDrawingArea::adoptLayersFromDrawingArea(DrawingArea& oldDrawingArea)
{
RELEASE_ASSERT(oldDrawingArea.type() == type());
RemoteLayerTreeDrawingArea& oldRemoteDrawingArea = static_cast<RemoteLayerTreeDrawingArea&>(oldDrawingArea);
m_remoteLayerTreeContext->adoptLayersFromContext(*oldRemoteDrawingArea.m_remoteLayerTreeContext);
}
} // namespace WebKit