blob: 61af2c08af680f4122573c2f4fcaa7f7a8e3869e [file] [log] [blame]
/*
Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
Copyright (C) 2012 Company 100, Inc.
Copyright (C) 2017 Sony Interactive Entertainment Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "CoordinatedGraphicsScene.h"
#if USE(COORDINATED_GRAPHICS)
#include <WebCore/CoordinatedBackingStore.h>
#include <WebCore/NicosiaBackingStoreTextureMapperImpl.h>
#include <WebCore/NicosiaBuffer.h>
#include <WebCore/NicosiaCompositionLayerTextureMapperImpl.h>
#include <WebCore/NicosiaContentLayerTextureMapperImpl.h>
#include <WebCore/NicosiaImageBackingTextureMapperImpl.h>
#include <WebCore/NicosiaScene.h>
#include <WebCore/TextureMapper.h>
#include <WebCore/TextureMapperBackingStore.h>
#include <WebCore/TextureMapperGL.h>
#include <WebCore/TextureMapperLayer.h>
#include <wtf/Atomics.h>
namespace WebKit {
using namespace WebCore;
static bool layerShouldHaveBackingStore(TextureMapperLayer* layer)
{
return layer->drawsContent() && layer->contentsAreVisible() && !layer->size().isEmpty();
}
CoordinatedGraphicsScene::CoordinatedGraphicsScene(CoordinatedGraphicsSceneClient* client)
: m_client(client)
{
}
CoordinatedGraphicsScene::~CoordinatedGraphicsScene() = default;
void CoordinatedGraphicsScene::applyStateChanges(const Vector<CoordinatedGraphicsState>& states)
{
if (!m_textureMapper) {
m_textureMapper = TextureMapper::create();
static_cast<TextureMapperGL*>(m_textureMapper.get())->setEnableEdgeDistanceAntialiasing(true);
}
ensureRootLayer();
for (auto& state : states)
commitSceneState(state.nicosia);
}
void CoordinatedGraphicsScene::paintToCurrentGLContext(const TransformationMatrix& matrix, const FloatRect& clipRect, TextureMapper::PaintFlags PaintFlags)
{
updateSceneState();
TextureMapperLayer* currentRootLayer = rootLayer();
if (!currentRootLayer)
return;
currentRootLayer->setTextureMapper(m_textureMapper.get());
bool sceneHasRunningAnimations = currentRootLayer->applyAnimationsRecursively(MonotonicTime::now());
m_textureMapper->beginPainting(PaintFlags);
m_textureMapper->beginClip(TransformationMatrix(), clipRect);
if (currentRootLayer->transform() != matrix)
currentRootLayer->setTransform(matrix);
currentRootLayer->paint();
m_fpsCounter.updateFPSAndDisplay(*m_textureMapper, clipRect.location(), matrix);
m_textureMapper->endClip();
m_textureMapper->endPainting();
if (sceneHasRunningAnimations)
updateViewport();
}
void CoordinatedGraphicsScene::updateViewport()
{
if (m_client)
m_client->updateViewport();
}
void CoordinatedGraphicsScene::onNewBufferAvailable()
{
updateViewport();
}
Nicosia::CompositionLayerTextureMapperImpl& compositionLayerImpl(Nicosia::CompositionLayer& compositionLayer)
{
return downcast<Nicosia::CompositionLayerTextureMapperImpl>(compositionLayer.impl());
}
Nicosia::ContentLayerTextureMapperImpl& contentLayerImpl(Nicosia::ContentLayer& contentLayer)
{
return downcast<Nicosia::ContentLayerTextureMapperImpl>(contentLayer.impl());
}
Nicosia::BackingStoreTextureMapperImpl& backingStoreImpl(Nicosia::BackingStore& backingStore)
{
return downcast<Nicosia::BackingStoreTextureMapperImpl>(backingStore.impl());
}
Nicosia::ImageBackingTextureMapperImpl& imageBackingImpl(Nicosia::ImageBacking& imageBacking)
{
return downcast<Nicosia::ImageBackingTextureMapperImpl>(imageBacking.impl());
}
TextureMapperLayer& texmapLayer(Nicosia::CompositionLayer& compositionLayer)
{
auto& compositionState = compositionLayerImpl(compositionLayer).compositionState();
if (!compositionState.layer) {
compositionState.layer = makeUnique<TextureMapperLayer>();
compositionState.layer->setID(compositionLayer.id());
}
return *compositionState.layer;
}
void updateBackingStore(TextureMapperLayer& layer,
Nicosia::BackingStoreTextureMapperImpl::CompositionState& compositionState,
const Nicosia::BackingStoreTextureMapperImpl::TileUpdate& update)
{
if (!layerShouldHaveBackingStore(&layer)) {
layer.setBackingStore(nullptr);
compositionState.backingStore = nullptr;
return;
}
if (!compositionState.backingStore)
compositionState.backingStore = CoordinatedBackingStore::create();
auto& backingStore = *compositionState.backingStore;
layer.setBackingStore(&backingStore);
backingStore.setSize(layer.size());
for (auto& tile : update.tilesToCreate)
backingStore.createTile(tile.tileID, tile.scale);
for (auto& tile : update.tilesToRemove)
backingStore.removeTile(tile.tileID);
for (auto& tile : update.tilesToUpdate) {
backingStore.updateTile(tile.tileID, tile.updateInfo.updateRect,
tile.tileRect, tile.updateInfo.buffer.copyRef(), { 0, 0 });
}
}
void updateImageBacking(TextureMapperLayer& layer,
Nicosia::ImageBackingTextureMapperImpl::CompositionState& compositionState,
Nicosia::ImageBackingTextureMapperImpl::Update& update)
{
if (!update.isVisible) {
layer.setBackingStore(nullptr);
return;
}
if (!compositionState.backingStore)
compositionState.backingStore = CoordinatedBackingStore::create();
auto& backingStore = *compositionState.backingStore;
layer.setContentsLayer(&backingStore);
if (!update.buffer)
return;
backingStore.createTile(1, 1.0);
WebCore::IntRect rect { { }, update.buffer->size() };
ASSERT(2000 >= std::max(rect.width(), rect.height()));
backingStore.setSize(rect.size());
backingStore.updateTile(1, rect, rect, WTFMove(update.buffer), rect.location());
}
void removeLayer(Nicosia::CompositionLayer& layer)
{
layer.accessCommitted(
[](const Nicosia::CompositionLayer::LayerState& committed)
{
if (committed.backingStore) {
auto& compositionState = backingStoreImpl(*committed.backingStore).compositionState();
compositionState.backingStore = nullptr;
}
if (committed.contentLayer)
contentLayerImpl(*committed.contentLayer).proxy().invalidate();
if (committed.imageBacking) {
auto& compositionState = imageBackingImpl(*committed.imageBacking).compositionState();
compositionState.backingStore = nullptr;
}
});
auto& compositionState = compositionLayerImpl(layer).compositionState();
compositionState.layer = nullptr;
}
void CoordinatedGraphicsScene::commitSceneState(const CoordinatedGraphicsState::NicosiaState& state)
{
if (!m_client)
return;
m_nicosia.scene = state.scene;
}
void CoordinatedGraphicsScene::updateSceneState()
{
if (!m_nicosia.scene)
return;
// Store layer and impl references along with the corresponding update
// for each type of possible layer backing.
struct {
struct BackingStore {
std::reference_wrapper<TextureMapperLayer> layer;
std::reference_wrapper<Nicosia::BackingStoreTextureMapperImpl> backingStore;
Nicosia::BackingStoreTextureMapperImpl::TileUpdate update;
};
Vector<BackingStore> backingStore;
struct ContentLayer {
std::reference_wrapper<TextureMapperLayer> layer;
std::reference_wrapper<TextureMapperPlatformLayerProxy> proxy;
bool needsActivation { false };
};
Vector<ContentLayer> contentLayer;
struct ImageBacking {
std::reference_wrapper<TextureMapperLayer> layer;
std::reference_wrapper<Nicosia::ImageBackingTextureMapperImpl> imageBacking;
Nicosia::ImageBackingTextureMapperImpl::Update update;
};
Vector<ImageBacking> imageBacking;
} layersByBacking;
// Access the scene state and perform state update for each layer.
m_nicosia.scene->accessState(
[this, &layersByBacking](Nicosia::Scene::State& state)
{
// FIXME: try to minimize the amount of work in case the Scene::State object
// didn't change (i.e. no layer flush was done), but don't forget to properly
// gather and update proxy objects for content layers.
// Handle the root layer, adding it to the TextureMapperLayer tree
// on the first update. No such change is expected later.
{
auto& rootLayer = texmapLayer(*state.rootLayer);
if (rootLayer.id() != m_rootLayerID) {
m_rootLayerID = rootLayer.id();
RELEASE_ASSERT(m_rootLayer->children().isEmpty());
m_rootLayer->addChild(&rootLayer);
}
}
// Gather all the to-be-removed layers so that composition-side state
// can be properly purged after the current state's set of layers is adopted.
HashSet<RefPtr<Nicosia::CompositionLayer>> removedLayers;
for (auto& layer : m_nicosia.state.layers) {
if (!state.layers.contains(layer))
removedLayers.add(layer);
}
m_nicosia.state = state;
for (auto& layer : removedLayers)
removeLayer(*layer);
removedLayers = { };
// Iterate the current state's set of layers, updating state values according to
// the incoming state changes. Layer backings are stored so that the updates
// (possibly time-consuming) can be done outside of this scene update.
for (auto& compositionLayer : m_nicosia.state.layers) {
auto& layer = texmapLayer(*compositionLayer);
compositionLayer->commitState(
[this, &layer, &compositionLayer, &layersByBacking]
(const Nicosia::CompositionLayer::LayerState& layerState)
{
if (layerState.delta.positionChanged)
layer.setPosition(layerState.position);
if (layerState.delta.anchorPointChanged)
layer.setAnchorPoint(layerState.anchorPoint);
if (layerState.delta.sizeChanged)
layer.setSize(layerState.size);
if (layerState.delta.boundsOriginChanged)
layer.setBoundsOrigin(layerState.boundsOrigin);
if (layerState.delta.transformChanged)
layer.setTransform(layerState.transform);
if (layerState.delta.childrenTransformChanged)
layer.setChildrenTransform(layerState.childrenTransform);
if (layerState.delta.contentsRectChanged)
layer.setContentsRect(layerState.contentsRect);
if (layerState.delta.contentsTilingChanged) {
layer.setContentsTilePhase(layerState.contentsTilePhase);
layer.setContentsTileSize(layerState.contentsTileSize);
}
if (layerState.delta.opacityChanged)
layer.setOpacity(layerState.opacity);
if (layerState.delta.solidColorChanged)
layer.setSolidColor(layerState.solidColor);
if (layerState.delta.filtersChanged)
layer.setFilters(layerState.filters);
if (layerState.delta.animationsChanged)
layer.setAnimations(layerState.animations);
if (layerState.delta.childrenChanged) {
layer.setChildren(WTF::map(layerState.children,
[](auto& child) { return &texmapLayer(*child); }));
}
if (layerState.delta.maskChanged)
layer.setMaskLayer(layerState.mask ? &texmapLayer(*layerState.mask) : nullptr);
if (layerState.delta.replicaChanged)
layer.setReplicaLayer(layerState.replica ? &texmapLayer(*layerState.replica) : nullptr);
if (layerState.delta.flagsChanged) {
layer.setContentsOpaque(layerState.flags.contentsOpaque);
layer.setDrawsContent(layerState.flags.drawsContent);
layer.setContentsVisible(layerState.flags.contentsVisible);
layer.setBackfaceVisibility(layerState.flags.backfaceVisible);
layer.setMasksToBounds(layerState.flags.masksToBounds);
layer.setPreserves3D(layerState.flags.preserves3D);
}
if (layerState.delta.repaintCounterChanged)
layer.setRepaintCounter(layerState.repaintCounter.visible, layerState.repaintCounter.count);
if (layerState.delta.debugBorderChanged)
layer.setDebugVisuals(layerState.debugBorder.visible, layerState.debugBorder.color, layerState.debugBorder.width);
if (layerState.backingStore) {
auto& impl = backingStoreImpl(*layerState.backingStore);
layersByBacking.backingStore.append(
{ std::ref(layer), std::ref(impl), impl.takeUpdate() });
} else
layer.setBackingStore(nullptr);
if (layerState.contentLayer) {
auto& impl = contentLayerImpl(*layerState.contentLayer);
layersByBacking.contentLayer.append(
{ std::ref(layer), std::ref(impl.proxy()), layerState.delta.contentLayerChanged });
} else if (layerState.imageBacking) {
auto& impl = imageBackingImpl(*layerState.imageBacking);
layersByBacking.imageBacking.append(
{ std::ref(layer), std::ref(impl), impl.takeUpdate() });
} else
layer.setContentsLayer(nullptr);
if (layerState.animatedBackingStoreClient)
layer.setAnimatedBackingStoreClient(layerState.animatedBackingStoreClient.get());
else
layer.setAnimatedBackingStoreClient(nullptr);
});
}
});
// Iterate through each backing type of layers and gather backing store
// or proxy objects that need an update.
// FIXME: HashSet<std::reference_wrapper<>> would be ideal, but doesn't work (yet).
HashSet<Ref<WebCore::CoordinatedBackingStore>> backingStoresWithPendingBuffers;
HashSet<Ref<WebCore::TextureMapperPlatformLayerProxy>> proxiesForSwapping;
{
for (auto& entry : layersByBacking.backingStore) {
auto& compositionState = entry.backingStore.get().compositionState();
updateBackingStore(entry.layer.get(), compositionState, entry.update);
if (compositionState.backingStore)
backingStoresWithPendingBuffers.add(makeRef(*compositionState.backingStore));
}
layersByBacking.backingStore = { };
}
{
for (auto& entry : layersByBacking.contentLayer) {
auto& proxy = entry.proxy.get();
if (entry.needsActivation)
proxy.activateOnCompositingThread(this, &entry.layer.get());
proxiesForSwapping.add(makeRef(proxy));
}
layersByBacking.contentLayer = { };
}
{
for (auto& entry : layersByBacking.imageBacking) {
auto& compositionState = entry.imageBacking.get().compositionState();
updateImageBacking(entry.layer.get(), compositionState, entry.update);
if (compositionState.backingStore)
backingStoresWithPendingBuffers.add(makeRef(*compositionState.backingStore));
}
layersByBacking.imageBacking = { };
}
for (auto& backingStore : backingStoresWithPendingBuffers)
backingStore->commitTileOperations(*m_textureMapper);
for (auto& proxy : proxiesForSwapping)
proxy->swapBuffer();
}
void CoordinatedGraphicsScene::ensureRootLayer()
{
if (m_rootLayer)
return;
m_rootLayer = makeUnique<TextureMapperLayer>();
m_rootLayer->setMasksToBounds(false);
m_rootLayer->setDrawsContent(false);
m_rootLayer->setAnchorPoint(FloatPoint3D(0, 0, 0));
// The root layer should not have zero size, or it would be optimized out.
m_rootLayer->setSize(FloatSize(1.0, 1.0));
ASSERT(m_textureMapper);
m_rootLayer->setTextureMapper(m_textureMapper.get());
}
void CoordinatedGraphicsScene::purgeGLResources()
{
ASSERT(!m_client);
if (m_nicosia.scene) {
m_nicosia.scene->accessState(
[](Nicosia::Scene::State& state)
{
for (auto& layer : state.layers)
removeLayer(*layer);
state.layers = { };
state.rootLayer = nullptr;
});
m_nicosia.scene = nullptr;
}
m_rootLayer = nullptr;
m_rootLayerID = 0;
m_textureMapper = nullptr;
}
void CoordinatedGraphicsScene::detach()
{
ASSERT(RunLoop::isMain());
m_isActive = false;
m_client = nullptr;
}
} // namespace WebKit
#endif // USE(COORDINATED_GRAPHICS)