CoordinatedGraphicsScene.cpp [plain text]
#include "config.h"
#if USE(COORDINATED_GRAPHICS)
#include "CoordinatedGraphicsScene.h"
#include "CoordinatedBackingStore.h"
#include "TextureMapper.h"
#include "TextureMapperBackingStore.h"
#include "TextureMapperGL.h"
#include "TextureMapperLayer.h"
#include <wtf/Atomics.h>
#include <wtf/MainThread.h>
namespace WebCore {
void CoordinatedGraphicsScene::dispatchOnMainThread(std::function<void()> function)
{
if (isMainThread())
function();
else
callOnMainThread(WTF::move(function));
}
static bool layerShouldHaveBackingStore(TextureMapperLayer* layer)
{
return layer->drawsContent() && layer->contentsAreVisible() && !layer->size().isEmpty();
}
CoordinatedGraphicsScene::CoordinatedGraphicsScene(CoordinatedGraphicsSceneClient* client)
: m_client(client)
, m_isActive(false)
, m_rootLayerID(InvalidCoordinatedLayerID)
, m_backgroundColor(Color::white)
, m_viewBackgroundColor(Color::white)
, m_setDrawsBackground(false)
{
ASSERT(isMainThread());
}
CoordinatedGraphicsScene::~CoordinatedGraphicsScene()
{
}
void CoordinatedGraphicsScene::paintToCurrentGLContext(const TransformationMatrix& matrix, float opacity, const FloatRect& clipRect, TextureMapper::PaintFlags PaintFlags)
{
if (!m_textureMapper) {
m_textureMapper = TextureMapper::create(TextureMapper::OpenGLMode);
static_cast<TextureMapperGL*>(m_textureMapper.get())->setEnableEdgeDistanceAntialiasing(true);
}
ASSERT(m_textureMapper->accelerationMode() == TextureMapper::OpenGLMode);
syncRemoteContent();
adjustPositionForFixedLayers();
TextureMapperLayer* currentRootLayer = rootLayer();
if (!currentRootLayer)
return;
currentRootLayer->setTextureMapper(m_textureMapper.get());
currentRootLayer->applyAnimationsRecursively();
m_textureMapper->beginPainting(PaintFlags);
m_textureMapper->beginClip(TransformationMatrix(), clipRect);
if (m_setDrawsBackground) {
RGBA32 rgba = makeRGBA32FromFloats(m_backgroundColor.red(),
m_backgroundColor.green(), m_backgroundColor.blue(),
m_backgroundColor.alpha() * opacity);
m_textureMapper->drawSolidColor(clipRect, TransformationMatrix(), Color(rgba));
} else {
GraphicsContext3D* context = static_cast<TextureMapperGL*>(m_textureMapper.get())->graphicsContext3D();
context->clearColor(m_viewBackgroundColor.red() / 255.0f, m_viewBackgroundColor.green() / 255.0f, m_viewBackgroundColor.blue() / 255.0f, m_viewBackgroundColor.alpha() / 255.0f);
context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
}
if (currentRootLayer->opacity() != opacity || currentRootLayer->transform() != matrix) {
currentRootLayer->setOpacity(opacity);
currentRootLayer->setTransform(matrix);
}
currentRootLayer->paint();
m_fpsCounter.updateFPSAndDisplay(m_textureMapper.get(), clipRect.location(), matrix);
m_textureMapper->endClip();
m_textureMapper->endPainting();
if (currentRootLayer->descendantsOrSelfHaveRunningAnimations()) {
RefPtr<CoordinatedGraphicsScene> protector(this);
dispatchOnMainThread([=] {
protector->updateViewport();
});
}
}
void CoordinatedGraphicsScene::paintToGraphicsContext(PlatformGraphicsContext* platformContext)
{
if (!m_textureMapper)
m_textureMapper = TextureMapper::create();
ASSERT(m_textureMapper->accelerationMode() == TextureMapper::SoftwareMode);
syncRemoteContent();
TextureMapperLayer* layer = rootLayer();
if (!layer)
return;
GraphicsContext graphicsContext(platformContext);
m_textureMapper->setGraphicsContext(&graphicsContext);
m_textureMapper->beginPainting();
IntRect clipRect = graphicsContext.clipBounds();
if (m_setDrawsBackground)
m_textureMapper->drawSolidColor(clipRect, TransformationMatrix(), m_backgroundColor);
else
m_textureMapper->drawSolidColor(clipRect, TransformationMatrix(), m_viewBackgroundColor);
layer->paint();
m_fpsCounter.updateFPSAndDisplay(m_textureMapper.get(), clipRect.location());
m_textureMapper->endPainting();
m_textureMapper->setGraphicsContext(0);
}
void CoordinatedGraphicsScene::setScrollPosition(const FloatPoint& scrollPosition)
{
m_scrollPosition = scrollPosition;
}
void CoordinatedGraphicsScene::updateViewport()
{
ASSERT(isMainThread());
if (m_client)
m_client->updateViewport();
}
void CoordinatedGraphicsScene::adjustPositionForFixedLayers()
{
if (m_fixedLayers.isEmpty())
return;
FloatSize delta = m_scrollPosition - m_renderedContentsScrollPosition;
for (auto& fixedLayer : m_fixedLayers.values())
fixedLayer->setScrollPositionDeltaIfNeeded(delta);
}
#if USE(GRAPHICS_SURFACE)
void CoordinatedGraphicsScene::createCanvasIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
if (!state.canvasToken.isValid())
return;
RefPtr<TextureMapperSurfaceBackingStore> canvasBackingStore(TextureMapperSurfaceBackingStore::create());
m_surfaceBackingStores.set(layer, canvasBackingStore);
canvasBackingStore->setGraphicsSurface(GraphicsSurface::create(state.canvasSize, state.canvasSurfaceFlags, state.canvasToken));
layer->setContentsLayer(canvasBackingStore.get());
}
void CoordinatedGraphicsScene::syncCanvasIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
ASSERT(m_textureMapper);
if (state.canvasChanged) {
destroyCanvasIfNeeded(layer, state);
createCanvasIfNeeded(layer, state);
}
if (state.canvasShouldSwapBuffers) {
ASSERT(m_surfaceBackingStores.contains(layer));
SurfaceBackingStoreMap::iterator it = m_surfaceBackingStores.find(layer);
RefPtr<TextureMapperSurfaceBackingStore> canvasBackingStore = it->value;
canvasBackingStore->swapBuffersIfNeeded(state.canvasFrontBuffer);
}
}
void CoordinatedGraphicsScene::destroyCanvasIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
if (state.canvasToken.isValid())
return;
m_surfaceBackingStores.remove(layer);
layer->setContentsLayer(0);
}
#endif
void CoordinatedGraphicsScene::setLayerRepaintCountIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
if (!layer->isShowingRepaintCounter() || !state.repaintCountChanged)
return;
layer->setRepaintCount(state.repaintCount);
}
void CoordinatedGraphicsScene::setLayerChildrenIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
if (!state.childrenChanged)
return;
Vector<TextureMapperLayer*> children;
for (size_t i = 0; i < state.children.size(); ++i) {
CoordinatedLayerID childID = state.children[i];
TextureMapperLayer* child = layerByID(childID);
children.append(child);
}
layer->setChildren(children);
}
#if ENABLE(CSS_FILTERS)
void CoordinatedGraphicsScene::setLayerFiltersIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
if (!state.filtersChanged)
return;
layer->setFilters(state.filters);
}
#endif
void CoordinatedGraphicsScene::setLayerState(CoordinatedLayerID id, const CoordinatedGraphicsLayerState& layerState)
{
ASSERT(m_rootLayerID != InvalidCoordinatedLayerID);
TextureMapperLayer* layer = layerByID(id);
if (layerState.positionChanged)
layer->setPosition(layerState.pos);
if (layerState.anchorPointChanged)
layer->setAnchorPoint(layerState.anchorPoint);
if (layerState.sizeChanged)
layer->setSize(layerState.size);
if (layerState.transformChanged)
layer->setTransform(layerState.transform);
if (layerState.childrenTransformChanged)
layer->setChildrenTransform(layerState.childrenTransform);
if (layerState.contentsRectChanged)
layer->setContentsRect(layerState.contentsRect);
if (layerState.contentsTilingChanged) {
layer->setContentsTilePhase(layerState.contentsTilePhase);
layer->setContentsTileSize(layerState.contentsTileSize);
}
if (layerState.opacityChanged)
layer->setOpacity(layerState.opacity);
if (layerState.solidColorChanged)
layer->setSolidColor(layerState.solidColor);
if (layerState.debugBorderColorChanged || layerState.debugBorderWidthChanged)
layer->setDebugVisuals(layerState.showDebugBorders, layerState.debugBorderColor, layerState.debugBorderWidth, layerState.showRepaintCounter);
if (layerState.replicaChanged)
layer->setReplicaLayer(getLayerByIDIfExists(layerState.replica));
if (layerState.maskChanged)
layer->setMaskLayer(getLayerByIDIfExists(layerState.mask));
if (layerState.imageChanged)
assignImageBackingToLayer(layer, layerState.imageID);
if (layerState.flagsChanged) {
layer->setContentsOpaque(layerState.contentsOpaque);
layer->setDrawsContent(layerState.drawsContent);
layer->setContentsVisible(layerState.contentsVisible);
layer->setBackfaceVisibility(layerState.backfaceVisible);
layer->setMasksToBounds(id == m_rootLayerID ? false : layerState.masksToBounds);
layer->setPreserves3D(layerState.preserves3D);
bool fixedToViewportChanged = layer->fixedToViewport() != layerState.fixedToViewport;
layer->setFixedToViewport(layerState.fixedToViewport);
if (fixedToViewportChanged) {
if (layerState.fixedToViewport)
m_fixedLayers.add(id, layer);
else
m_fixedLayers.remove(id);
}
layer->setIsScrollable(layerState.isScrollable);
}
if (layerState.committedScrollOffsetChanged)
layer->didCommitScrollOffset(layerState.committedScrollOffset);
prepareContentBackingStore(layer);
setLayerChildrenIfNeeded(layer, layerState);
createTilesIfNeeded(layer, layerState);
removeTilesIfNeeded(layer, layerState);
updateTilesIfNeeded(layer, layerState);
#if ENABLE(CSS_FILTERS)
setLayerFiltersIfNeeded(layer, layerState);
#endif
setLayerAnimationsIfNeeded(layer, layerState);
#if USE(GRAPHICS_SURFACE)
syncCanvasIfNeeded(layer, layerState);
#endif
setLayerRepaintCountIfNeeded(layer, layerState);
}
TextureMapperLayer* CoordinatedGraphicsScene::getLayerByIDIfExists(CoordinatedLayerID id)
{
return (id != InvalidCoordinatedLayerID) ? layerByID(id) : 0;
}
void CoordinatedGraphicsScene::createLayers(const Vector<CoordinatedLayerID>& ids)
{
for (size_t index = 0; index < ids.size(); ++index)
createLayer(ids[index]);
}
void CoordinatedGraphicsScene::createLayer(CoordinatedLayerID id)
{
std::unique_ptr<TextureMapperLayer> newLayer = std::make_unique<TextureMapperLayer>();
newLayer->setID(id);
newLayer->setScrollClient(this);
m_layers.add(id, WTF::move(newLayer));
}
void CoordinatedGraphicsScene::deleteLayers(const Vector<CoordinatedLayerID>& layerIDs)
{
for (size_t index = 0; index < layerIDs.size(); ++index)
deleteLayer(layerIDs[index]);
}
void CoordinatedGraphicsScene::deleteLayer(CoordinatedLayerID layerID)
{
std::unique_ptr<TextureMapperLayer> layer = m_layers.take(layerID);
ASSERT(layer);
m_backingStores.remove(layer.get());
m_fixedLayers.remove(layerID);
#if USE(GRAPHICS_SURFACE)
m_surfaceBackingStores.remove(layer.get());
#endif
}
void CoordinatedGraphicsScene::setRootLayerID(CoordinatedLayerID layerID)
{
ASSERT(layerID != InvalidCoordinatedLayerID);
ASSERT(m_rootLayerID == InvalidCoordinatedLayerID);
m_rootLayerID = layerID;
TextureMapperLayer* layer = layerByID(layerID);
ASSERT(m_rootLayer->children().isEmpty());
m_rootLayer->addChild(layer);
}
void CoordinatedGraphicsScene::prepareContentBackingStore(TextureMapperLayer* layer)
{
if (!layerShouldHaveBackingStore(layer)) {
removeBackingStoreIfNeeded(layer);
return;
}
createBackingStoreIfNeeded(layer);
resetBackingStoreSizeToLayerSize(layer);
}
void CoordinatedGraphicsScene::createBackingStoreIfNeeded(TextureMapperLayer* layer)
{
if (m_backingStores.contains(layer))
return;
RefPtr<CoordinatedBackingStore> backingStore(CoordinatedBackingStore::create());
m_backingStores.add(layer, backingStore);
layer->setBackingStore(backingStore);
}
void CoordinatedGraphicsScene::removeBackingStoreIfNeeded(TextureMapperLayer* layer)
{
RefPtr<CoordinatedBackingStore> backingStore = m_backingStores.take(layer);
if (!backingStore)
return;
layer->setBackingStore(0);
}
void CoordinatedGraphicsScene::resetBackingStoreSizeToLayerSize(TextureMapperLayer* layer)
{
RefPtr<CoordinatedBackingStore> backingStore = m_backingStores.get(layer);
ASSERT(backingStore);
backingStore->setSize(layer->size());
m_backingStoresWithPendingBuffers.add(backingStore);
}
void CoordinatedGraphicsScene::createTilesIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
if (state.tilesToCreate.isEmpty())
return;
RefPtr<CoordinatedBackingStore> backingStore = m_backingStores.get(layer);
ASSERT(backingStore);
for (size_t i = 0; i < state.tilesToCreate.size(); ++i)
backingStore->createTile(state.tilesToCreate[i].tileID, state.tilesToCreate[i].scale);
}
void CoordinatedGraphicsScene::removeTilesIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
if (state.tilesToRemove.isEmpty())
return;
RefPtr<CoordinatedBackingStore> backingStore = m_backingStores.get(layer);
if (!backingStore)
return;
for (size_t i = 0; i < state.tilesToRemove.size(); ++i)
backingStore->removeTile(state.tilesToRemove[i]);
m_backingStoresWithPendingBuffers.add(backingStore);
}
void CoordinatedGraphicsScene::updateTilesIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
if (state.tilesToUpdate.isEmpty())
return;
RefPtr<CoordinatedBackingStore> backingStore = m_backingStores.get(layer);
ASSERT(backingStore);
for (size_t i = 0; i < state.tilesToUpdate.size(); ++i) {
const TileUpdateInfo& tileInfo = state.tilesToUpdate[i];
const SurfaceUpdateInfo& surfaceUpdateInfo = tileInfo.updateInfo;
SurfaceMap::iterator surfaceIt = m_surfaces.find(surfaceUpdateInfo.atlasID);
ASSERT(surfaceIt != m_surfaces.end());
backingStore->updateTile(tileInfo.tileID, surfaceUpdateInfo.updateRect, tileInfo.tileRect, surfaceIt->value, surfaceUpdateInfo.surfaceOffset);
m_backingStoresWithPendingBuffers.add(backingStore);
}
}
void CoordinatedGraphicsScene::syncUpdateAtlases(const CoordinatedGraphicsState& state)
{
for (size_t i = 0; i < state.updateAtlasesToCreate.size(); ++i)
createUpdateAtlas(state.updateAtlasesToCreate[i].first, state.updateAtlasesToCreate[i].second);
for (size_t i = 0; i < state.updateAtlasesToRemove.size(); ++i)
removeUpdateAtlas(state.updateAtlasesToRemove[i]);
}
void CoordinatedGraphicsScene::createUpdateAtlas(uint32_t atlasID, PassRefPtr<CoordinatedSurface> surface)
{
ASSERT(!m_surfaces.contains(atlasID));
m_surfaces.add(atlasID, surface);
}
void CoordinatedGraphicsScene::removeUpdateAtlas(uint32_t atlasID)
{
ASSERT(m_surfaces.contains(atlasID));
m_surfaces.remove(atlasID);
}
void CoordinatedGraphicsScene::syncImageBackings(const CoordinatedGraphicsState& state)
{
for (size_t i = 0; i < state.imagesToRemove.size(); ++i)
removeImageBacking(state.imagesToRemove[i]);
for (size_t i = 0; i < state.imagesToCreate.size(); ++i)
createImageBacking(state.imagesToCreate[i]);
for (size_t i = 0; i < state.imagesToUpdate.size(); ++i)
updateImageBacking(state.imagesToUpdate[i].first, state.imagesToUpdate[i].second);
for (size_t i = 0; i < state.imagesToClear.size(); ++i)
clearImageBackingContents(state.imagesToClear[i]);
}
void CoordinatedGraphicsScene::createImageBacking(CoordinatedImageBackingID imageID)
{
ASSERT(!m_imageBackings.contains(imageID));
RefPtr<CoordinatedBackingStore> backingStore(CoordinatedBackingStore::create());
m_imageBackings.add(imageID, backingStore.release());
}
void CoordinatedGraphicsScene::updateImageBacking(CoordinatedImageBackingID imageID, PassRefPtr<CoordinatedSurface> surface)
{
ASSERT(m_imageBackings.contains(imageID));
ImageBackingMap::iterator it = m_imageBackings.find(imageID);
RefPtr<CoordinatedBackingStore> backingStore = it->value;
backingStore->createTile(1 , 1 );
IntRect rect(IntPoint::zero(), surface->size());
ASSERT(2000 >= std::max(rect.width(), rect.height()));
backingStore->setSize(rect.size());
backingStore->updateTile(1 , rect, rect, surface, rect.location());
m_backingStoresWithPendingBuffers.add(backingStore);
}
void CoordinatedGraphicsScene::clearImageBackingContents(CoordinatedImageBackingID imageID)
{
ASSERT(m_imageBackings.contains(imageID));
ImageBackingMap::iterator it = m_imageBackings.find(imageID);
RefPtr<CoordinatedBackingStore> backingStore = it->value;
backingStore->removeAllTiles();
m_backingStoresWithPendingBuffers.add(backingStore);
}
void CoordinatedGraphicsScene::removeImageBacking(CoordinatedImageBackingID imageID)
{
ASSERT(m_imageBackings.contains(imageID));
m_releasedImageBackings.append(m_imageBackings.take(imageID));
}
void CoordinatedGraphicsScene::assignImageBackingToLayer(TextureMapperLayer* layer, CoordinatedImageBackingID imageID)
{
#if USE(GRAPHICS_SURFACE)
if (m_surfaceBackingStores.contains(layer))
return;
#endif
if (imageID == InvalidCoordinatedImageBackingID) {
layer->setContentsLayer(0);
return;
}
ImageBackingMap::iterator it = m_imageBackings.find(imageID);
ASSERT(it != m_imageBackings.end());
layer->setContentsLayer(it->value.get());
}
void CoordinatedGraphicsScene::removeReleasedImageBackingsIfNeeded()
{
m_releasedImageBackings.clear();
}
void CoordinatedGraphicsScene::commitPendingBackingStoreOperations()
{
for (auto& backingStore : m_backingStoresWithPendingBuffers)
backingStore->commitTileOperations(m_textureMapper.get());
m_backingStoresWithPendingBuffers.clear();
}
void CoordinatedGraphicsScene::commitSceneState(const CoordinatedGraphicsState& state)
{
m_renderedContentsScrollPosition = state.scrollPosition;
createLayers(state.layersToCreate);
deleteLayers(state.layersToRemove);
if (state.rootCompositingLayer != m_rootLayerID)
setRootLayerID(state.rootCompositingLayer);
syncImageBackings(state);
syncUpdateAtlases(state);
for (size_t i = 0; i < state.layersToUpdate.size(); ++i)
setLayerState(state.layersToUpdate[i].first, state.layersToUpdate[i].second);
commitPendingBackingStoreOperations();
removeReleasedImageBackingsIfNeeded();
RefPtr<CoordinatedGraphicsScene> protector(this);
dispatchOnMainThread([=] {
protector->renderNextFrame();
});
}
void CoordinatedGraphicsScene::renderNextFrame()
{
if (m_client)
m_client->renderNextFrame();
}
void CoordinatedGraphicsScene::ensureRootLayer()
{
if (m_rootLayer)
return;
m_rootLayer = std::make_unique<TextureMapperLayer>();
m_rootLayer->setMasksToBounds(false);
m_rootLayer->setDrawsContent(false);
m_rootLayer->setAnchorPoint(FloatPoint3D(0, 0, 0));
m_rootLayer->setSize(FloatSize(1.0, 1.0));
ASSERT(m_textureMapper);
m_rootLayer->setTextureMapper(m_textureMapper.get());
}
void CoordinatedGraphicsScene::syncRemoteContent()
{
ensureRootLayer();
Vector<std::function<void()>> renderQueue;
bool calledOnMainThread = WTF::isMainThread();
if (!calledOnMainThread)
m_renderQueueMutex.lock();
renderQueue = WTF::move(m_renderQueue);
if (!calledOnMainThread)
m_renderQueueMutex.unlock();
for (auto& function : renderQueue)
function();
}
void CoordinatedGraphicsScene::purgeGLResources()
{
m_imageBackings.clear();
m_releasedImageBackings.clear();
#if USE(GRAPHICS_SURFACE)
m_surfaceBackingStores.clear();
#endif
m_surfaces.clear();
m_rootLayer = nullptr;
m_rootLayerID = InvalidCoordinatedLayerID;
m_layers.clear();
m_fixedLayers.clear();
m_textureMapper = nullptr;
m_backingStores.clear();
m_backingStoresWithPendingBuffers.clear();
setActive(false);
RefPtr<CoordinatedGraphicsScene> protector(this);
dispatchOnMainThread([=] {
protector->purgeBackingStores();
});
}
void CoordinatedGraphicsScene::dispatchCommitScrollOffset(uint32_t layerID, const IntSize& offset)
{
m_client->commitScrollOffset(layerID, offset);
}
void CoordinatedGraphicsScene::commitScrollOffset(uint32_t layerID, const IntSize& offset)
{
RefPtr<CoordinatedGraphicsScene> protector(this);
dispatchOnMainThread([=] {
protector->dispatchCommitScrollOffset(layerID, offset);
});
}
void CoordinatedGraphicsScene::purgeBackingStores()
{
if (m_client)
m_client->purgeBackingStores();
}
void CoordinatedGraphicsScene::setLayerAnimationsIfNeeded(TextureMapperLayer* layer, const CoordinatedGraphicsLayerState& state)
{
if (!state.animationsChanged)
return;
layer->setAnimations(state.animations);
}
void CoordinatedGraphicsScene::detach()
{
ASSERT(isMainThread());
m_renderQueue.clear();
m_client = 0;
}
void CoordinatedGraphicsScene::appendUpdate(std::function<void()> function)
{
if (!m_isActive)
return;
ASSERT(isMainThread());
MutexLocker locker(m_renderQueueMutex);
m_renderQueue.append(WTF::move(function));
}
void CoordinatedGraphicsScene::setActive(bool active)
{
if (m_isActive == active)
return;
m_renderQueue.clear();
m_isActive = active;
if (m_isActive) {
RefPtr<CoordinatedGraphicsScene> protector(this);
dispatchOnMainThread([=] {
protector->renderNextFrame();
});
}
}
void CoordinatedGraphicsScene::setBackgroundColor(const Color& color)
{
m_backgroundColor = color;
}
TextureMapperLayer* CoordinatedGraphicsScene::findScrollableContentsLayerAt(const FloatPoint& point)
{
return rootLayer() ? rootLayer()->findScrollableContentsLayerAt(point) : 0;
}
}
#endif // USE(COORDINATED_GRAPHICS)