From 8fea66e503c97640d6bdb5b8e5ddcb27324b5866 Mon Sep 17 00:00:00 2001 From: Nicolas Roard Date: Wed, 31 Aug 2011 16:02:00 -0700 Subject: Add shared images for layers bug:5242595 bug:5218173 Change-Id: I37d395e85441671312aac3e236cc8276019aa990 --- Source/WebCore/Android.mk | 1 + .../graphics/android/GraphicsLayerAndroid.cpp | 16 +-- .../platform/graphics/android/ImageTexture.cpp | 128 +++++++++++++++++++++ .../platform/graphics/android/ImageTexture.h | 84 ++++++++++++++ .../platform/graphics/android/LayerAndroid.cpp | 71 ++++++------ .../platform/graphics/android/LayerAndroid.h | 11 +- .../platform/graphics/android/TilesManager.cpp | 46 ++++++++ .../platform/graphics/android/TilesManager.h | 9 ++ 8 files changed, 323 insertions(+), 43 deletions(-) create mode 100644 Source/WebCore/platform/graphics/android/ImageTexture.cpp create mode 100644 Source/WebCore/platform/graphics/android/ImageTexture.h diff --git a/Source/WebCore/Android.mk b/Source/WebCore/Android.mk index 335915f..1250d91 100644 --- a/Source/WebCore/Android.mk +++ b/Source/WebCore/Android.mk @@ -655,6 +655,7 @@ LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \ platform/graphics/android/ImageAndroid.cpp \ platform/graphics/android/ImageBufferAndroid.cpp \ platform/graphics/android/ImageSourceAndroid.cpp \ + platform/graphics/android/ImageTexture.cpp \ platform/graphics/android/Layer.cpp \ platform/graphics/android/LayerAndroid.cpp \ platform/graphics/android/MediaLayer.cpp \ diff --git a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp index 40d25e4..7722e5a 100644 --- a/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/GraphicsLayerAndroid.cpp @@ -630,6 +630,8 @@ bool GraphicsLayerAndroid::repaint() if (m_needsRepaint && m_haveImage && m_newImage) { // We need to tell the GL thread that we will need to repaint the // texture. Only do so if we effectively have a new image! + m_contentLayer->markAsDirty(m_dirtyRegion); + m_dirtyRegion.setEmpty(); m_contentLayer->needsRepaint(); m_newImage = false; m_needsRepaint = false; @@ -656,13 +658,7 @@ bool GraphicsLayerAndroid::paintContext(SkPicture* context, void GraphicsLayerAndroid::setNeedsDisplayInRect(const FloatRect& rect) { - for (unsigned int i = 0; i < m_children.size(); i++) { - GraphicsLayer* layer = m_children[i]; - if (layer) { - FloatRect childrenRect = m_transform.mapRect(rect); - layer->setNeedsDisplayInRect(childrenRect); - } - } + // rect is in the render object coordinates if (!m_haveImage && !drawsContent()) { LOG("(%x) setNeedsDisplay(%.2f,%.2f,%.2f,%.2f) doesn't have content, bypass...", @@ -848,6 +844,12 @@ void GraphicsLayerAndroid::setContentsToImage(Image* image) askForSync(); } } + if (m_haveImage && !image) { + m_contentLayer->setContentsImage(0); + m_imageRef = 0; + setNeedsDisplay(); + askForSync(); + } } void GraphicsLayerAndroid::setContentsToMedia(PlatformLayer* mediaLayer) diff --git a/Source/WebCore/platform/graphics/android/ImageTexture.cpp b/Source/WebCore/platform/graphics/android/ImageTexture.cpp new file mode 100644 index 0000000..7e71740 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/ImageTexture.cpp @@ -0,0 +1,128 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ImageTexture.h" + +#include "SkDevice.h" +#include "TilesManager.h" + +#ifdef DEBUG + +#include +#include +#include + +#undef XLOG +#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "ImageTexture", __VA_ARGS__) + +#else + +#undef XLOG +#define XLOG(...) + +#endif // DEBUG + +namespace WebCore { + +ImageTexture::ImageTexture(SkBitmapRef* img) + : m_imageRef(img) + , m_image(0) + , m_textureId(0) + , m_refCount(0) +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->increment("ImageTexture"); +#endif + if (!m_imageRef) + return; + + SkBitmap* bitmap = &m_imageRef->bitmap(); + m_image = new SkBitmap(); + int w = bitmap->width(); + int h = bitmap->height(); + m_image->setConfig(SkBitmap::kARGB_8888_Config, w, h); + m_image->allocPixels(); + SkDevice* device = new SkDevice(NULL, *m_image, false); + SkCanvas canvas; + canvas.setDevice(device); + device->unref(); + SkRect dest; + dest.set(0, 0, w, h); + m_image->setIsOpaque(false); + m_image->eraseARGB(0, 0, 0, 0); + canvas.drawBitmapRect(*bitmap, 0, dest); +} + +ImageTexture::~ImageTexture() +{ +#ifdef DEBUG_COUNT + ClassTracker::instance()->decrement("ImageTexture"); +#endif + delete m_image; +} + +void ImageTexture::prepare() +{ + if (m_textureId) + return; + + glGenTextures(1, &m_textureId); + GLUtils::createTextureWithBitmap(m_textureId, *m_image); +} + +void ImageTexture::draw(LayerAndroid* layer) +{ + if (!layer) + return; + if (!m_textureId) + return; + if (!m_image) + return; + + SkRect rect; + rect.fLeft = 0; + rect.fTop = 0; + rect.fRight = layer->getSize().width(); + rect.fBottom = layer->getSize().height(); + TilesManager::instance()->shader()->drawLayerQuad(*layer->drawTransform(), + rect, m_textureId, + layer->drawOpacity(), true); +} + +void ImageTexture::release() +{ + if (m_refCount >= 1) + m_refCount--; + if (!m_refCount) + deleteTexture(); +} + +void ImageTexture::deleteTexture() +{ + glDeleteTextures(1, &m_textureId); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/ImageTexture.h b/Source/WebCore/platform/graphics/android/ImageTexture.h new file mode 100644 index 0000000..7e51d2b --- /dev/null +++ b/Source/WebCore/platform/graphics/android/ImageTexture.h @@ -0,0 +1,84 @@ +/* + * Copyright 2011, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageTexture_h +#define ImageTexture_h + +#include "ClassTracker.h" +#include "GLUtils.h" +#include "SkBitmap.h" +#include "SkBitmapRef.h" +#include "SkRefCnt.h" +#include "LayerAndroid.h" + +namespace WebCore { + +class LayerAndroid; + +///////////////////////////////////////////////////////////////////////////////// +// Image sharing codepath for layers +///////////////////////////////////////////////////////////////////////////////// +// +// We receive an SkBitmapRef on the webcore thread; from this we create +// an ImageTexture instance and keep it in TilesManager in a hashmap +// (see TilesManager::addImage()) +// +// The ImageTexture will recopy the pointed SkBitmap locally (so we can safely +// use it on the texture generation thread), and just use the SkBitmapRef as a +// key. +// +// Layers on the shared image path will ask TilesManager for the corresponding +// ImageTexture, instead of using a PaintedSurface+TiledTexture. +// When the ImageTexture is prepared for the first time, we directly upload +// the bitmap to a texture. +// +// TODO: limit how many ImageTextures can be uploaded in one draw cycle +// TODO: limit the size of ImageTextures (use a TiledTexture when needed) +// +///////////////////////////////////////////////////////////////////////////////// +class ImageTexture { +public: + ImageTexture(SkBitmapRef* img); + virtual ~ImageTexture(); + + void prepare(); + void draw(LayerAndroid* painter); + void deleteTexture(); + void retain() { m_refCount++; } + void release(); + unsigned int refCount() { return m_refCount; } + SkBitmapRef* imageRef() { return m_imageRef; } + +private: + + SkBitmapRef* m_imageRef; + SkBitmap* m_image; + GLuint m_textureId; + unsigned int m_refCount; +}; + +} // namespace WebCore + +#endif // ImageTexture diff --git a/Source/WebCore/platform/graphics/android/LayerAndroid.cpp b/Source/WebCore/platform/graphics/android/LayerAndroid.cpp index a39614f..81f0ce7 100644 --- a/Source/WebCore/platform/graphics/android/LayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/LayerAndroid.cpp @@ -64,10 +64,11 @@ LayerAndroid::LayerAndroid(RenderLayer* owner) : Layer(), m_preserves3D(false), m_anchorPointZ(0), m_recordingPicture(0), - m_contentsImage(0), m_extra(0), m_uniqueId(++gUniqueId), m_texture(0), + m_imageRef(0), + m_imageTexture(0), m_pictureUsed(0), m_requestSent(false), m_scale(1), @@ -88,15 +89,17 @@ LayerAndroid::LayerAndroid(RenderLayer* owner) : Layer(), LayerAndroid::LayerAndroid(const LayerAndroid& layer) : Layer(layer), m_haveClip(layer.m_haveClip), m_isIframe(layer.m_isIframe), - m_contentsImage(0), m_extra(0), // deliberately not copied m_uniqueId(layer.m_uniqueId), m_texture(0), + m_imageTexture(0), m_requestSent(false), m_owningLayer(layer.m_owningLayer) { m_isFixed = layer.m_isFixed; - copyBitmap(layer.m_contentsImage); + m_imageRef = layer.m_imageRef; + if (m_imageRef) + TilesManager::instance()->addImage(m_imageRef); m_renderLayerPos = layer.m_renderLayerPos; m_transform = layer.m_transform; m_backfaceVisibility = layer.m_backfaceVisibility; @@ -145,10 +148,11 @@ LayerAndroid::LayerAndroid(SkPicture* picture) : Layer(), m_isFixed(false), m_isIframe(false), m_recordingPicture(picture), - m_contentsImage(0), m_extra(0), m_uniqueId(-1), m_texture(0), + m_imageRef(0), + m_imageTexture(0), m_requestSent(false), m_scale(1), m_lastComputeTextureSize(0), @@ -169,9 +173,10 @@ LayerAndroid::~LayerAndroid() if (m_texture) m_texture->removeLayer(this); SkSafeUnref(m_texture); + if (m_imageTexture) + TilesManager::instance()->removeImage(m_imageTexture->imageRef()); removeChildren(); delete m_extra; - delete m_contentsImage; SkSafeUnref(m_recordingPicture); m_animations.clear(); #ifdef DEBUG_COUNT @@ -646,28 +651,18 @@ void LayerAndroid::updateGLPositionsAndScale(const TransformationMatrix& parentM this->getChild(i)->updateGLPositionsAndScale(localMatrix, drawClip(), opacity, scale); } -void LayerAndroid::copyBitmap(SkBitmap* bitmap) +void LayerAndroid::setContentsImage(SkBitmapRef* img) { - if (!bitmap) + m_imageRef = img; + if (!img) return; - delete m_contentsImage; - m_contentsImage = new SkBitmap(); - SkBitmap::Config config = bitmap->config(); - int w = bitmap->width(); - int h = bitmap->height(); - m_contentsImage->setConfig(config, w, h); - bitmap->copyTo(m_contentsImage, config); -} - -void LayerAndroid::setContentsImage(SkBitmapRef* img) -{ - copyBitmap(&img->bitmap()); + TilesManager::instance()->addImage(img); } bool LayerAndroid::needsTexture() { - return m_contentsImage || (prepareContext() + return m_imageRef || (prepareContext() && m_recordingPicture->width() && m_recordingPicture->height()); } @@ -727,7 +722,7 @@ void LayerAndroid::showLayer(int indent) } // We go through our tree, and if we have layer in the new -// tree that is similar, we tranfer our texture to it. +// tree that is similar, we transfer our texture to it. // Otherwise, we remove ourselves from the texture so // that TilesManager::swapLayersTextures() have a chance // at deallocating the textures (PaintedSurfaces) @@ -762,12 +757,23 @@ void LayerAndroid::createTexture() if (!needsTexture()) return; - if (!m_texture) - m_texture = new PaintedSurface(this); + if (m_imageRef) { + if (!m_imageTexture) { + m_imageTexture = TilesManager::instance()->getTextureForImage(m_imageRef); + m_dirtyRegion.setEmpty(); + } + if (m_texture) { + m_texture->removeLayer(this); + m_texture = 0; + } + } else { + if (!m_texture) + m_texture = new PaintedSurface(this); - // pass the invalidated regions to the PaintedSurface - m_texture->markAsDirty(m_dirtyRegion); - m_dirtyRegion.setEmpty(); + // pass the invalidated regions to the PaintedSurface + m_texture->markAsDirty(m_dirtyRegion); + m_dirtyRegion.setEmpty(); + } } static inline bool compareLayerZ(const LayerAndroid* a, const LayerAndroid* b) @@ -811,6 +817,9 @@ void LayerAndroid::prepare(GLWebViewState* glWebViewState) if (m_texture) m_texture->prepare(glWebViewState); + + if (m_imageTexture) + m_imageTexture->prepare(); } bool LayerAndroid::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix) @@ -824,6 +833,9 @@ bool LayerAndroid::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix) if (m_texture) askPaint |= m_texture->draw(); + if (m_imageTexture) + m_imageTexture->draw(this); + // When the layer is dirty, the UI thread should be notified to redraw. askPaint |= drawChildrenGL(glWebViewState, matrix); m_atomicSync.lock(); @@ -865,13 +877,8 @@ void LayerAndroid::extraDraw(SkCanvas* canvas) void LayerAndroid::contentDraw(SkCanvas* canvas) { - if (m_contentsImage) { - SkRect dest; - dest.set(0, 0, getSize().width(), getSize().height()); - canvas->drawBitmapRect(*m_contentsImage, 0, dest); - } else { + if (m_recordingPicture) canvas->drawPicture(*m_recordingPicture); - } if (TilesManager::instance()->getShowVisualIndicator()) { float w = getSize().width(); diff --git a/Source/WebCore/platform/graphics/android/LayerAndroid.h b/Source/WebCore/platform/graphics/android/LayerAndroid.h index 8078762..2150a78 100644 --- a/Source/WebCore/platform/graphics/android/LayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/LayerAndroid.h @@ -23,6 +23,7 @@ #include "FloatPoint3D.h" #include "FloatRect.h" #include "GraphicsLayerClient.h" +#include "ImageTexture.h" #include "Layer.h" #include "RefPtr.h" #include "SkBitmap.h" @@ -49,6 +50,7 @@ class SkPicture; namespace WebCore { class LayerAndroid; +class ImageTexture; } namespace android { @@ -234,12 +236,11 @@ public: /** This sets a content image -- calling it means we will use the image directly when drawing the layer instead of using - the content painted by WebKit. See comments below for - m_recordingPicture and m_contentsImage. + the content painted by WebKit. + Images are handled in TilesManager, as they can be shared + between layers. */ void setContentsImage(SkBitmapRef* img); - bool hasContentsImage() { return m_contentsImage; } - void copyBitmap(SkBitmap*); void bounds(SkRect*) const; @@ -348,6 +349,8 @@ private: int m_uniqueId; PaintedSurface* m_texture; + SkBitmapRef* m_imageRef; + ImageTexture* m_imageTexture; // used to signal that the tile is out-of-date and needs to be redrawn bool m_dirty; diff --git a/Source/WebCore/platform/graphics/android/TilesManager.cpp b/Source/WebCore/platform/graphics/android/TilesManager.cpp index e1d7665..c16f945 100644 --- a/Source/WebCore/platform/graphics/android/TilesManager.cpp +++ b/Source/WebCore/platform/graphics/android/TilesManager.cpp @@ -400,6 +400,52 @@ void TilesManager::unregisterGLWebViewState(GLWebViewState* state) transferQueue()->discardQueue(); } +void TilesManager::addImage(SkBitmapRef* imgRef) +{ + if (!imgRef) + return; + + android::Mutex::Autolock lock(m_imagesLock); + if (!m_images.contains(imgRef)) + m_images.set(imgRef, new ImageTexture(imgRef)); +} + +void TilesManager::removeImage(SkBitmapRef* imgRef) +{ + android::Mutex::Autolock lock(m_imagesLock); + if (!m_images.contains(imgRef)) + return; + + ImageTexture* image = m_images.get(imgRef); + image->release(); + + if (!image->refCount()) { + m_images.remove(imgRef); + delete image; + } +} + +void TilesManager::showImages() +{ + XLOGC("We have %d images", m_images.size()); + HashMap::iterator end = m_images.end(); + int i = 0; + for (HashMap::iterator it = m_images.begin(); it != end; ++it) { + XLOGC("Image %x (%d/%d) has %d references", it->first, i, + m_images.size(), it->second->refCount()); + i++; + } +} + +ImageTexture* TilesManager::getTextureForImage(SkBitmapRef* img) +{ + android::Mutex::Autolock lock(m_imagesLock); + ImageTexture* image = m_images.get(img); + if (image) + image->retain(); + return image; +} + TilesManager* TilesManager::instance() { if (!gInstance) { diff --git a/Source/WebCore/platform/graphics/android/TilesManager.h b/Source/WebCore/platform/graphics/android/TilesManager.h index 3298c94..775af48 100644 --- a/Source/WebCore/platform/graphics/android/TilesManager.h +++ b/Source/WebCore/platform/graphics/android/TilesManager.h @@ -30,8 +30,10 @@ #include "BaseTile.h" #include "BaseTileTexture.h" +#include "ImageTexture.h" #include "LayerAndroid.h" #include "ShaderProgram.h" +#include "SkBitmapRef.h" #include "TexturesGenerator.h" #include "TiledPage.h" #include "TilesProfiler.h" @@ -176,6 +178,10 @@ public: { return m_drawGLCount; } + void addImage(SkBitmapRef* img); + void removeImage(SkBitmapRef* img); + ImageTexture* getTextureForImage(SkBitmapRef* img); + void showImages(); private: TilesManager(); @@ -209,6 +215,7 @@ private: android::Mutex m_texturesLock; android::Mutex m_generatorLock; + android::Mutex m_imagesLock; android::Condition m_generatorReadyCond; static TilesManager* gInstance; @@ -221,6 +228,8 @@ private: TilesProfiler m_profiler; TilesTracker m_tilesTracker; unsigned long long m_drawGLCount; + + HashMap m_images; }; } // namespace WebCore -- cgit v1.1