CompositingCoordinator.cpp [plain text]
#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::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)
m_lastAnimationServiceTime = timestamp();
m_page->mainFrame().view()->serviceScriptedAnimations();
#endif
m_page->mainFrame().view()->updateLayoutAndStyleIfNeededRecursive();
}
#if ENABLE(REQUEST_ANIMATION_FRAME)
double CompositingCoordinator::nextAnimationServiceTime() const
{
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);
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 )
{
}
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)
{
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) {
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)) {
if (atlas->paintOnAvailableBuffer(size, atlasID, offset, client))
return true;
}
}
static const int ScratchBufferDimension = 1024; 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()
{
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();
}
}
#endif // USE(COORDINATED_GRAPHICS)