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