/* Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) 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" #if USE(COORDINATED_GRAPHICS) #include "LayerTreeRenderer.h" #include "CoordinatedBackingStore.h" #include "GraphicsLayerTextureMapper.h" #include "LayerTreeCoordinatorProxy.h" #include "MessageID.h" #include "ShareableBitmap.h" #include "TextureMapper.h" #include "TextureMapperBackingStore.h" #include "TextureMapperGL.h" #include "TextureMapperLayer.h" #include "UpdateInfo.h" #include #include #include namespace WebKit { using namespace WebCore; template class MainThreadGuardedInvoker { public: static void call(PassRefPtr objectToGuard, const Function& function) { MainThreadGuardedInvoker* invoker = new MainThreadGuardedInvoker(objectToGuard, function); callOnMainThread(invoke, invoker); } private: MainThreadGuardedInvoker(PassRefPtr object, const Function& newFunction) : objectToGuard(object) , function(newFunction) { } RefPtr objectToGuard; Function function; static void invoke(void* data) { MainThreadGuardedInvoker* invoker = static_cast*>(data); invoker->function(); delete invoker; } }; void LayerTreeRenderer::dispatchOnMainThread(const Function& function) { if (isMainThread()) function(); else MainThreadGuardedInvoker::call(this, function); } static FloatPoint boundedScrollPosition(const FloatPoint& scrollPosition, const FloatRect& visibleContentRect, const FloatSize& contentSize) { float scrollPositionX = std::max(scrollPosition.x(), 0.0f); scrollPositionX = std::min(scrollPositionX, contentSize.width() - visibleContentRect.width()); float scrollPositionY = std::max(scrollPosition.y(), 0.0f); scrollPositionY = std::min(scrollPositionY, contentSize.height() - visibleContentRect.height()); return FloatPoint(scrollPositionX, scrollPositionY); } LayerTreeRenderer::LayerTreeRenderer(LayerTreeCoordinatorProxy* layerTreeCoordinatorProxy) : m_layerTreeCoordinatorProxy(layerTreeCoordinatorProxy) , m_rootLayerID(InvalidWebLayerID) , m_isActive(false) , m_animationsLocked(false) #if ENABLE(REQUEST_ANIMATION_FRAME) , m_animationFrameRequested(false) #endif { } LayerTreeRenderer::~LayerTreeRenderer() { } PassOwnPtr LayerTreeRenderer::createLayer(WebLayerID) { GraphicsLayer* newLayer = new GraphicsLayerTextureMapper(this); TextureMapperLayer* layer = toTextureMapperLayer(newLayer); layer->setShouldUpdateBackingStoreFromLayer(false); return adoptPtr(newLayer); } void LayerTreeRenderer::paintToCurrentGLContext(const TransformationMatrix& matrix, float opacity, const FloatRect& clipRect, TextureMapper::PaintFlags PaintFlags) { if (!m_textureMapper) m_textureMapper = TextureMapper::create(TextureMapper::OpenGLMode); ASSERT(m_textureMapper->accelerationMode() == TextureMapper::OpenGLMode); adjustPositionForFixedLayers(); GraphicsLayer* currentRootLayer = rootLayer(); if (!currentRootLayer) return; TextureMapperLayer* layer = toTextureMapperLayer(currentRootLayer); if (!layer) return; layer->setTextureMapper(m_textureMapper.get()); if (!m_animationsLocked) layer->applyAnimationsRecursively(); m_textureMapper->beginPainting(PaintFlags); m_textureMapper->beginClip(TransformationMatrix(), clipRect); if (currentRootLayer->opacity() != opacity || currentRootLayer->transform() != matrix) { currentRootLayer->setOpacity(opacity); currentRootLayer->setTransform(matrix); currentRootLayer->flushCompositingStateForThisLayerOnly(); } layer->paint(); m_textureMapper->endClip(); m_textureMapper->endPainting(); if (layer->descendantsOrSelfHaveRunningAnimations()) dispatchOnMainThread(bind(&LayerTreeRenderer::updateViewport, this)); #if ENABLE(REQUEST_ANIMATION_FRAME) if (m_animationFrameRequested) { m_animationFrameRequested = false; dispatchOnMainThread(bind(&LayerTreeRenderer::animationFrameReady, this)); } #endif } #if ENABLE(REQUEST_ANIMATION_FRAME) void LayerTreeRenderer::animationFrameReady() { if (m_layerTreeCoordinatorProxy) m_layerTreeCoordinatorProxy->animationFrameReady(); } void LayerTreeRenderer::requestAnimationFrame() { m_animationFrameRequested = true; } #endif void LayerTreeRenderer::paintToGraphicsContext(BackingStore::PlatformGraphicsContext painter) { if (!m_textureMapper) m_textureMapper = TextureMapper::create(); ASSERT(m_textureMapper->accelerationMode() == TextureMapper::SoftwareMode); syncRemoteContent(); TextureMapperLayer* layer = toTextureMapperLayer(rootLayer()); if (!layer) return; GraphicsContext graphicsContext(painter); m_textureMapper->setGraphicsContext(&graphicsContext); m_textureMapper->beginPainting(); layer->paint(); m_textureMapper->endPainting(); m_textureMapper->setGraphicsContext(0); } void LayerTreeRenderer::setContentsSize(const WebCore::FloatSize& contentsSize) { m_contentsSize = contentsSize; } void LayerTreeRenderer::setVisibleContentsRect(const FloatRect& rect) { m_visibleContentsRect = rect; } void LayerTreeRenderer::updateViewport() { if (m_layerTreeCoordinatorProxy) m_layerTreeCoordinatorProxy->updateViewport(); } void LayerTreeRenderer::adjustPositionForFixedLayers() { if (m_fixedLayers.isEmpty()) return; // Fixed layer positions are updated by the web process when we update the visible contents rect / scroll position. // If we want those layers to follow accurately the viewport when we move between the web process updates, we have to offset // them by the delta between the current position and the position of the viewport used for the last layout. FloatPoint scrollPosition = boundedScrollPosition(m_visibleContentsRect.location(), m_visibleContentsRect, m_contentsSize); FloatPoint renderedScrollPosition = boundedScrollPosition(m_renderedContentsScrollPosition, m_visibleContentsRect, m_contentsSize); FloatSize delta = scrollPosition - renderedScrollPosition; LayerMap::iterator end = m_fixedLayers.end(); for (LayerMap::iterator it = m_fixedLayers.begin(); it != end; ++it) toTextureMapperLayer(it->value)->setScrollPositionDeltaIfNeeded(delta); } void LayerTreeRenderer::didChangeScrollPosition(const IntPoint& position) { m_pendingRenderedContentsScrollPosition = position; } #if USE(GRAPHICS_SURFACE) void LayerTreeRenderer::syncCanvas(WebLayerID id, const WebCore::IntSize& canvasSize, const GraphicsSurfaceToken& token, uint32_t frontBuffer) { if (canvasSize.isEmpty() || !m_textureMapper) return; ensureLayer(id); GraphicsLayer* layer = layerByID(id); RefPtr canvasBackingStore; SurfaceBackingStoreMap::iterator it = m_surfaceBackingStores.find(id); if (it == m_surfaceBackingStores.end()) { canvasBackingStore = TextureMapperSurfaceBackingStore::create(); m_surfaceBackingStores.set(id, canvasBackingStore); } else canvasBackingStore = it->value; canvasBackingStore->setGraphicsSurface(token, canvasSize, frontBuffer); layer->setContentsToMedia(canvasBackingStore.get()); } #endif void LayerTreeRenderer::setLayerChildren(WebLayerID id, const Vector& childIDs) { ensureLayer(id); LayerMap::iterator it = m_layers.find(id); GraphicsLayer* layer = it->value; Vector children; for (size_t i = 0; i < childIDs.size(); ++i) { WebLayerID childID = childIDs[i]; GraphicsLayer* child = layerByID(childID); if (!child) { child = createLayer(childID).leakPtr(); m_layers.add(childID, child); } children.append(child); } layer->setChildren(children); } #if ENABLE(CSS_FILTERS) void LayerTreeRenderer::setLayerFilters(WebLayerID id, const FilterOperations& filters) { ensureLayer(id); LayerMap::iterator it = m_layers.find(id); ASSERT(it != m_layers.end()); GraphicsLayer* layer = it->value; layer->setFilters(filters); } #endif void LayerTreeRenderer::setLayerState(WebLayerID id, const WebLayerInfo& layerInfo) { ensureLayer(id); LayerMap::iterator it = m_layers.find(id); ASSERT(it != m_layers.end()); GraphicsLayer* layer = it->value; layer->setReplicatedByLayer(layerByID(layerInfo.replica)); layer->setMaskLayer(layerByID(layerInfo.mask)); layer->setPosition(layerInfo.pos); layer->setSize(layerInfo.size); layer->setTransform(layerInfo.transform); layer->setAnchorPoint(layerInfo.anchorPoint); layer->setChildrenTransform(layerInfo.childrenTransform); layer->setBackfaceVisibility(layerInfo.backfaceVisible); layer->setContentsOpaque(layerInfo.contentsOpaque); layer->setContentsRect(layerInfo.contentsRect); layer->setDrawsContent(layerInfo.drawsContent); layer->setContentsVisible(layerInfo.contentsVisible); toGraphicsLayerTextureMapper(layer)->setFixedToViewport(layerInfo.fixedToViewport); if (layerInfo.fixedToViewport) m_fixedLayers.add(id, layer); else m_fixedLayers.remove(id); assignImageToLayer(layer, layerInfo.imageBackingStoreID); // Never make the root layer clip. layer->setMasksToBounds(layerInfo.isRootLayer ? false : layerInfo.masksToBounds); layer->setOpacity(layerInfo.opacity); layer->setPreserves3D(layerInfo.preserves3D); if (layerInfo.isRootLayer && m_rootLayerID != id) setRootLayerID(id); } void LayerTreeRenderer::deleteLayer(WebLayerID layerID) { GraphicsLayer* layer = layerByID(layerID); if (!layer) return; layer->removeFromParent(); m_layers.remove(layerID); m_fixedLayers.remove(layerID); #if USE(GRAPHICS_SURFACE) m_surfaceBackingStores.remove(layerID); #endif delete layer; } void LayerTreeRenderer::ensureLayer(WebLayerID id) { // We have to leak the new layer's pointer and manage it ourselves, // because OwnPtr is not copyable. if (m_layers.find(id) == m_layers.end()) m_layers.add(id, createLayer(id).leakPtr()); } void LayerTreeRenderer::setRootLayerID(WebLayerID layerID) { if (layerID == m_rootLayerID) return; m_rootLayerID = layerID; m_rootLayer->removeAllChildren(); if (!layerID) return; GraphicsLayer* layer = layerByID(layerID); if (!layer) return; m_rootLayer->addChild(layer); } PassRefPtr LayerTreeRenderer::getBackingStore(WebLayerID id) { TextureMapperLayer* layer = toTextureMapperLayer(layerByID(id)); ASSERT(layer); RefPtr backingStore = static_cast(layer->backingStore().get()); if (!backingStore) { backingStore = CoordinatedBackingStore::create(); layer->setBackingStore(backingStore.get()); } ASSERT(backingStore); return backingStore; } void LayerTreeRenderer::createTile(WebLayerID layerID, int tileID, float scale) { getBackingStore(layerID)->createTile(tileID, scale); } void LayerTreeRenderer::removeTile(WebLayerID layerID, int tileID) { getBackingStore(layerID)->removeTile(tileID); } void LayerTreeRenderer::updateTile(WebLayerID layerID, int tileID, const TileUpdate& update) { RefPtr backingStore = getBackingStore(layerID); backingStore->updateTile(tileID, update.sourceRect, update.targetRect, update.surface, update.offset); m_backingStoresWithPendingBuffers.add(backingStore); } void LayerTreeRenderer::createImage(int64_t imageID, PassRefPtr weakBitmap) { RefPtr bitmap = weakBitmap; RefPtr backingStore = TextureMapperTiledBackingStore::create(); m_directlyCompositedImages.set(imageID, backingStore); backingStore->updateContents(m_textureMapper.get(), bitmap->createImage().get(), BitmapTexture::UpdateCannotModifyOriginalImageData); } void LayerTreeRenderer::destroyImage(int64_t imageID) { m_directlyCompositedImages.remove(imageID); } void LayerTreeRenderer::assignImageToLayer(GraphicsLayer* layer, int64_t imageID) { if (!imageID) { layer->setContentsToMedia(0); return; } HashMap >::iterator it = m_directlyCompositedImages.find(imageID); ASSERT(it != m_directlyCompositedImages.end()); layer->setContentsToMedia(it->value.get()); } void LayerTreeRenderer::commitTileOperations() { HashSet >::iterator end = m_backingStoresWithPendingBuffers.end(); for (HashSet >::iterator it = m_backingStoresWithPendingBuffers.begin(); it != end; ++it) (*it)->commitTileOperations(m_textureMapper.get()); m_backingStoresWithPendingBuffers.clear(); } void LayerTreeRenderer::flushLayerChanges() { m_renderedContentsScrollPosition = m_pendingRenderedContentsScrollPosition; // Since the frame has now been rendered, we can safely unlock the animations until the next layout. setAnimationsLocked(false); m_rootLayer->flushCompositingState(FloatRect()); commitTileOperations(); // The pending tiles state is on its way for the screen, tell the web process to render the next one. dispatchOnMainThread(bind(&LayerTreeRenderer::renderNextFrame, this)); } void LayerTreeRenderer::renderNextFrame() { if (m_layerTreeCoordinatorProxy) m_layerTreeCoordinatorProxy->renderNextFrame(); } void LayerTreeRenderer::ensureRootLayer() { if (m_rootLayer) return; if (!m_textureMapper) { m_textureMapper = TextureMapper::create(TextureMapper::OpenGLMode); static_cast(m_textureMapper.get())->setEnableEdgeDistanceAntialiasing(true); } m_rootLayer = createLayer(InvalidWebLayerID); 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)); toTextureMapperLayer(m_rootLayer.get())->setTextureMapper(m_textureMapper.get()); } void LayerTreeRenderer::syncRemoteContent() { // We enqueue messages and execute them during paint, as they require an active GL context. ensureRootLayer(); Vector > renderQueue; bool calledOnMainThread = WTF::isMainThread(); if (!calledOnMainThread) m_renderQueueMutex.lock(); renderQueue.swap(m_renderQueue); if (!calledOnMainThread) m_renderQueueMutex.unlock(); for (size_t i = 0; i < renderQueue.size(); ++i) renderQueue[i](); m_renderQueue.clear(); } void LayerTreeRenderer::purgeGLResources() { TextureMapperLayer* layer = toTextureMapperLayer(rootLayer()); if (layer) layer->clearBackingStoresRecursive(); m_directlyCompositedImages.clear(); #if USE(GRAPHICS_SURFACE) m_surfaceBackingStores.clear(); #endif m_rootLayer->removeAllChildren(); m_rootLayer.clear(); m_rootLayerID = InvalidWebLayerID; m_layers.clear(); m_fixedLayers.clear(); m_textureMapper.clear(); m_backingStoresWithPendingBuffers.clear(); setActive(false); dispatchOnMainThread(bind(&LayerTreeRenderer::purgeBackingStores, this)); } void LayerTreeRenderer::setLayerAnimations(WebLayerID id, const GraphicsLayerAnimations& animations) { GraphicsLayerTextureMapper* layer = toGraphicsLayerTextureMapper(layerByID(id)); if (!layer) return; layer->setAnimations(animations); } void LayerTreeRenderer::setAnimationsLocked(bool locked) { m_animationsLocked = locked; } void LayerTreeRenderer::purgeBackingStores() { if (m_layerTreeCoordinatorProxy) m_layerTreeCoordinatorProxy->purgeBackingStores(); } void LayerTreeRenderer::detach() { m_layerTreeCoordinatorProxy = 0; } void LayerTreeRenderer::appendUpdate(const Function& function) { if (!m_isActive) return; ASSERT(isMainThread()); MutexLocker locker(m_renderQueueMutex); m_renderQueue.append(function); } void LayerTreeRenderer::setActive(bool active) { if (m_isActive == active) return; // Have to clear render queue in both cases. // If there are some updates in queue during activation then those updates are from previous instance of paint node // and cannot be applied to the newly created instance. m_renderQueue.clear(); m_isActive = active; if (m_isActive) renderNextFrame(); } } // namespace WebKit #endif // USE(COORDINATED_GRAPHICS)