1/* 2 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). 3 * Copyright (C) 2013 Company 100, Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 * THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28 29#if USE(COORDINATED_GRAPHICS) 30 31#include "CompositingCoordinator.h" 32 33#include "FrameView.h" 34#include "GraphicsContext.h" 35#include "InspectorController.h" 36#include "MainFrame.h" 37#include "Page.h" 38#include "Settings.h" 39#include <wtf/CurrentTime.h> 40#include <wtf/TemporaryChange.h> 41 42// FIXME: Having this in the platform directory is a layering violation. This does not belong here. 43 44namespace WebCore { 45 46CompositingCoordinator::~CompositingCoordinator() 47{ 48 purgeBackingStores(); 49 50 for (auto& registeredLayer : m_registeredLayers.values()) 51 registeredLayer->setCoordinator(0); 52} 53 54CompositingCoordinator::CompositingCoordinator(Page* page, CompositingCoordinator::Client* client) 55 : m_page(page) 56 , m_client(client) 57 , m_rootCompositingLayer(0) 58 , m_isPurging(false) 59 , m_isFlushingLayerChanges(false) 60 , m_shouldSyncFrame(false) 61 , m_didInitializeRootCompositingLayer(false) 62 , m_releaseInactiveAtlasesTimer(this, &CompositingCoordinator::releaseInactiveAtlasesTimerFired) 63#if ENABLE(REQUEST_ANIMATION_FRAME) 64 , m_lastAnimationServiceTime(0) 65#endif 66{ 67 m_page->settings().setApplyDeviceScaleFactorInCompositor(true); 68 69 // This is a temporary way to enable this only in the GL case, until TextureMapperImageBuffer is removed. 70 // See https://bugs.webkit.org/show_bug.cgi?id=114869 71 CoordinatedGraphicsLayer::setShouldSupportContentsTiling(true); 72} 73 74void CompositingCoordinator::setRootCompositingLayer(GraphicsLayer* compositingLayer, GraphicsLayer* overlayLayer) 75{ 76 if (m_rootCompositingLayer) 77 m_rootCompositingLayer->removeFromParent(); 78 79 m_rootCompositingLayer = compositingLayer; 80 if (m_rootCompositingLayer) 81 m_rootLayer->addChildAtIndex(m_rootCompositingLayer, 0); 82 83 if (overlayLayer) 84 m_rootLayer->addChild(overlayLayer); 85} 86 87void CompositingCoordinator::sizeDidChange(const IntSize& newSize) 88{ 89 m_rootLayer->setSize(newSize); 90 notifyFlushRequired(m_rootLayer.get()); 91} 92 93bool CompositingCoordinator::flushPendingLayerChanges() 94{ 95 TemporaryChange<bool> protector(m_isFlushingLayerChanges, true); 96 97 initializeRootCompositingLayerIfNeeded(); 98 99 m_rootLayer->flushCompositingStateForThisLayerOnly(); 100 m_client->didFlushRootLayer(m_visibleContentsRect); 101 102 bool didSync = m_page->mainFrame().view()->flushCompositingStateIncludingSubframes(); 103 104 toCoordinatedGraphicsLayer(m_rootLayer.get())->updateContentBuffersIncludingSubLayers(); 105 toCoordinatedGraphicsLayer(m_rootLayer.get())->syncPendingStateChangesIncludingSubLayers(); 106 107 flushPendingImageBackingChanges(); 108 109 if (m_shouldSyncFrame) { 110 didSync = true; 111 112 if (m_rootCompositingLayer) { 113 m_state.contentsSize = roundedIntSize(m_rootCompositingLayer->size()); 114 if (CoordinatedGraphicsLayer* contentsLayer = mainContentsLayer()) 115 m_state.coveredRect = contentsLayer->coverRect(); 116 } 117 m_state.scrollPosition = m_visibleContentsRect.location(); 118 119 m_client->commitSceneState(m_state); 120 121 clearPendingStateChanges(); 122 m_shouldSyncFrame = false; 123 } 124 125 return didSync; 126} 127 128void CompositingCoordinator::syncDisplayState() 129{ 130#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER) && !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) 131 // Make sure that any previously registered animation callbacks are being executed before we flush the layers. 132 m_lastAnimationServiceTime = WTF::monotonicallyIncreasingTime(); 133 m_page->mainFrame().view()->serviceScriptedAnimations(m_lastAnimationServiceTime); 134#endif 135 m_page->mainFrame().view()->updateLayoutAndStyleIfNeededRecursive(); 136} 137 138#if ENABLE(REQUEST_ANIMATION_FRAME) 139double CompositingCoordinator::nextAnimationServiceTime() const 140{ 141 // According to the requestAnimationFrame spec, rAF callbacks should not be faster than 60FPS. 142 static const double MinimalTimeoutForAnimations = 1. / 60.; 143 return std::max<double>(0., MinimalTimeoutForAnimations - WTF::monotonicallyIncreasingTime() + m_lastAnimationServiceTime); 144} 145#endif 146 147void CompositingCoordinator::clearPendingStateChanges() 148{ 149 m_state.layersToCreate.clear(); 150 m_state.layersToUpdate.clear(); 151 m_state.layersToRemove.clear(); 152 153 m_state.imagesToCreate.clear(); 154 m_state.imagesToRemove.clear(); 155 m_state.imagesToUpdate.clear(); 156 m_state.imagesToClear.clear(); 157 158 m_state.updateAtlasesToCreate.clear(); 159 m_state.updateAtlasesToRemove.clear(); 160} 161 162void CompositingCoordinator::initializeRootCompositingLayerIfNeeded() 163{ 164 if (m_didInitializeRootCompositingLayer) 165 return; 166 167 m_state.rootCompositingLayer = toCoordinatedGraphicsLayer(m_rootLayer.get())->id(); 168 m_didInitializeRootCompositingLayer = true; 169 m_shouldSyncFrame = true; 170} 171 172void CompositingCoordinator::createRootLayer(const IntSize& size) 173{ 174 ASSERT(!m_rootLayer); 175 // Create a root layer. 176 m_rootLayer = GraphicsLayer::create(this, *this); 177#ifndef NDEBUG 178 m_rootLayer->setName("CompositingCoordinator root layer"); 179#endif 180 m_rootLayer->setDrawsContent(false); 181 m_rootLayer->setSize(size); 182} 183 184void CompositingCoordinator::syncLayerState(CoordinatedLayerID id, CoordinatedGraphicsLayerState& state) 185{ 186 m_shouldSyncFrame = true; 187 m_state.layersToUpdate.append(std::make_pair(id, state)); 188} 189 190PassRefPtr<CoordinatedImageBacking> CompositingCoordinator::createImageBackingIfNeeded(Image* image) 191{ 192 CoordinatedImageBackingID imageID = CoordinatedImageBacking::getCoordinatedImageBackingID(image); 193 ImageBackingMap::iterator it = m_imageBackings.find(imageID); 194 RefPtr<CoordinatedImageBacking> imageBacking; 195 if (it == m_imageBackings.end()) { 196 imageBacking = CoordinatedImageBacking::create(this, image); 197 m_imageBackings.add(imageID, imageBacking); 198 } else 199 imageBacking = it->value; 200 201 return imageBacking; 202} 203 204void CompositingCoordinator::createImageBacking(CoordinatedImageBackingID imageID) 205{ 206 m_state.imagesToCreate.append(imageID); 207} 208 209void CompositingCoordinator::updateImageBacking(CoordinatedImageBackingID imageID, PassRefPtr<CoordinatedSurface> coordinatedSurface) 210{ 211 m_shouldSyncFrame = true; 212 m_state.imagesToUpdate.append(std::make_pair(imageID, coordinatedSurface)); 213} 214 215void CompositingCoordinator::clearImageBackingContents(CoordinatedImageBackingID imageID) 216{ 217 m_shouldSyncFrame = true; 218 m_state.imagesToClear.append(imageID); 219} 220 221void CompositingCoordinator::removeImageBacking(CoordinatedImageBackingID imageID) 222{ 223 if (m_isPurging) 224 return; 225 226 ASSERT(m_imageBackings.contains(imageID)); 227 m_imageBackings.remove(imageID); 228 229 m_state.imagesToRemove.append(imageID); 230 231 size_t imageIDPosition = m_state.imagesToClear.find(imageID); 232 if (imageIDPosition != notFound) 233 m_state.imagesToClear.remove(imageIDPosition); 234} 235 236void CompositingCoordinator::flushPendingImageBackingChanges() 237{ 238 for (auto& imageBacking : m_imageBackings.values()) 239 imageBacking->update(); 240} 241 242void CompositingCoordinator::notifyAnimationStarted(const GraphicsLayer*, double /* time */) 243{ 244} 245 246void CompositingCoordinator::notifyFlushRequired(const GraphicsLayer*) 247{ 248 if (!isFlushingLayerChanges()) 249 m_client->notifyFlushRequired(); 250} 251 252 253void CompositingCoordinator::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& graphicsContext, GraphicsLayerPaintingPhase, const FloatRect& clipRect) 254{ 255 m_client->paintLayerContents(graphicsLayer, graphicsContext, enclosingIntRect(clipRect)); 256} 257 258std::unique_ptr<GraphicsLayer> CompositingCoordinator::createGraphicsLayer(GraphicsLayerClient& client) 259{ 260 CoordinatedGraphicsLayer* layer = new CoordinatedGraphicsLayer(client); 261 layer->setCoordinator(this); 262 m_registeredLayers.add(layer->id(), layer); 263 m_state.layersToCreate.append(layer->id()); 264 layer->setNeedsVisibleRectAdjustment(); 265 notifyFlushRequired(layer); 266 return std::unique_ptr<GraphicsLayer>(layer); 267} 268 269float CompositingCoordinator::deviceScaleFactor() const 270{ 271 return m_page->deviceScaleFactor(); 272} 273 274float CompositingCoordinator::pageScaleFactor() const 275{ 276 return m_page->pageScaleFactor(); 277} 278 279void CompositingCoordinator::createUpdateAtlas(uint32_t atlasID, PassRefPtr<CoordinatedSurface> coordinatedSurface) 280{ 281 m_state.updateAtlasesToCreate.append(std::make_pair(atlasID, coordinatedSurface)); 282} 283 284void CompositingCoordinator::removeUpdateAtlas(uint32_t atlasID) 285{ 286 if (m_isPurging) 287 return; 288 m_state.updateAtlasesToRemove.append(atlasID); 289} 290 291FloatRect CompositingCoordinator::visibleContentsRect() const 292{ 293 return m_visibleContentsRect; 294} 295 296CoordinatedGraphicsLayer* CompositingCoordinator::mainContentsLayer() 297{ 298 if (!m_rootCompositingLayer) 299 return 0; 300 301 return toCoordinatedGraphicsLayer(m_rootCompositingLayer)->findFirstDescendantWithContentsRecursively(); 302} 303 304void CompositingCoordinator::setVisibleContentsRect(const FloatRect& rect, const FloatPoint& trajectoryVector) 305{ 306 // A zero trajectoryVector indicates that tiles all around the viewport are requested. 307 if (CoordinatedGraphicsLayer* contentsLayer = mainContentsLayer()) 308 contentsLayer->setVisibleContentRectTrajectoryVector(trajectoryVector); 309 310 bool contentsRectDidChange = rect != m_visibleContentsRect; 311 if (contentsRectDidChange) { 312 m_visibleContentsRect = rect; 313 314 for (auto& registeredLayer : m_registeredLayers.values()) 315 registeredLayer->setNeedsVisibleRectAdjustment(); 316 } 317 318 FrameView* view = m_page->mainFrame().view(); 319 if (view->useFixedLayout()) { 320 // Round the rect instead of enclosing it to make sure that its size stays 321 // the same while panning. This can have nasty effects on layout. 322 view->setFixedVisibleContentRect(roundedIntRect(rect)); 323 } 324} 325 326void CompositingCoordinator::deviceOrPageScaleFactorChanged() 327{ 328 m_rootLayer->deviceOrPageScaleFactorChanged(); 329} 330 331void CompositingCoordinator::detachLayer(CoordinatedGraphicsLayer* layer) 332{ 333 if (m_isPurging) 334 return; 335 336 m_registeredLayers.remove(layer->id()); 337 338 size_t index = m_state.layersToCreate.find(layer->id()); 339 if (index != notFound) { 340 m_state.layersToCreate.remove(index); 341 return; 342 } 343 344 m_state.layersToRemove.append(layer->id()); 345 notifyFlushRequired(layer); 346} 347 348void CompositingCoordinator::commitScrollOffset(uint32_t layerID, const WebCore::IntSize& offset) 349{ 350 LayerMap::iterator i = m_registeredLayers.find(layerID); 351 if (i == m_registeredLayers.end()) 352 return; 353 354 i->value->commitScrollOffset(offset); 355} 356 357void CompositingCoordinator::renderNextFrame() 358{ 359 for (unsigned i = 0; i < m_updateAtlases.size(); ++i) 360 m_updateAtlases[i]->didSwapBuffers(); 361} 362 363void CompositingCoordinator::purgeBackingStores() 364{ 365 TemporaryChange<bool> purgingToggle(m_isPurging, true); 366 367 for (auto& registeredLayer : m_registeredLayers.values()) 368 registeredLayer->purgeBackingStores(); 369 370 m_imageBackings.clear(); 371 m_updateAtlases.clear(); 372} 373 374bool CompositingCoordinator::paintToSurface(const IntSize& size, CoordinatedSurface::Flags flags, uint32_t& atlasID, IntPoint& offset, CoordinatedSurface::Client* client) 375{ 376 for (unsigned i = 0; i < m_updateAtlases.size(); ++i) { 377 UpdateAtlas* atlas = m_updateAtlases[i].get(); 378 if (atlas->supportsAlpha() == (flags & CoordinatedSurface::SupportsAlpha)) { 379 // This will be false if there is no available buffer space. 380 if (atlas->paintOnAvailableBuffer(size, atlasID, offset, client)) 381 return true; 382 } 383 } 384 385 static const int ScratchBufferDimension = 1024; // Should be a power of two. 386 m_updateAtlases.append(std::make_unique<UpdateAtlas>(this, ScratchBufferDimension, flags)); 387 scheduleReleaseInactiveAtlases(); 388 return m_updateAtlases.last()->paintOnAvailableBuffer(size, atlasID, offset, client); 389} 390 391const double ReleaseInactiveAtlasesTimerInterval = 0.5; 392 393void CompositingCoordinator::scheduleReleaseInactiveAtlases() 394{ 395 if (!m_releaseInactiveAtlasesTimer.isActive()) 396 m_releaseInactiveAtlasesTimer.startRepeating(ReleaseInactiveAtlasesTimerInterval); 397} 398 399void CompositingCoordinator::releaseInactiveAtlasesTimerFired(Timer<CompositingCoordinator>*) 400{ 401 // We always want to keep one atlas for root contents layer. 402 std::unique_ptr<UpdateAtlas> atlasToKeepAnyway; 403 bool foundActiveAtlasForRootContentsLayer = false; 404 for (int i = m_updateAtlases.size() - 1; i >= 0; --i) { 405 UpdateAtlas* atlas = m_updateAtlases[i].get(); 406 if (!atlas->isInUse()) 407 atlas->addTimeInactive(ReleaseInactiveAtlasesTimerInterval); 408 bool usableForRootContentsLayer = !atlas->supportsAlpha(); 409 if (atlas->isInactive()) { 410 if (!foundActiveAtlasForRootContentsLayer && !atlasToKeepAnyway && usableForRootContentsLayer) 411 atlasToKeepAnyway = WTF::move(m_updateAtlases[i]); 412 m_updateAtlases.remove(i); 413 } else if (usableForRootContentsLayer) 414 foundActiveAtlasForRootContentsLayer = true; 415 } 416 417 if (!foundActiveAtlasForRootContentsLayer && atlasToKeepAnyway) 418 m_updateAtlases.append(atlasToKeepAnyway.release()); 419 420 if (m_updateAtlases.size() <= 1) 421 m_releaseInactiveAtlasesTimer.stop(); 422} 423 424} // namespace WebCore 425 426#endif // USE(COORDINATED_GRAPHICS) 427