blob: 735b7f19a6b5256a19cd885fe14179630d63bbe8 [file] [log] [blame]
/*
* Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
* Copyright (C) 2013 Company 100, 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.
*/
#include "config.h"
#include "CompositingCoordinator.h"
#if USE(COORDINATED_GRAPHICS)
#include <WebCore/DOMWindow.h>
#include <WebCore/Document.h>
#include <WebCore/FrameView.h>
#include <WebCore/GraphicsContext.h>
#include <WebCore/InspectorController.h>
#include <WebCore/MainFrame.h>
#include <WebCore/Page.h>
#include <wtf/TemporaryChange.h>
using namespace WebCore;
namespace WebKit {
CompositingCoordinator::CompositingCoordinator(Page* page, CompositingCoordinator::Client& client)
: m_page(page)
, m_client(client)
, m_releaseInactiveAtlasesTimer(*this, &CompositingCoordinator::releaseInactiveAtlasesTimerFired)
{
}
CompositingCoordinator::~CompositingCoordinator()
{
m_isDestructing = true;
purgeBackingStores();
for (auto& registeredLayer : m_registeredLayers.values())
registeredLayer->setCoordinator(nullptr);
}
void CompositingCoordinator::invalidate()
{
m_rootLayer = nullptr;
purgeBackingStores();
}
void CompositingCoordinator::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
{
if (m_rootCompositingLayer == graphicsLayer)
return;
if (m_rootCompositingLayer)
m_rootCompositingLayer->removeFromParent();
m_rootCompositingLayer = graphicsLayer;
if (m_rootCompositingLayer)
m_rootLayer->addChildAtIndex(m_rootCompositingLayer, 0);
}
void CompositingCoordinator::setViewOverlayRootLayer(GraphicsLayer* graphicsLayer)
{
if (m_overlayCompositingLayer == graphicsLayer)
return;
if (m_overlayCompositingLayer)
m_overlayCompositingLayer->removeFromParent();
m_overlayCompositingLayer = graphicsLayer;
if (m_overlayCompositingLayer)
m_rootLayer->addChild(m_overlayCompositingLayer);
}
void CompositingCoordinator::sizeDidChange(const IntSize& newSize)
{
m_rootLayer->setSize(newSize);
notifyFlushRequired(m_rootLayer.get());
}
bool CompositingCoordinator::flushPendingLayerChanges()
{
TemporaryChange<bool> protector(m_isFlushingLayerChanges, true);
initializeRootCompositingLayerIfNeeded();
bool viewportIsStable = m_page->mainFrame().view()->viewportIsStable();
m_rootLayer->flushCompositingStateForThisLayerOnly(viewportIsStable);
m_client.didFlushRootLayer(m_visibleContentsRect);
if (m_overlayCompositingLayer)
m_overlayCompositingLayer->flushCompositingState(FloatRect(FloatPoint(), m_rootLayer->size()), viewportIsStable);
bool didSync = m_page->mainFrame().view()->flushCompositingStateIncludingSubframes();
auto& coordinatedLayer = downcast<CoordinatedGraphicsLayer>(*m_rootLayer);
coordinatedLayer.updateContentBuffersIncludingSubLayers();
coordinatedLayer.syncPendingStateChangesIncludingSubLayers();
flushPendingImageBackingChanges();
if (m_shouldSyncFrame) {
didSync = true;
if (m_rootCompositingLayer) {
m_state.contentsSize = roundedIntSize(m_rootCompositingLayer->size());
if (CoordinatedGraphicsLayer* contentsLayer = mainContentsLayer())
m_state.coveredRect = contentsLayer->coverRect();
}
m_state.scrollPosition = m_visibleContentsRect.location();
m_client.commitSceneState(m_state);
clearPendingStateChanges();
m_shouldSyncFrame = false;
}
return didSync;
}
double CompositingCoordinator::timestamp() const
{
auto* document = m_page->mainFrame().document();
if (!document)
return 0;
return document->domWindow() ? document->domWindow()->nowTimestamp() : document->monotonicTimestamp();
}
void CompositingCoordinator::syncDisplayState()
{
#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER) && !USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
// Make sure that any previously registered animation callbacks are being executed before we flush the layers.
m_lastAnimationServiceTime = timestamp();
m_page->mainFrame().view()->serviceScriptedAnimations();
#endif
m_page->mainFrame().view()->updateLayoutAndStyleIfNeededRecursive();
}
#if ENABLE(REQUEST_ANIMATION_FRAME)
double CompositingCoordinator::nextAnimationServiceTime() const
{
// According to the requestAnimationFrame spec, rAF callbacks should not be faster than 60FPS.
static const double MinimalTimeoutForAnimations = 1. / 60.;
return std::max<double>(0., MinimalTimeoutForAnimations - timestamp() + m_lastAnimationServiceTime);
}
#endif
void CompositingCoordinator::clearPendingStateChanges()
{
m_state.layersToCreate.clear();
m_state.layersToUpdate.clear();
m_state.layersToRemove.clear();
m_state.imagesToCreate.clear();
m_state.imagesToRemove.clear();
m_state.imagesToUpdate.clear();
m_state.imagesToClear.clear();
m_state.updateAtlasesToCreate.clear();
m_state.updateAtlasesToRemove.clear();
}
void CompositingCoordinator::initializeRootCompositingLayerIfNeeded()
{
if (m_didInitializeRootCompositingLayer)
return;
m_state.rootCompositingLayer = downcast<CoordinatedGraphicsLayer>(*m_rootLayer).id();
m_didInitializeRootCompositingLayer = true;
m_shouldSyncFrame = true;
}
void CompositingCoordinator::createRootLayer(const IntSize& size)
{
ASSERT(!m_rootLayer);
// Create a root layer.
m_rootLayer = GraphicsLayer::create(this, *this);
#ifndef NDEBUG
m_rootLayer->setName("CompositingCoordinator root layer");
#endif
m_rootLayer->setDrawsContent(false);
m_rootLayer->setSize(size);
}
void CompositingCoordinator::syncLayerState(CoordinatedLayerID id, CoordinatedGraphicsLayerState& state)
{
m_shouldSyncFrame = true;
m_state.layersToUpdate.append(std::make_pair(id, state));
}
Ref<CoordinatedImageBacking> CompositingCoordinator::createImageBackingIfNeeded(Image* image)
{
CoordinatedImageBackingID imageID = CoordinatedImageBacking::getCoordinatedImageBackingID(image);
auto addResult = m_imageBackings.ensure(imageID, [this, image] {
return CoordinatedImageBacking::create(this, image);
});
return *addResult.iterator->value;
}
void CompositingCoordinator::createImageBacking(CoordinatedImageBackingID imageID)
{
m_state.imagesToCreate.append(imageID);
}
void CompositingCoordinator::updateImageBacking(CoordinatedImageBackingID imageID, RefPtr<CoordinatedSurface>&& coordinatedSurface)
{
m_shouldSyncFrame = true;
m_state.imagesToUpdate.append(std::make_pair(imageID, WTFMove(coordinatedSurface)));
}
void CompositingCoordinator::clearImageBackingContents(CoordinatedImageBackingID imageID)
{
m_shouldSyncFrame = true;
m_state.imagesToClear.append(imageID);
}
void CompositingCoordinator::removeImageBacking(CoordinatedImageBackingID imageID)
{
if (m_isPurging)
return;
ASSERT(m_imageBackings.contains(imageID));
m_imageBackings.remove(imageID);
m_state.imagesToRemove.append(imageID);
size_t imageIDPosition = m_state.imagesToClear.find(imageID);
if (imageIDPosition != notFound)
m_state.imagesToClear.remove(imageIDPosition);
}
void CompositingCoordinator::flushPendingImageBackingChanges()
{
for (auto& imageBacking : m_imageBackings.values())
imageBacking->update();
}
void CompositingCoordinator::notifyAnimationStarted(const GraphicsLayer*, const String&, double /* time */)
{
}
void CompositingCoordinator::notifyFlushRequired(const GraphicsLayer*)
{
if (!m_isDestructing && !isFlushingLayerChanges())
m_client.notifyFlushRequired();
}
void CompositingCoordinator::paintContents(const GraphicsLayer* graphicsLayer, GraphicsContext& graphicsContext, GraphicsLayerPaintingPhase, const FloatRect& clipRect)
{
m_client.paintLayerContents(graphicsLayer, graphicsContext, enclosingIntRect(clipRect));
}
std::unique_ptr<GraphicsLayer> CompositingCoordinator::createGraphicsLayer(GraphicsLayer::Type layerType, GraphicsLayerClient& client)
{
CoordinatedGraphicsLayer* layer = new CoordinatedGraphicsLayer(layerType, client);
layer->setCoordinator(this);
m_registeredLayers.add(layer->id(), layer);
m_state.layersToCreate.append(layer->id());
layer->setNeedsVisibleRectAdjustment();
notifyFlushRequired(layer);
return std::unique_ptr<GraphicsLayer>(layer);
}
float CompositingCoordinator::deviceScaleFactor() const
{
return m_page->deviceScaleFactor();
}
float CompositingCoordinator::pageScaleFactor() const
{
return m_page->pageScaleFactor();
}
void CompositingCoordinator::createUpdateAtlas(uint32_t atlasID, RefPtr<CoordinatedSurface>&& coordinatedSurface)
{
m_state.updateAtlasesToCreate.append(std::make_pair(atlasID, WTFMove(coordinatedSurface)));
}
void CompositingCoordinator::removeUpdateAtlas(uint32_t atlasID)
{
if (m_isPurging)
return;
m_state.updateAtlasesToRemove.append(atlasID);
}
FloatRect CompositingCoordinator::visibleContentsRect() const
{
return m_visibleContentsRect;
}
CoordinatedGraphicsLayer* CompositingCoordinator::mainContentsLayer()
{
if (!is<CoordinatedGraphicsLayer>(m_rootCompositingLayer))
return nullptr;
return downcast<CoordinatedGraphicsLayer>(*m_rootCompositingLayer).findFirstDescendantWithContentsRecursively();
}
void CompositingCoordinator::setVisibleContentsRect(const FloatRect& rect, const FloatPoint& trajectoryVector)
{
// A zero trajectoryVector indicates that tiles all around the viewport are requested.
if (CoordinatedGraphicsLayer* contentsLayer = mainContentsLayer())
contentsLayer->setVisibleContentRectTrajectoryVector(trajectoryVector);
bool contentsRectDidChange = rect != m_visibleContentsRect;
if (contentsRectDidChange) {
m_visibleContentsRect = rect;
for (auto& registeredLayer : m_registeredLayers.values())
registeredLayer->setNeedsVisibleRectAdjustment();
}
FrameView* view = m_page->mainFrame().view();
if (view->useFixedLayout() && contentsRectDidChange) {
// Round the rect instead of enclosing it to make sure that its size stays
// the same while panning. This can have nasty effects on layout.
view->setFixedVisibleContentRect(roundedIntRect(rect));
}
}
void CompositingCoordinator::deviceOrPageScaleFactorChanged()
{
m_rootLayer->deviceOrPageScaleFactorChanged();
}
void CompositingCoordinator::detachLayer(CoordinatedGraphicsLayer* layer)
{
if (m_isPurging)
return;
m_registeredLayers.remove(layer->id());
size_t index = m_state.layersToCreate.find(layer->id());
if (index != notFound) {
m_state.layersToCreate.remove(index);
return;
}
m_state.layersToRemove.append(layer->id());
notifyFlushRequired(layer);
}
void CompositingCoordinator::commitScrollOffset(uint32_t layerID, const WebCore::IntSize& offset)
{
if (auto* layer = m_registeredLayers.get(layerID))
layer->commitScrollOffset(offset);
}
void CompositingCoordinator::renderNextFrame()
{
for (auto& atlas : m_updateAtlases)
atlas->didSwapBuffers();
}
void CompositingCoordinator::purgeBackingStores()
{
TemporaryChange<bool> purgingToggle(m_isPurging, true);
for (auto& registeredLayer : m_registeredLayers.values())
registeredLayer->purgeBackingStores();
m_imageBackings.clear();
m_updateAtlases.clear();
}
bool CompositingCoordinator::paintToSurface(const IntSize& size, CoordinatedSurface::Flags flags, uint32_t& atlasID, IntPoint& offset, CoordinatedSurface::Client& client)
{
for (auto& updateAtlas : m_updateAtlases) {
UpdateAtlas* atlas = updateAtlas.get();
if (atlas->supportsAlpha() == (flags & CoordinatedSurface::SupportsAlpha)) {
// This will be false if there is no available buffer space.
if (atlas->paintOnAvailableBuffer(size, atlasID, offset, client))
return true;
}
}
static const int ScratchBufferDimension = 1024; // Should be a power of two.
m_updateAtlases.append(std::make_unique<UpdateAtlas>(*this, ScratchBufferDimension, flags));
scheduleReleaseInactiveAtlases();
return m_updateAtlases.last()->paintOnAvailableBuffer(size, atlasID, offset, client);
}
const double ReleaseInactiveAtlasesTimerInterval = 0.5;
void CompositingCoordinator::scheduleReleaseInactiveAtlases()
{
if (!m_releaseInactiveAtlasesTimer.isActive())
m_releaseInactiveAtlasesTimer.startRepeating(ReleaseInactiveAtlasesTimerInterval);
}
void CompositingCoordinator::releaseInactiveAtlasesTimerFired()
{
// We always want to keep one atlas for root contents layer.
std::unique_ptr<UpdateAtlas> atlasToKeepAnyway;
bool foundActiveAtlasForRootContentsLayer = false;
for (int i = m_updateAtlases.size() - 1; i >= 0; --i) {
UpdateAtlas* atlas = m_updateAtlases[i].get();
if (!atlas->isInUse())
atlas->addTimeInactive(ReleaseInactiveAtlasesTimerInterval);
bool usableForRootContentsLayer = !atlas->supportsAlpha();
if (atlas->isInactive()) {
if (!foundActiveAtlasForRootContentsLayer && !atlasToKeepAnyway && usableForRootContentsLayer)
atlasToKeepAnyway = WTFMove(m_updateAtlases[i]);
m_updateAtlases.remove(i);
} else if (usableForRootContentsLayer)
foundActiveAtlasForRootContentsLayer = true;
}
if (!foundActiveAtlasForRootContentsLayer && atlasToKeepAnyway)
m_updateAtlases.append(atlasToKeepAnyway.release());
m_updateAtlases.shrinkToFit();
if (m_updateAtlases.size() <= 1)
m_releaseInactiveAtlasesTimer.stop();
}
} // namespace WebKit
#endif // USE(COORDINATED_GRAPHICS)