457 lines
18 KiB
C++
457 lines
18 KiB
C++
/*
|
|
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;
|
|
|
|
CoordinatedGraphicsScene::CoordinatedGraphicsScene(CoordinatedGraphicsSceneClient* client)
|
|
: m_client(client)
|
|
{
|
|
}
|
|
|
|
CoordinatedGraphicsScene::~CoordinatedGraphicsScene() = default;
|
|
|
|
void CoordinatedGraphicsScene::applyStateChanges(const Vector<CoordinatedGraphicsState>& states)
|
|
{
|
|
if (!m_textureMapper) {
|
|
m_textureMapper = TextureMapper::create(TextureMapper::OpenGLMode);
|
|
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;
|
|
|
|
if (currentRootLayer->transform() != matrix)
|
|
currentRootLayer->setTransform(matrix);
|
|
|
|
bool sceneHasRunningAnimations = currentRootLayer->applyAnimationsRecursively(MonotonicTime::now());
|
|
m_textureMapper->beginPainting(PaintFlags);
|
|
m_textureMapper->beginClip(TransformationMatrix(), FloatRoundedRect(clipRect));
|
|
|
|
currentRootLayer->paint(*m_textureMapper);
|
|
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 (!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.setContentsLayer(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(
|
|
[&layer, &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.contentsClippingRectChanged)
|
|
layer.setContentsClippingRect(layerState.contentsClippingRect);
|
|
|
|
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.backdropFiltersChanged)
|
|
layer.setBackdropLayer(layerState.backdropLayer ? &texmapLayer(*layerState.backdropLayer) : nullptr);
|
|
if (layerState.delta.backdropFiltersRectChanged)
|
|
layer.setBackdropFiltersRect(layerState.backdropFiltersRect);
|
|
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));
|
|
}
|
|
|
|
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)
|